下来会分享如何获取子控件的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")); }运行结果: