分类: 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.
基础
Active Accessibility 原理
如何得到 IAccessible 接口指针
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 接口指针了!!现在你可以用这个接口指针为所欲为了!!!呵呵:)
在 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吧,这里就不罗嗦了!!:)
监视WinEventsif(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;
} 恩…………,一个应用基本成型了,虽然比较简单。就先写这么多吧,请关注后续介绍。
附录:
注:
参考资料: