Windows 提供 11 種標準鉤子類型(WH_KEYBOARD、WH_MOUSE、WH_JOURNALRECORD 等),形成一條鉤子鏈。系統產生消息時,依序傳遞給鏈首回呼函數;各函數可選擇處理、修改、攔截或呼叫 CallNextHookEx 傳遞給下一節點。鍵盤鉤子(WH_KEYBOARD)因應用廣泛、實用性高,成為最常被採用的類型。
int FAR PASCAL KeyboardProc(int nCode, WORD wParam, DWORD lParam);
nCode:決定是否處理該消息(>=0 表需處理)。wParam、lParam:封裝虛擬鍵碼、掃描碼、轉換旗標等資訊。EXPORTS 導出。SetWindowsHookEx 參數詳解HHOOK SetWindowsHookEx(
int idHook, // WH_KEYBOARD
HOOKPROC lpfn, // 回呼函數位址
HINSTANCE hMod, // DLL 實例控制代碼
DWORD dwThreadId // 0 = 全域鉤子
);
全域鍵盤鉤子必須置於 DLL 中,因系統需將回呼函數映射至所有目標程序位址空間。DLL 匯出方式三種:
KeyboardProc @4,調用時用 KEYDLL.KeyboardProc。LoadLibrary + GetProcAddress 取得函數指標。IMPLIB 產生 .lib,專案直接連結。// 全域變數
static HINSTANCE hInstance;
static HWND hWndMain;
static HHOOK hKeyHook;
static BOOL HookStates = FALSE;
static FARPROC lpfnKeyHook = (FARPROC)KeyHook;
// 1. DLL 進入點
int FAR PASCAL LibMain(HANDLE hModule, UINT wDataSeg, UINT cbHeapSize, LPSTR lpszCmdLine)
{
if (cbHeapSize) UnlockData(0);
hInstance = hModule;
return 1;
}
// 2. 系統卸載清理
int WINAPI WEP(int bSystemExit) { return 1; }
// 3. 初始化:接收主程式視窗控制代碼
int WINAPI InitHooksDll(HWND hwndMainWindow)
{
hWndMain = hwndMainWindow;
return 0;
}
// 4. 安裝/移除鉤子
int WINAPI InstallFilter(BOOL nCode)
{
if (nCode) {
hKeyHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)lpfnKeyHook, hInstance, 0);
HookStates = TRUE;
} else {
UnhookWindowsHookEx(hKeyHook);
HookStates = FALSE;
}
return 0;
}
// 5. 鍵盤回呼核心:攔截 Win 鍵並轉發自訂消息
LRESULT CALLBACK KeyHook(int nCode, WORD wParam, DWORD lParam)
{
static BOOL msflag = FALSE;
if (nCode >= 0 && HookStates) {
// Win95/98 虛擬鍵碼:0x5B (左 Win)、0x5C (右 Win)
if (wParam == 0x5B || wParam == 0x5C) {
// lParam 高位判斷按下/釋放
if ((lParam & 0x80000000) == 0) { // 按下
msflag = TRUE;
PostMessage(hWndMain, 0x7FFF, 1, 3L);
} else { // 釋放
msflag = FALSE;
PostMessage(hWndMain, 0x7FFF, 2, 3L);
}
}
}
return CallNextHookEx(hKeyHook, nCode, wParam, lParam);
}
InitHooksDll(hMainWnd); // 傳遞主視窗 HWND
InstallFilter(TRUE); // 啟用鉤子
case 0x7FFF: // 自訂消息
if (lParam == 3L) {
if (wParam == 1) WinKeyDown = TRUE; // Win 鍵按下
else if (wParam == 2) WinKeyDown = FALSE; // Win 鍵釋放
}
break;
case WM_KEYDOWN:
if (WinKeyDown) {
switch (LOBYTE(wParam)) {
case 0x5B: /* Win+[ */ break;
case 0x5D: /* Win+] */ break;
// 任意組合鍵擴充...
}
}
break;
InstallFilter(FALSE); // 釋放鉤子資源
效能提醒:全域鉤子會增加系統消息派送延遲,請於必要時才啟用,並在表單銷毀或應用程式退出時確實呼叫
UnhookWindowsHookEx。