背景: 针对某券商客户端(招商证券/中信证券通达信版本均可测试)做一个自动交易的辅助程序,对按钮,列表各种控件进行自动化控制操作,主要通过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期)