【问题标题】:AccessibleObjectFromPoint is returning client object instead of check boxAccessibleObjectFromPoint 正在返回客户端对象而不是复选框
【发布时间】:2017-11-14 04:20:37
【问题描述】:

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

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。 Inspect.exe 将角色正确显示为复选框。 Inspect.exe 有两个选项 - 一个查看 UI 自动化属性,另一个查看 MSAA 属性。我正在查看 MSAA 属性。 如何获得正确的角色? deos Inspect.exe 使用什么方法?

【问题讨论】:

  • 当我在 WinEventProc 中监听 SYSTEM_STATECHANGE 事件时,我得到了正确的对象。
  • 我如何开始赏金?
  • 如何在给定点获得最低级别的可访问对象? AccessibleObjectFromPoint 通常不会给出最低级别的对象,而是该对象的祖先。

标签: c# c++ wpf msaa


【解决方案1】:

Microsoft Active Accessibility / MSAA(基于IAccessible 接口)是旧版 API。您现在应该使用Windows Automation。 UIA 基于 COM,使用接口,最重要的接口是IUIAutomationElement。 Inspect.exe 使用 UIA 或 MSAA。

注意 .NET 与 UIA 兼容,WPF 可以通过 AutomationPeer classUIElement.OnCreateAutomationPeer Method 方法轻松地将其 UI 元素公开给 UIA。 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: %u\n", 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: %s\n", 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;
}

【讨论】:

  • 谢谢。所有支持 MSAA 的应用程序都支持 UIA 吗?如果不是,那么我认为我应该为 MSAA 和 UIA 编写代码。当我们使用 ElementFromPoint 时,UIA 是否给出最低级别的元素?
  • 阅读链接。 MSAA 是“遗留”,在 MSAA 和 UIA 之间存在隐含的桥梁,因此您可以忘记 MSAA。
  • 我现在正在使用 UI 自动化。有一个全局变量:
  • 忽略前面的评论。我面临两个问题。 (1) 在某些情况下,我得到的不是与之交互的控件,而是它背后的控件。例如,在记事本中,当我单击文件然后单击打开菜单项时,我会正确获取有关文件单击的信息。但是当我点击打开菜单项时,我没有得到关于打开菜单项的信息,而是记事本的文本编辑器的信息,它位于打开菜单项的后面。
  • (2) 目标应用程序变得非常慢。例如,如果我在记事本上设置了挂钩,那么记事本就会变得非常慢。我发现这是因为 ElementFromPoint 方法。我在互联网上搜索以找到解决这些问题的方法,但找不到。任何想法?谢谢。
猜你喜欢
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
  • 2011-05-05
  • 2017-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多