前面已經寫過API注入的Hook應用場景,本文則展示Hook的另一個應用場景:監控mouse事件(包括keyboard等輸入事件)。
問題背景:
一個工具多個版本(比如V1,V2)根據輸入文件動態切換,要求V1能切到V2,V2也能切回V1。
但切換控制代碼只能在最新版(比如V2)上添加,V1已發佈且穩定不能做更改。
解決思路:
V2上響應輸入button事件時判斷是否需要切到V1;
如果不切,則顯示V2主界面;
如果要切,則隱藏V2界面、創建V1進程、安裝WH_MOUSE鉤子,並創建線程監控V1的運行狀況(主要是mouse輸入事件);
Mouse鉤子運行後檢查V1中鼠標輸入條件是否觸發文件選擇button,如果觸發則終止V1並激活顯示V2(結束mouse hook);
同時,V1激活時用戶如果關閉V1,則必須hook到此事件並關閉V2(即退出工具)
具體實現:
直接上代碼Hook.dll
/*自定義結構,V2與V1的數據交換*/
typedef struct
{
DWORD HookPID;/*V1進程pid*/
BOOL HookActived;/*hook是否有效*/
BOOL MouseDown;/*mouse是否被按下*/
RECT Client; /*V1待hook的button座標區域*/
}HOOK_SHARED_MEM;
/*安裝鉤子*/
HOOK WINAPI InstallHook(int nID, HINSTANCE hInstance, LPVOID pAppSharedMem)
{
HANDLE hMemMap = ::OpenFileMapping( FILE_MAP_WRITE, false, HOOK_MEM_SHARE);
HOOK_SHARED_MEM* pSharedMem = (HOOK_SHARED_MEM*)MapViewOfFile(hMemMap, FILE_MAP_WRITE,0, 0, 0);
pSharedMem->HookActived = FALSE;
pSharedMem->MouseDown = FALSE;
memcpy(pSharedMem, pAppSharedMem, sizeof(HOOK_SHARED_MEM));
/*安裝鉤子函數*/
return SetWindowsHookEx(WH_MOUSE, HookMouseProc, hInstance, 0);
/*用完卸載鉤子則需調用UnhookWindowsHookEx*/
}
/*具體的mouse hook響應處理函數,重點*/
LRESULT WINAPI HookMouseProc(int code, WPARAM wParam, LPARAM lParam)
{
if (wParam == WM_LBUTTONDOWN || wParam == WM_LBUTTONUP || wParam == WM_LBUTTONDBLCLK)
{
HANDLE hMemMap = ::OpenFileMapping( FILE_MAP_WRITE, false, HOOK_MEM_SHARE);
if (hMemMap)
{
HOOK_SHARED_MEM* pSharedMem = (HOOK_SHARED_MEM*)MapViewOfFile(hMemMap, FILE_MAP_WRITE,0, 0, 0);
if (pSharedMem->HookPID == GetCurrentProcessId())
{
PMOUSEHOOKSTRUCT p = (PMOUSEHOOKSTRUCT)lParam;
POINT pt = p->pt;
::ScreenToClient(p->hwnd, &pt);
if (PtInRect(&pSharedMem->Client, pt))
{
if (wParam == WM_LBUTTONDOWN)
{
pSharedMem->MouseDown = TRUE;
}
else if ((wParam == WM_LBUTTONUP && pSharedMem->MouseDown)
|| wParam == WM_LBUTTONDBLCLK)
{
/*單擊或雙擊觸發文件選擇,結束V1回到V2進行判斷*/
pSharedMem->HookActived = TRUE;
TerminateProcess(GetCurrentProcess(), 0);
return TRUE;
}
}
}
}
}
return(CallNextHookEx(GetHook(WH_MOUSE), code, wParam, lParam));
}
主程序,調用hook.dll
BOOL DoSwitchV1
{
/*創建共享內存寫入HOOK_SHARED_MEM結構*/
/*創建V1進程並執行,隱藏V2界面*/
/*Load Hook.dll並執行InstallHook接口 */
/*創建監控線程*/
DWORD dwThreadId = 0;
CreateThread(NULL, 0, HookMonitorThread, m_hAppWnd, 0, &dwThreadId);
return TRUE;
}
/*監控線程函數*/
DWORD WINAPI HookMonitorThread(LPVOID lpParam)
{
Sleep(500);
/*隱藏V2*/
ShowWindow((HWND)lpParam, SW_HIDE);
HOOK_SHARED_MEM hMem;
while(1)
{
Sleep(50);
/*獲取共享內存中的HOOK_SHARED_MEM接口*/
/*是否激活*/
if (!hMem->HookActived)
{
/*檢查V1進程是否存在*/
/*不存在,則關閉hook終止V2*/
TerminateProcess(GetCurrentProcess(), 0);
}
else
{
/*顯示V2並激活V2de文件選擇對話框*/
ShowWindow((HWND)lpParam, SW_SHOW);
PostMessage((HWND)lpParam, WM_FILE_OPEN, 0, 0);
break;
}
}
return 0;
}