下来会分享如何获取子控件的IAccessible接口,在这个之前先讲点其它的东西:
获取控件如果使用Findwindow(FindwindowEx)我们需要知道的控件的ClassName和CaptionName,这两个信息可以通过Spy++等工具获取,但我们在测试工具中会常见到Role等很多属性,先看这些信息是从什么地方来的,在microsoft的站点上下载Inspect32.exe运行后如:
上面告诉了我们一个控件的详细信息,现在我们只关心classname, role, name。
下面开始实现获取子控件IAccessible接口的代码,先看函数定义:
BOOL Find ( IAccessible* paccParent, LPSTR szName, LPSTR szRole, LPSTR szClass, IAccessible** paccChild, VARIANT* pvarChild)第一个就是父窗体的IAcessible接口,依次分别为:name, role, classname, 子控件接口,类似于ID的东西(后面再解释) >看实现:
BOOL Find (
IAccessible* paccParent,
LPSTR szName,
LPSTR szRole,
LPSTR szClass,
IAccessible** paccChild,
VARIANT* pvarChild)
{
HRESULT hr;
long numChildren;
unsigned long numFetched;
VARIANT varChild;
int indexCount;
IAccessible* pCAcc = NULL;
IEnumVARIANT* pEnum = NULL;
IDispatch* pDisp = NULL;
BOOL found = false;
char szObjName[MAX_PATH], szObjRole[MAX_PATH], szObjClass[MAX_PATH], szObjState[MAX_PATH];
//Get the IEnumVARIANT interface
hr = paccParent -> QueryInterface(IID_IEnumVARIANT, (PVOID*) & pEnum);
if(pEnum)
pEnum -> Reset();
// Get child count
paccParent -> get_accChildCount(&numChildren);
for(indexCount = 1; indexCount <= numChildren && !found; indexCount++)
{
pCAcc = NULL;
// Get next child
if (pEnum)
hr = pEnum -> Next(1, &varChild, &numFetched);
else
{
varChild.vt = VT_I4;
varChild.lVal = indexCount;
}
// Get IDispatch interface for the child
if (varChild.vt == VT_I4)
{
pDisp = NULL;
hr = paccParent -> get_accChild(varChild, &pDisp);
}
else
pDisp = varChild.pdispVal;
// Get IAccessible interface for the child
if (pDisp)
{
hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pCAcc);
hr = pDisp->Release();
}
// Get information about the child
if(pCAcc)
{
VariantInit(&varChild);
varChild.vt = VT_I4;
varChild.lVal = CHILDID_SELF;
*paccChild = pCAcc;
}
else
*paccChild = paccParent;
// Skip invisible and unavailable objects and their children
GetObjectState(*paccChild, &varChild, szObjState, sizeof(szObjState));
if(NULL != strstr(szObjState, "unavailable"))
{
if(pCAcc)
pCAcc->Release();
continue;
}
GetObjectName(*paccChild, &varChild, szObjName, sizeof(szObjName));
GetObjectRole(*paccChild, &varChild, szObjRole, sizeof(szObjRole));
GetObjectClass(*paccChild, szObjClass, sizeof(szObjClass));
if ((!szName || !strcmp(szName, szObjName)) && (!szRole || !strcmp(szRole, szObjRole)) && (!szClass || !strcmp(szClass, szObjClass)))
{
found = true;
*pvarChild = varChild;
break;
}
if(!found && pCAcc)
{
// Go deeper
found = Find(pCAcc, szName, szRole, szClass, paccChild, pvarChild);
if(*paccChild != pCAcc)
pCAcc->Release();
}
}
// Clean up
if(pEnum)
pEnum -> Release();
return found;
}
看获取控件状态的函数:
UINT GetObjectState(IAccessible* pacc, VARIANT* pvarChild, LPSTR lpszState, UINT cchState)
{
HRESULT hr;
VARIANT varRetVal;
*lpszState = 0;
VariantInit(&varRetVal);
hr = pacc->get_accState(*pvarChild, &varRetVal);
if (!SUCCEEDED(hr))
return(0);
DWORD dwStateBit;
int cChars = 0;
if (varRetVal.vt == VT_I4)
{
// Convert state flags to comma separated list.
for (dwStateBit = STATE_SYSTEM_UNAVAILABLE; dwStateBit < STATE_SYSTEM_ALERT_HIGH; dwStateBit <<= 1)
{
if (varRetVal.lVal & dwStateBit)
{
cChars += GetStateTextA(dwStateBit, lpszState + cChars, cchState - cChars);
*(lpszState + cChars++) = ',';
}
}
if(cChars > 1)
*(lpszState + cChars - 1) = '\0';
}
else if (varRetVal.vt == VT_BSTR)
{
WideCharToMultiByte(CP_ACP, 0, varRetVal.bstrVal, -1, lpszState,
cchState, NULL, NULL);
}
VariantClear(&varRetVal);
return(lstrlenA(lpszState));
}
用来获取一个控件是否可用。 下来看一个例子:
HWND hwnd = ::FindWindowW( NULL,L"Run");
IAccessible *paccMainWindow = NULL;
AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&paccMainWindow);
long count = 0;
paccMainWindow->get_accChildCount(&count);
IAccessible* paccControl = NULL;
VARIANT varControl;
if (Find(paccMainWindow, "Open:", "editable text", "Edit", &paccControl, &varControl))
{
paccControl->put_accValue(varControl, CComBSTR("123"));
}
运行结果: