首页
社区
课程
招聘
[求助]MSAA IAccessible接口枚举信息不全
发表于: 2016-12-16 19:08 7494

[求助]MSAA IAccessible接口枚举信息不全

2016-12-16 19:08
7494
背景: 针对某券商客户端(招商证券/中信证券通达信版本均可测试)做一个自动交易的辅助程序,对按钮,列表各种控件进行自动化控制操作,主要通过MSAA api来完成. 现在碰到了一个问题,请教下各位.

问题: 利用IAccessible接口遍历窗口,遍历的数据不全,和用工具AccCheckUI/AccExplorer32/AccViewer遍历的数据比起来,不一样有缺少,遍历的代码网上抄来的,改了下,自己调试了好久没找到原因,请大家帮忙找找原因

如图所示,"买入","卖出"都是窗口"MainViewBar",自定义类MHPToolBar下子元素,用工具AccViewer可以找到"买入"的层次关系,但自己的遍历代码无法找到role是"工具栏"的对象,找到的role=菜单栏,数据都不对了. 主要代码如下:
        //查找自定义类MHPToolBar窗口句柄
  HWND hwnd = ::FindWindow(_T("TdxW_MainFrame_Class"), NULL);
  if (hwnd)
  {
    OFindWnd oWnd(hwnd, _T("MHPToolBar"), _T("MainViewBar"));
    hwnd = oWnd.m_hWnd;
  }
  if (!::IsWindow(hwnd))
    return FALSE;

  //枚举并查找"买入"控件
  IAccessible *pIAcc = NULL;
  HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&pIAcc);
  if (SUCCEEDED(hr) && pIAcc)
  {
    VARIANT varChild;
    VariantInit(&varChild);
    IAccessible *pIAccChild = NULL;
    g_sSubPrefix = "";
    if (CXMSAALib::FindAccessible(pIAcc, _T("买入"), _T("按下按钮"), _T("MHPToolBar"), &pIAccChild, &varChild))
    {
      //find
      DEBUGTRACE("find");
    }
    else
    {
      //not find
      DEBUGTRACE("not find");
    }
  }


BOOL CXMSAALib::FindAccessible(IAccessible* accParent,
  LPTSTR szName,
  LPTSTR szRole,
  LPTSTR szClass,
  IAccessible** paccChild,
  VARIANT* pvarChild)
{
  BOOL found = false;
  TCHAR szObjName[MAX_PATH], szObjRole[MAX_PATH], szObjClass[MAX_PATH], szObjState[MAX_PATH];
  IAccessible* pCAcc = NULL;

  VARIANT* vt_output = NULL;
  if (accParent == NULL)
    return FALSE;

  long lChildCount = 0;
  HRESULT hr = accParent->get_accChildCount(&lChildCount);
  if (FAILED(hr) || (lChildCount == 0))
    return FALSE;
  DEBUGTRACE("%s obj=0x%p, numChildren=%d", g_sSubPrefix.c_str(), accParent, lChildCount);

  vt_output = new VARIANT[lChildCount];
  for (int i = 0; i < lChildCount; i++)
    VariantInit(&vt_output[i]);

  long lNewChildCount = 0;
  hr = AccessibleChildren(accParent, 0, lChildCount, vt_output, &lNewChildCount);
  if (FAILED(hr))
    goto exit;

  for (int j = 0; j < lNewChildCount && !found; j++)
  {
    VARIANT varChild;
    VariantInit(&varChild);
    IDispatch* disp = NULL;
    pCAcc = NULL;
    //If the vt member of an array element is VT_DISPATCH, 
    //then the pdispVal member for that element is the address of the child object's IDispatch interface.
    if (vt_output[j].vt == VT_DISPATCH)
    {
      disp = vt_output[j].pdispVal;
    }
    //If the vt member of an array element is VT_I4, then the lVal member for that element is the child ID.
    else if (vt_output[j].vt == VT_I4)
    {
      hr = accParent->get_accChild(vt_output[j], &disp);
      //If a child is an element, get_accChild returns S_FALSE, and the parent will provide information for that child. 
      if (!SUCCEEDED(hr) || !disp)
      {
        //element deal
        varChild.vt = VT_I4;
        varChild.lVal = vt_output[j].lVal;
        *paccChild = accParent;
      }
    }
    else //未发现有进入
    {
      continue;
    }

    if (disp)
    {
      hr = disp->QueryInterface(IID_IAccessible, (void**)&pCAcc);
      if (FAILED(hr))
        continue;
      varChild.vt = VT_I4;
      varChild.lVal = CHILDID_SELF;
      *paccChild = pCAcc;
    }

    // Skip invisible and unavailable objects and their children
    GetObjectStateString(*paccChild, &varChild, szObjState, MAX_PATH);
//     if (NULL != _tcsstr(szObjState, _T("unavailable")))
//     {
//         if (pCAcc)
//           pCAcc->Release();
//         continue;
//     }
    UINT nState = 0;
    GetObjectState(*paccChild, &varChild, nState);
    // check if object is available
    if ((nState & STATE_SYSTEM_UNAVAILABLE /*STATE_SYSTEM_INVISIBLE*/ /*| STATE_SYSTEM_OFFSCREEN*/))
    {
       SAFE_RELEASE(pCAcc);
       continue;
    }
    GetObjectName(*paccChild, &varChild, szObjName, MAX_PATH);
    GetObjectRoleString(*paccChild, &varChild, szObjRole, MAX_PATH);
    //GetObjectClass(*paccChild, szObjClass, sizeof(szObjClass));
    memset(szObjClass, 0, sizeof(szObjClass));
    HWND hwndChild = 0;
    WindowFromAccessibleObject(*paccChild, &hwndChild);
    if (hwndChild)
      GetClassName(hwndChild, szObjClass, MAX_PATH);

    if ((!szName || !_tcscmp(szName, szObjName)) && (!szRole || !_tcscmp(szRole, szObjRole)) && (!szClass || !_tcscmp(szClass, szObjClass)))
    {
      found = true;
      *pvarChild = varChild;
      break;
    }

    if (pCAcc)
      DEBUGTRACE("%s name=%s, role=%s, class=%s, state=%s obj=0x%p,idx=0x%p", g_sSubPrefix.c_str(), szObjName, szObjRole, szObjClass, szObjState, pCAcc, varChild.lVal);
    else
      DEBUGTRACE("%s name=%s, role=%s, class=%s, state=%s elem,idx=0x%p", g_sSubPrefix.c_str(), szObjName, szObjRole, szObjClass, szObjState, varChild.lVal);
    
    if (!found && pCAcc)
    {
      string sParentPrefix = g_sSubPrefix;
      g_sSubPrefix += "    ";
      // Go deeper
      found = FindAccessible(pCAcc, szName, szRole, szClass, paccChild, pvarChild);
      if (*paccChild != pCAcc)
        pCAcc->Release();
      g_sSubPrefix = sParentPrefix;
    }
  }
exit:
  if (vt_output)
  {
    for (int k = 0; k < lChildCount; k++)
      VariantClear(&vt_output[k]);
    delete vt_output;
  }
  return found;
}


