在MFC应用程序中实现IServiceProvider

时间:2018-05-31 00:11:32

标签: c++ mfc com visual-studio-2017

我需要在开源MFC应用程序中实现IServiceProvider接口;特别是我的TTSApp申请。

我正在尝试添加对IAccessibleApplication interface的支持,屏幕阅读器使用该IServiceProviderImpl Class来获取有关应用程序名称和版本信息的信息。

Google Chrome似乎通过AXPlatformNodeWin类实现了IServiceProvider接口,该类派生自CComObjectRootEx类以及其他类和接口。问题是MFC应用程序不使用CComObjectRootEx类;它由ATL使用。

我找到了{{3}}。不幸的是,我无法找到有关它如何适合应用程序上下文的任何信息。我的类层次结构中的哪个类需要从IServiceProviderImpl类派生;我的CWinApp派生类,我的CDialogEx派生类,还是其他一些类?

1 个答案:

答案 0 :(得分:0)

我在寻找这个问题答案的过程中学到了很多东西。在这个任务中,我堕下了兔子洞(Alice's Adventures in Wonderland,查尔斯·路特维奇·道奇森,a.k.a.刘易斯·卡罗尔),却发现Cthulhu(H. {Lovecraft The Call of Cthulhu)在等我。

我最初的研究使我得到了afxwin.h中定义的以下宏。

  • DECLARE_INTERFACE_MAP
  • BEGIN_INTERFACE_MAP
  • END_INTERFACE_MAP
  • BEGIN_INTERFACE_PART
  • END_INTERFACE_PART

我可以在TN038: MFC/OLE IUnknown Implementation技术说明中找到这些宏的最佳文档。一个很好的示例演示了这些宏的使用以及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 callback function调用,以响应EVENT_OBJECT_FOCUS事件。这表明我需要为每个可以获得焦点的窗口执行此操作。

我认为是我的问题的答案,我潜入并实现了IAccessibleApplication接口,并为我所有可关注的窗口添加了必要的代码。令我恐惧的是,我的QueryService函数从未被调用过。当我调试屏幕阅读器以找出原因时,我发现下面一行代码隐含的隐式QueryInterface失败了。

    CComQIPtr<IServiceProvider> serviceProvider = acc;

这导致了一个漫长而艰巨的任务,即发现调用QueryInterface失败的原因。

我一开始就在做个人项目,所以我无法调用我雇主的资源。然后,完全偶然的,我被分配了一项任务,要求我提供有关如何在C ++应用程序中实现IAccessible2接口的信息给需要信息的客户端,以帮助他们使应用程序更易于访问。华友世纪,我终于可以联系同事寻求帮助了!

我的同事引导我走上正确的道路。

  1. 使用从atlacc.h获取的源代码创建IAccessibleProxyImpl类和CAccessibleProxy类的自定义版本。
  2. 为我的自定义IAccessibleProxyImpl类在COM_MAP(COM_INTERFACE_ENTRY / BEGIN_COM_MAP)中为IAccessibleApplication添加END_COM_MAP
  3. 使用BEGIN_SERVICE_MAP, END_SERVICE_MAP, and SERVICE_ENTRY macros提供IServiceProvider接口的实现。
  4. CWnd::CreateAccessibleProxy function提供覆盖,以使我的窗口使用我的自定义可访问代理,从而实现IAccessibleApplication接口。
  5. 现在,屏幕阅读器使用我为我的应用程序提供的IAccessibleApplication接口的应用程序名称。

    我这样做的应用程序是开源的。这是我的TTSApp申请。我还举了一个例子来演示如何使用类似的技术来支持可用的IAccessible2接口here

    我正在分享这一点,希望这些信息能够证明是有用的。