在 windows 上使用 uiautomation 和 c++ 获取 child window 按钮的标题

Get caption of child window button on windows using uiautomation with c++

我想在 windows 上使用带有 C++ 的 uiautomation

获得此 Zoom Meeting 的“静音我的音频”标题

这是我的调用当前代码:

// Get the handle of the Zoom Meetings window.
  zoomWnd = ::FindWindow(NULL, "Zoom Meeting");
  if (zoomWnd != NULL)
  {
    std::cout<<"zoom meeting"<<"\n";
    IUIAutomationElement *zoom = GetTopLevelWindowByName(L"Zoom Meeting");
    rawListDescendants(zoom, "Meeting");
  }

输出:

zoom meeting
sName: ContentLeftPanel
sName: VideoContainerWnd
sName: Video Content
sName:
sName:
sName:

GetTopLevelWindowByName 函数定义:

IUIAutomationElement *GetTopLevelWindowByName(LPWSTR windowName)
{
  if (windowName == NULL)
  {
    return NULL;
  }

  VARIANT varProp;
  varProp.vt = VT_BSTR;
  varProp.bstrVal = SysAllocString(windowName);
  if (varProp.bstrVal == NULL)
  {
    return NULL;
  }

  IUIAutomationElement *pRoot = NULL;
  IUIAutomationElement *pFound = NULL;

  // Get the desktop element.
  HRESULT hr = g_pAutomation->GetRootElement(&pRoot);
  if (FAILED(hr) || pRoot == NULL)
  {
    goto cleanup;
  }

  // Get a top-level element by name, such as "Program Manager"
  IUIAutomationCondition *pCondition = NULL;
  hr = g_pAutomation->CreatePropertyCondition(UIA_NamePropertyId, varProp, &pCondition);
  if (FAILED(hr))
  {
    goto cleanup;
  }

  pRoot->FindFirst(TreeScope_Children, pCondition, &pFound);

cleanup:
  if (pRoot != NULL)
    pRoot->Release();

  if (pCondition != NULL)
    pCondition->Release();

  VariantClear(&varProp);
  return pFound;
}

rawListDescendants 函数定义:

void rawListDescendants(IUIAutomationElement *pParent, std::string windowType)
{
  if (pParent == NULL)
    return;

  IUIAutomationTreeWalker *pControlWalker = NULL;
  IUIAutomationElement *pNode = NULL;

  g_pAutomation->get_RawViewWalker(&pControlWalker);
  if (pControlWalker == NULL)
  {
    goto cleanup;
  }

  pControlWalker->GetFirstChildElement(pParent, &pNode);
  if (pNode == NULL)
  {
    goto cleanup;
  }

  while (pNode)
  {
    BSTR desc;
    BSTR sName;

    pNode->get_CurrentLocalizedControlType(&desc);
    pNode->get_CurrentName(&sName);

    
    std::wcout<<"sName: "<<sName<<"\n";
    //std::wcout<<"desc: "<<desc<<"\n";

    // only go into windows and panes
    if (desc != NULL)
      if (0 == wcscmp(desc, L"window") || 0 == wcscmp(desc, L"pane"))
        rawListDescendants(pNode, windowType);

    if (desc != NULL)
      SysFreeString(desc);
    if (sName != NULL)
      SysFreeString(sName);

    // get the next element
    IUIAutomationElement *pNext;
    pControlWalker->GetNextSiblingElement(pNode, &pNext);
    pNode->Release();
    pNode = pNext;
  }

cleanup:
  if (pControlWalker != NULL)
    pControlWalker->Release();

  if (pNode != NULL)
    pNode->Release();

  return;
}

Spy++ windows Zoom Meeting 树 window:

如何抓取 child window 并获取它的按钮标题

我之前没有使用过这个库,所以请尽可能详细说明(谢谢)


一个解决方案是您可以使用 IUIAutomationElement::FindAll() 找到所有按钮,然后将名称与“Mute My Audio”进行比较。

以下是您可以参考的示例(我假设您已经拥有按钮的父 window 句柄:hwnd):

IUIAutomation *pClientUIA;
IUIAutomationElement *pRootElement;

hr = pClientUIA->ElementFromHandle(hwnd, &pRootElement);
WCHAR controlName[] = L"Mute My Audio";
FindControl(UIA_ButtonControlTypeId, controlName);

查找控件函数:

void FindControl(const long controlType, BSTR controlName)
{
    HRESULT hr;
    BSTR name;
    IUIAutomationCondition *pCondition;
    VARIANT varProp;
    varProp.vt = VT_I4;
    varProp.uintVal = controlType;
    hr = pClientUIA->CreatePropertyCondition(UIA_ControlTypePropertyId, varProp, &pCondition);

    IUIAutomationElementArray *pElementFound;
    hr = pRootElement->FindAll(TreeScope_Subtree, pCondition, &pElementFound);

    int eleCount;
    pElementFound->get_Length(&eleCount);
    if (eleCount == 0)
        return;

    for (int i = 0; i <= eleCount; i++)
    {
        IUIAutomationElement *pElement;
        hr = pElementFound->GetElement(i, &pElement);
        hr = pElement->get_CurrentName(&name);
        wprintf(L"Control Name: %s\n", name);

        if (!lstrcmp(controlName, name))
        {
            printf("Found it!\n");    
        }
    }    
}

这里是有效的代码...

IUIAutomationElement *zoom = GetTopLevelWindowByName(L"Zoom Meeting");
ListDescendants(zoom, 2);

void ListDescendants(IUIAutomationElement* pParent, int indent)
{
  osFocusZoomWindow();
  if (pParent == NULL)
      return;

  IUIAutomationTreeWalker* pControlWalker = NULL;
  IUIAutomationElement* pNode = NULL;

  g_pAutomation->get_ControlViewWalker(&pControlWalker);
  if (pControlWalker == NULL)
      goto cleanup;

  pControlWalker->GetFirstChildElement(pParent, &pNode);
  if (pNode == NULL)
      goto cleanup;

  while (pNode)
  {
      BSTR sName;
      pNode->get_CurrentName(&sName);
      //std::wcout << sName << L"\n";
      std::wstring strName(sName, SysStringLen(sName));
      if (strName.find(L"currently unmuted") != std::string::npos)
      {
        std::cout<<"####### UNMUTE"<<"\n";
      }else if(strName.find(L"currently muted") != std::string::npos){
        std::cout<<"####### MUTE"<<"\n";
      }
      SysFreeString(sName);

      ListDescendants(pNode, indent+1);
      IUIAutomationElement* pNext;
      pControlWalker->GetNextSiblingElement(pNode, &pNext);
      pNode->Release();
      pNode = pNext;
  }

cleanup:
  if (pControlWalker != NULL)
      pControlWalker->Release();

  if (pNode != NULL)
      pNode->Release();

  return;
}

特别是在我的案例中,zoom 具有此功能,当我们将鼠标悬停在 zoom window 上时,它会显示所有控制按钮。这就是我没有列出每个 window 和按钮的原因。所以我只是在调用上面的函数 osFocusZoomWindow()

之前聚焦并将光标位置设置为缩放 window