运行的遍历错误结果:
obj=0x00E088C8, numChildren=7
name=系统, role=菜单栏, class=MHPToolBar obj=0x00E03FA0,idx=1
    obj=0x00E03FA0, numChildren=0
name=, role=标题栏, class=MHPToolBar obj=0x00E051C0,idx=2
    obj=0x00E051C0, numChildren=5
    name=输入法, role=按下按钮, class=MHPToolBar elem,idx=1
    name=最小化, role=按下按钮, class=MHPToolBar elem,idx=2
    name=最大化, role=按下按钮, class=MHPToolBar elem,idx=3
    name=上下文帮助, role=按下按钮, class=MHPToolBar elem,idx=4
    name=关闭, role=按下按钮, class=MHPToolBar elem,idx=5
name=应用程序, role=菜单栏, class=MHPToolBar obj=0x00E2F598,idx=3  <-----这里开始遍历数据就不一样了,没有得到预期的role=工具栏
    obj=0x00E2F598, numChildren=0
name=MainViewBar, role=客户端, class=MHPToolBar obj=0x00E2F388,idx=4
    obj=0x00E2F388, numChildren=1
    name=, role=窗口, class=ComboBox obj=0x00E2F6F8,idx=1
        obj=0x00E2F6F8, numChildren=7
        name=系统, role=菜单栏, class=ComboBox obj=0x00E2F5F0,idx=1
            obj=0x00E2F5F0, numChildren=0
        name=, role=标题栏, class=ComboBox obj=0x00E2F648,idx=2
            obj=0x00E2F648, numChildren=5
            name=输入法, role=按下按钮, class=ComboBox elem,idx=1
            name=最小化, role=按下按钮, class=ComboBox elem,idx=2
            name=最大化, role=按下按钮, class=ComboBox elem,idx=3
            name=上下文帮助, role=按下按钮, class=ComboBox elem,idx=4
            name=关闭, role=按下按钮, class=ComboBox elem,idx=5
        name=应用程序, role=菜单栏, class=ComboBox obj=0x00E2F4E8,idx=3

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
  • 1.png (67.36kb,12次下载)
收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 126
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
顺便问下MSAA api有么有封装好用的库,最好c++版本的 :)
还有券商交易有么有开源的接口,我这种做法有点傻的 hoho~~
2016-12-16 19:12
0
雪    币: 126
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
找到问题原因,没有初始化...
::CoInitialize(NULL);
2016-12-18 00:34
0
雪    币: 28
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
哦,这样啊,好厉害呢
2017-8-10 05:15
0
游客
登录 | 注册 方可回帖
返回
//