分类: C/C++
2008-03-17 17:35:53
Microsoft© Active Accessibility 2.0 is a COM-based technology that improves the way accessibility aids work with applications running on Microsoft Windows?. It provides dynamic-link libraries that are incorporated into the operating system as well as a COM interface and application programming elements that provide reliable methods for exposing information about user interface elements.基础
HWND hWndMainWindow; IAccessible *paccMainWindow = NULL; HRESULT hr; //得到标题为"运行"的窗口的句柄 if(NULL == (hWndMainWindow = FindWindow(NULL, "运行"))) { MessageBox(NULL, "没有发现窗口!", "错误", MB_OK); } else { //通过窗口句柄得到窗口的 IAccessible 接口指针。 if(S_OK == (hr = AccessibleObjectFromWindow(hWndMainWindow, OBJID_WINDOW, IID_IAccessible, (void**)&paccMainWindow))) { //……我们可以通过这个指针paccMainWindow进行操作。 paccMainWindow->Release(); } }现在我们已经得到窗口的 IAccessible 接口指针了(paccMainWindow),那么,我们可以干什么呢?我们怎么得到窗口中某个控件的 IAccessible 接口指针呢?我们就以上面的运行窗口为例。看看如何得到文本框的 IAccessible 接口指针!!
Name = "打开(O):" Role = "可编辑文字" Window className = "Edit"当开发自定义、owner drawn 或者无窗口的控件时,为同一窗口的每个"角色-名字"指定独一无二的表示是一个非常好的编程习惯。然而,如果由于某种原因,同一窗口中的2个 UI 元素具有同样的"角色-名字"对,那么就需要增加一个参数--windows 类--以唯一的来表示这个元素。
IAccessible* paccControl = NULL;//输入框的 IAccessible 接口 VARIANT varControl; //子ID。 FindChild( paccMainWindow, "打开(O):", "可编辑文字", "Edit", &paccControl, &varControl )第一个参数是先前得到的窗口 IAccessible 接口指针。
BOOL FindChild (IAccessible* paccParent, LPSTR szName, LPSTR szRole, LPSTR szClass, IAccessible** paccChild, VARIANT* pvarChild) { HRESULT hr; long numChildren; unsigned long numFetched; VARIANT varChild; int index; IAccessible* pCAcc = NULL; IEnumVARIANT* pEnum = NULL; IDispatch* pDisp = NULL; BOOL found = false; char szObjName[256], szObjRole[256], szObjClass[256], szObjState[256]; //得到父亲支持的IEnumVARIANT接口 hr = paccParent -> QueryInterface(IID_IEnumVARIANT, (PVOID*) & pEnum); if(pEnum) pEnum -> Reset(); //取得父亲拥有的可访问的子的数目 paccParent -> get_accChildCount(&numChildren); //搜索并比较每一个子ID,找到名字、角色、类与输入相一致的。 for(index = 1; index <= numChildren && !found; index++) { pCAcc = NULL; // 如果支持IEnumVARIANT接口,得到下一个子ID //以及其对应的 IDispatch 接口 if (pEnum) hr = pEnum -> Next(1, &varChild, &numFetched); else { //如果一个父亲不支持IEnumVARIANT接口,子ID就是它的序号 varChild.vt = VT_I4; varChild.lVal = index; } // 找到此子ID对应的 IDispatch 接口 if (varChild.vt == VT_I4) { //通过子ID序号得到对应的 IDispatch 接口 pDisp = NULL; hr = paccParent -> get_accChild(varChild, &pDisp); } else //如果父支持IEnumVARIANT接口可以直接得到子IDispatch 接口 pDisp = varChild.pdispVal; // 通过 IDispatch 接口得到子的 IAccessible 接口 pCAcc if (pDisp) { hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pCAcc); hr = pDisp->Release(); } // Get information about the child if(pCAcc) { //如果子支持IAccessible 接口,那么子ID就是CHILDID_SELF VariantInit(&varChild); varChild.vt = VT_I4; varChild.lVal = CHILDID_SELF; *paccChild = pCAcc; } else //如果子不支持IAccessible 接口 *paccChild = paccParent; //跳过了有不可访问状态的元素 GetObjectState(*paccChild, &varChild, szObjState, sizeof(szObjState)); if(NULL != strstr(szObjState, "unavailable")) { if(pCAcc) pCAcc->Release(); continue; } //通过get_accName得到Name GetObjectName(*paccChild, &varChild, szObjName, sizeof(szObjName)); //通过get_accRole得到Role GetObjectRole(*paccChild, &varChild, szObjRole, sizeof(szObjRole)); //通过WindowFromAccessibleObject和GetClassName得到Class GetObjectClass(*paccChild, szObjClass, sizeof(szObjClass)); //以上实现代码比较简单,大家自己看代码吧。 //如果这些参数与输入相符或输入为NULL if ((!szName || !strcmp(szName, szObjName)) && (!szRole || !strcmp(szRole, szObjRole)) && (!szClass || !strcmp(szClass, szObjClass))) { found = true; *pvarChild = varChild; break; } if(!found && pCAcc) { // 以这次得到的子接口为父递归调用 found = FindChild(pCAcc, szName, szRole, szClass, paccChild, pvarChild); if(*paccChild != pCAcc) pCAcc->Release(); } }//End for // Clean up if(pEnum) pEnum -> Release(); return found; } // UI元素的状态也表示成整型形式。因为一个状态可以有多个值, //例如可选的、可做焦点的,该整数是反映这些值的位的或操作结果。 //将这些或数转换成相应的用逗号分割的状态字符串。 UINT GetObjectState(IAccessible* pacc, VARIANT* pvarChild, LPTSTR 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) { // 根据返回的状态值生成以逗号连接的字符串。 for (dwStateBit = STATE_SYSTEM_UNAVAILABLE; dwStateBit < STATE_SYSTEM_ALERT_HIGH; dwStateBit <<= 1) { if (varRetVal.lVal & dwStateBit) { cChars += GetStateText(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(lstrlen(lpszState)); }好了!!我们已经成功得到文本框的 IAccessible 接口指针了!!现在你可以用这个接口指针为所欲为了!!!呵呵:)
//在文本输入框输入"regedit" if(1 == FindChild (paccMainWindow, "打开(O):", "可编辑文字", "Edit", &paccControl, &varControl)) { //在这里修改文本编辑框的值 hr = paccControl->put_accValue(varControl, CComBSTR("regedit")); paccControl->Release(); VariantClear(&varControl); } // 找到确定按钮,并执行默认动作。 if(1 == FindChild (paccMainWindow, "确定", "按下按钮", "Button", &paccControl, &varControl)) { //这里执行按钮的默认动作,即"按下这个按钮" hr = paccControl->accDoDefaultAction(varControl); paccControl->Release(); VariantClear(&varControl); }现在,你会发现已经成功启动了注册表编辑器!!
INPUT input[4]; memset(input, 0, sizeof(input)); //设置模拟键盘输入 input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD; input[0].ki.wVk = input[2].ki.wVk = VK_MENU; input[1].ki.wVk = input[3].ki.wVk = VK_F4; // 释放按键,这非常重要 input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP; SendInput(4, input, sizeof(INPUT));具体用法大家还是查MSDN吧,这里就不罗嗦了!!:)
if(NULL == (hWndMainWindow = FindWindow(NULL, szMainTitle))) { hEventHook = SetWinEventHook( EVENT_MIN, // eventMin ID EVENT_MAX, // eventMax ID NULL, // always NULL for outprocess hook WinCreateNotifyProc, // call back function 0, // idProcess 0, // idThread // always the same for outproc hook WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT); }第一、二个参数用来指定监视事件的范围。第四个参数是定义的回调函数。
void CALLBACK WinCreateNotifyProc( HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject, LONG idChild, DWORD idThread, DWORD dwmsEventTime ) { if( event != EVENT_OBJECT_CREATE) return; char bufferName[256]; IAccessible *pacc=NULL; VARIANT varChild; VariantInit(&varChild); //得到触发事件的 UI 元素的 IAccessible 接口/子ID对 HRESULT hr= AccessibleObjectFromEvent(hwndMsg, idObject, idChild, &pacc, &varChild); if(!SUCCEEDED(hr)) { VariantClear(&varChild); return; } //得到 UI 元素的Name,并比较,如果是"运行"就发送消息给主线程。 GetObjectName(pacc, &varChild, bufferName, sizeof(bufferName)); if(strstr(bufferName, szMainTitle)) PostThreadMessage(GetCurrentThreadId(), WM_TARGET_WINDOW_FOUND, 0, 0); return; }恩…………,一个应用基本成型了,虽然比较简单。就先写这么多吧,请关注后续介绍。