AccessObjectFromPoint 返回客户端对象而不是复选框



我正在使用Visual Studio 2017。我在解决方案中添加了两个项目。一个项目是使用 C# 和 WPF 的。另一个是带有ATL的VC++。在 C# 中,我在 VC++ 项目中调用了一个函数,该函数设置了低级鼠标钩子。低级鼠标进程中的部分代码如下所示:

MSLLHOOKSTRUCT stMouse = *(MSLLHOOKSTRUCT*)(lParam);
POINT pt = stMouse.pt;
IAccessible* pAcc;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varChild);
VARIANT varRole;
hr = pAcc->get_accRole(varChild, &varRole);

当我通过单击MS Word 2013中"查看"选项卡下的复选框进行测试时,我获得了WM_LBUTTONDOWN和WM_LBUTTONUP消息的客户端角色。但是我应该得到角色作为复选框。我检查了Windows 10 SDK附带的Inspect.exe。"检查.exe将角色正确显示为"复选框。检查.exe有两个选项 - 一个用于查看UI 自动化属性,另一个用于查看 MSAA 属性。我看到的是 MSAA 属性。如何获得正确的角色?德奥斯检查.exe使用什么方法?

Microsoft Active Accessibility/MSAA(基于IAccessible接口(是一个遗留的API。您现在应该使用 Windows 自动化。UIA基于COM,并使用接口,最重要的接口是IUIAutomationElement。检查.exe使用 UIA 或 MSAA。

注意.NET与UIA兼容,WPF可以通过AutomationPeer类和UIElement.OnCreateAutomationPeer Method方法非常容易地向UIA公开其UI元素。WPF 提供了一个默认实现,可以根据需要进行调整。

下面是一个与您在 C++ 中类似的示例,使用 UIA API,而不是 MSAA(我们可以使用 C# 编写相同的代码(:

#include "stdafx.h" // needs <uiautomation.h>
class CCoInitialize { // https://blogs.msdn.microsoft.com/oldnewthing/20040520-00/?p=39243
public:
  CCoInitialize() : m_hr(CoInitialize(NULL)) { }
  ~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
  operator HRESULT() const { return m_hr; }
  HRESULT m_hr;
};
// this is a poor-man COM object class
class CHandler : public IUIAutomationEventHandler
{
public:
  HRESULT QueryInterface(REFIID riid, LPVOID * ppv)
  {
    if (!ppv) return E_INVALIDARG;
    *ppv = NULL;
    if (riid == IID_IUnknown || riid == IID_IUIAutomationEventHandler)
    {
      *ppv = this;
      return NOERROR;
    }
    return E_NOINTERFACE;
  }
  ULONG AddRef() { return 1; }
  ULONG Release() { return 1; }
  // this will be called by UIA
  HRESULT HandleAutomationEvent(IUIAutomationElement *sender, EVENTID eventId)
  {
    wprintf(L"Event id: %un", eventId);
    return S_OK;
  }
};
int main()
{
  CCoInitialize init;
  // this sample uses Visual Studio's ATL smart pointers, but it's not mandatory at all
  CComPtr<IUIAutomation> uia;
  if (SUCCEEDED(uia.CoCreateInstance(CLSID_CUIAutomation)))
  {
    // get mouse pos now
    POINT pt;
    GetCursorPos(&pt);
    // find what type of "control" was under the mouse
    CComPtr<IUIAutomationElement> element;
    if (SUCCEEDED(uia->ElementFromPoint(pt, &element)))
    {
      CComBSTR type;
      element->get_CurrentLocalizedControlType(&type);
      wprintf(L"type at %u,%u: %sn", pt.x, pt.y, type.m_str);
    }
    // get root
    CComPtr<IUIAutomationElement> root;
    if (SUCCEEDED(uia->GetRootElement(&root)))
    {
      // add a handler that will trigger every time you open any window on the desktop
      // in the real world, you'll need to delete this pointer to avoid memory leaks of course
      CHandler *pHandler = new CHandler();
      if (SUCCEEDED(uia->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope::TreeScope_Children, NULL, pHandler)))
      {
        // since this is a console app, we need to run the Windows message loop otherwise, you'll never get any UIA events
        // for this sample, just press CTRL-C to stop the program. in the real world, you'll need to get out properly
        // if you have a standard windows app, the loop will be already there
        while (TRUE)
        {
          MSG msg;
          while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
          {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
          }
        }
      }
    }
  }
  return 0;
}

最新更新