Windows 作業系統建立在事件驅動機制之上,系統各元件間的溝通皆透過訊息傳遞實現。一般應用程式僅能處理自行程序內部的訊息或來自其他程序的訊息,若需攔截處理程序外傳遞的訊息,則必須採用 Hook(鉤子) 技術。鉤子是 Windows 系統中極重要的介面,能輕鬆截獲並處理應用程式間傳遞的訊息,進而完成一般應用程式難以實現的特殊功能。本文以 VC++ 6.0 為開發環境,詳細探討鉤子的基本概念、實作流程,並以滑鼠鉤子為例示範完整應用。
鉤子本質上是一段處理系統訊息的程式碼,透過系統呼叫將其掛入系統。每種鉤子負責截獲特定類型的訊息。鉤子機制允許應用程式監視指定視窗的訊息或特定事件,被監視視窗可以是本程序內的,也可以是其他程序建立的。當特定訊息發出且在抵達目標視窗前,鉤子程式先行截獲並取得控制權,此時可在鉤子函數中修改、處理甚至強制終止該訊息的傳遞。
系統為每個鉤子維護一個指標鏈結串列(鉤子鏈),指向各鉤子處理函數。新安裝的鉤子置於鏈首,最早安裝的在鏈尾。訊息發生時,系統從鏈首開始呼叫鉤子函數,即後安裝的鉤子優先獲得控制權。鉤子處理函數必須是回呼函數(callback function),且不能定義為類別成員函數,必須為一般 C 函數。
根據監視範圍,鉤子分為全域鉤子與執行緒鉤子。執行緒鉤子僅監視特定執行緒,全域鉤子則監視系統中所有執行緒。全域鉤子功能強大但實作較繁瑣:其鉤子函數必須封裝在動態連結庫(DLL)中才能使用。
安裝鉤子的核心是 SetWindowsHookEx() 函數,將回呼函數置於鉤子鏈首。函數原型如下:
HHOOK SetWindowsHookEx(
int idHook, // 鉤子類型
HOOKPROC lpfn, // 鉤子處理函數指標
HINSTANCE hMod, // 包含鉤子函數的模組句柄
DWORD dwThreadId // 被監視的執行緒 ID,0 表示全域鉤子
);
常見鉤子類型(idHook)包含 13 種,如 WH_KEYBOARD(鍵盤)、WH_MOUSE(滑鼠)、WH_GETMESSAGE(訊息取得)等。參數 hMod 需指向包含鉤子函數的 DLL 實例句柄;dwThreadId 設為 0 即為全域鉤子。
訊息處理完成後,應呼叫 CallNextHookEx() 將事件傳遞給鏈中下一個鉤子,否則可能導致系統行為異常或鎖死。卸載鉤子使用 UnhookWindowsHookEx(),釋放資源。
以下示範如何建立滑鼠全域鉤子 DLL 並應用於應用程式。
Win32 環境下多程序共享資料需將變數置於獨立資料段並設為讀寫共享:
#pragma data_seg("TestData")
HWND glhPrevTarWnd = NULL; // 目標視窗句柄
HWND glhHook = NULL; // 鉤子句柄
HINSTANCE glhInstance = NULL; // DLL 實例句柄
#pragma data_seg()
// .def 檔案中設定區段屬性
SECTIONS
TestData READ WRITE SHARED
void StartHook(HWND hWnd) {
glhHook = SetWindowsHookEx(WH_MOUSE, MouseProc, glhInstance, 0);
}
LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
LPMOUSEHOOKSTRUCT pMouseHook = (LPMOUSEHOOKSTRUCT)lParam;
HWND hTargetWnd = pMouseHook->hwnd;
HWND hParentWnd = hTargetWnd;
// 取得最上層視窗句柄
while (hParentWnd != NULL) {
hTargetWnd = hParentWnd;
hParentWnd = GetParent(hTargetWnd);
}
if (hTargetWnd != glhPrevTarWnd) {
char szCaption[100];
GetWindowText(hTargetWnd, szCaption, 100);
// 此處可處理視窗標題,如顯示、記錄等
glhPrevTarWnd = hTargetWnd;
}
}
return CallNextHookEx((HHOOK)glhHook, nCode, wParam, lParam);
}
void StopHook() {
UnhookWindowsHookEx((HHOOK)glhHook);
}
將編譯好的 DLL 載入程序,呼叫 StartHook() 安裝鉤子,卸載 DLL 時呼叫 StopHook() 移除鉤子。測試證實:滑鼠移動至任意視窗,即可透過攔截訊息獲取該視窗標題。
鉤子技術,特別是全域鉤子,提供了強大的系統級訊息攔截能力,廣泛應用於自動化監控、熱鍵實作、輸入法編輯器等領域。本文涵蓋原理、API 使用、DLL 封裝與實戰範例,讀者可依此基礎擴充鍵盤鉤子、Shell 鉤子等其他類型。實作時務必注意:鉤子會影響系統效能,用畢立即卸載;且必須正確呼叫 CallNextHookEx() 維持鏈完整性。