Реализация IServiceProvider в приложении MFC - PullRequest
0 голосов
/ 31 мая 2018

Мне нужно реализовать интерфейс IServiceProvider в приложении с открытым исходным кодом MFC;в частности, мое TTSApp приложение.

Я пытаюсь добавить поддержку интерфейса IAccessibleApplication , который используется программами чтения с экрана для получения информации об имени приложения и информации о версии.

Похоже, что Google Chrome реализует интерфейс IServiceProvider через класс AXPlatformNodeWin, который является производным от класса CComObjectRootEx и других классов и интерфейса.Проблема в том, что приложение MFC не использует класс CComObjectRootEx;он используется ATL.

Я обнаружил IServiceProviderImpl Class .К сожалению, я не могу найти никакой информации о том, как это вписывается в контекст приложения.Какой класс в моей иерархии классов должен быть производным от класса IServiceProviderImpl;мой производный класс CWinApp, мой производный класс CDialogEx или какой-то другой класс?

1 Ответ

0 голосов
/ 12 июня 2018

Я многому научился, находясь в поисках ответа на этот вопрос.Во время квеста я упал в кроличью нору ( Приключения Алисы в стране чудес Чарльза Лютвиджа Доджсона, также известного как Льюис Кэрролл), только чтобы найти Ктулху ( Зов Ктулху от Лавкрафта), ожидающегоme.

Мои первоначальные исследования привели меня к следующим макросам, определенным в afxwin.h.

  • DECLARE_INTERFACE_MAP
  • BEGIN_INTERFACE_MAP
  • END_INTERFACE_MAP
  • BEGIN_INTERFACE_PART
  • END_INTERFACE_PART

Лучшая документация, которую я мог найти для этих макросов, содержится в TN038: Реализация MFC / OLE IUnknown техническое примечание.Хорошим примером, демонстрирующим использование этих макросов и реализацию функции QueryService, является пример TstCon .

Конечно, это привело к другому вопросу, какое окно мне нужно для этогоза?Чтобы ответить на этот вопрос, я посмотрел исходный код определенной программы чтения с экрана, чтобы увидеть, как он использует интерфейс IAccessibleApplication.

Следующая функция, хотя и не фактический используемый код, демонстрирует метод (я не могу поделиться фактическимкод, поскольку программа чтения с экрана не является открытым исходным кодом).

std::wstring GetApplicationNameUsingTheIAccessibleApplicationInterface(
    HWND hwnd, long idObject, long idChild)
{
    CComPtr<IAccessible> acc;
    CComVariant var;
    auto hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &acc, &var);
    if (hr != S_OK) return L"";
    if (!acc) return L"";
    CComQIPtr<IServiceProvider> serviceProvider = acc;
    if (!serviceProvider) return L"";
    CComQIPtr<IAccessibleApplication> application;
    hr = serviceProvider->QueryService(
        IID_IAccessible, __uuidof(IAccessibleApplication),
        reinterpret_cast<void**>(&application));
    if (FAILED(hr)) return L"";
    if (!application) return L"";
    CComBSTR appName;
    hr = application->get_appName(&text);
    if (FAILED(hr)) return L"";
    return appName.m_str;
}

Эта функция или что-то подобное вызывается из нашей функции обратного вызова WinEventProc *1033* в ответ на событие EVENT_OBJECT_FOCUS.Это указывает на то, что мне нужно делать это для каждого окна, которое может получить фокус.

Вооружившись тем, что, по моему мнению, было ответом на мой вопрос, я углубился и реализовал интерфейс IAccessibleApplication и добавил необходимый код ко всеммои фокусируемые окна.К моему большому сожалению, моя функция QueryService никогда не вызывалась.Когда я отладил программу чтения с экрана, чтобы выяснить, почему, я обнаружил, что неявный QueryInterface, подразумеваемый следующей строкой кода, не удался.

    CComQIPtr<IServiceProvider> serviceProvider = acc;

Это привело к долгим и трудным квестам, чтобы выяснить, почему вызов QueryInterfaceтерпел неудачу.

Сначала я работал над личным проектом, поэтому я не мог использовать ресурсы, имеющиеся у моего работодателя.Затем, совершенно случайно, мне была поручена задача, которая требовала от меня предоставления информации о том, как реализовать интерфейс IAccessible2 в приложении C ++, клиенту, который нуждался в информации, чтобы помочь им сделать свои приложения более доступными.Ура, я наконец-то смог обратиться к коллегам за помощью!

Мой коллега вел меня по правильному пути.

  1. Создайте настроенную версию класса IAccessibleProxyImpl и класса CAccessibleProxy с использованием источникакод, полученный из atlacc.h.
  2. Добавьте COM_INTERFACE_ENTRY для IAccessibleApplication в COM_MAP ( BEGIN_COM_MAP / END_COM_MAP ) для моего пользовательского класса IAccessibleProxyImpl.
  3. Используйте макросы BEGIN_SERVICE_MAP, END_SERVICE_MAP и SERVICE_ENTRY для обеспечения реализации интерфейса IServiceProvider.
  4. Предоставьте переопределение для функции CWnd :: CreateAccessibleProxy , чтобы заставить мои окна использовать мой настраиваемый доступный прокси-сервер и, следовательно, мою реализацию интерфейса IAccessibleApplication.

Теперь программа чтения с экрана использует имя приложения, которое я предоставляю для интерфейса IAccessibleApplication для моего приложения.

Приложение, для которого я сделал этос открытым исходным кодом.Это мое TTSApp приложение.Я также привел пример, который демонстрирует, как использовать подобную технику для поддержки интерфейса IAccessible2, доступного здесь .

Я делюсь этим в надежде, что информация окажется полезной.

...