SetWindowsHookEx 是 Windows 鉤子機制的入口函數,用於將自定義回調函數插入系統訊息處理鏈中,實現全域或線程級的訊息攔截、監控、修改甚至阻斷。
function SetWindowsHookEx(
idHook: Integer; // 鉤子類型
lpfn: TFNHookProc; // 回調函數指標
hmod: HINST; // 包含回調函數的模組句柄 (EXE/DLL)
dwThreadId: DWORD // 目標線程 ID,0 表示系統級全域鉤子
): HHOOK; stdcall; // 返回鉤子句柄,0 表示失敗
| 常數 | 值 | 層級 | 用途 |
|---|---|---|---|
| WH_MSGFILTER | -1 | 線程級 | 攔截對話框/選單/捲軸等控件互動訊息 |
| WH_JOURNALRECORD | 0 | 系統級 | 記錄所有輸入事件(巨集錄製) |
| WH_JOURNALPLAYBACK | 1 | 系統級 | 回放記錄的輸入事件(巨集回放) |
| WH_KEYBOARD | 2 | 系統/線程 | 攔截鍵盤訊息 (WM_KEYDOWN 等) |
| WH_GETMESSAGE | 3 | 系統/線程 | 攔截 GetMessage/PeekMessage 取得的訊息 |
| WH_CALLWNDPROC | 4 | 系統/線程 | 攔截 SendMessage 發送前的訊息 |
| WH_CBT | 5 | 系統/線程 | 攔截視窗建立/啟動/關閉/移動等系統基本操作 |
| WH_SYSMSGFILTER | 6 | 系統級 | 系統範圍內控件互動訊息 |
| WH_MOUSE | 7 | 系統/線程 | 攔截滑鼠訊息 |
| WH_HARDWARE | 8 | 系統/線程 | 攔截非標準硬體輸入 |
| WH_DEBUG | 9 | 系統/線程 | 其他鉤子調用前調試用 |
| WH_SHELL | 10 | 系統/線程 | 攔截 Shell 相關通知(視窗開啟/關閉、任務列變化) |
| WH_FOREGROUNDIDLE | 11 | 系統/線程 | 前台線程閒置時調用 |
| WH_CALLWNDPROCRET | 12 | 系統/線程 | 攔截 SendMessage 返回後的訊息 |
HInstance。EXE 直接傳遞回調地址會失敗(返回 0,GetLastError = ERROR_INVALID_PARAMETER)。HInstance 或 0(當前模組)。目標線程必須有訊息佇列(GUI 線程)。GetCurrentThreadId(當前)、GetWindowThreadProcessId(指定視窗)。unit MainUnit;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1, Button2: TButton;
procedure Button1Click(Sender: TObject); // 安裝
procedure Button2Click(Sender: TObject); // 卸載
procedure FormDestroy(Sender: TObject);
private
hHook: HHOOK;
function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
end;
var Form1: TForm1;
implementation {$R *.dfm}
function TForm1.KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
if nCode = HC_ACTION then
begin
// wParam: 虛擬鍵碼, lParam: 掃描碼/狀態位
OutputDebugString(PChar(Format('Key: %d, State: %x', [wParam, lParam])));
end;
Result := CallNextHookEx(hHook, nCode, wParam, lParam); // 必須傳遞
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if hHook = 0 then
hHook := SetWindowsHookEx(WH_KEYBOARD, @KeyboardProc, HInstance, GetCurrentThreadId);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if hHook <> 0 then
begin
UnhookWindowsHookEx(hHook);
hHook := 0;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if hHook <> 0 then UnhookWindowsHookEx(hHook);
end;
end.
{ HookDll.dpr }
library HookDll;
uses Windows, SysUtils;
var hHook: HHOOK = 0;
function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
if nCode = HC_ACTION then
// 記錄或處理全域鍵盤事件
Result := CallNextHookEx(hHook, nCode, wParam, lParam);
end;
function InstallHook: Boolean; stdcall;
begin
hHook := SetWindowsHookEx(WH_KEYBOARD, @KeyboardProc, HInstance, 0); // 系統級
Result := hHook <> 0;
end;
function UninstallHook: Boolean; stdcall;
begin
if hHook <> 0 then
begin
Result := UnhookWindowsHookEx(hHook);
if Result then hHook := 0;
end else Result := True;
end;
exports InstallHook, UninstallHook;
begin
end.
WH_KEYBOARD_LL、WH_MOUSE_LL 不需 DLL,回調在安裝進程內執行,但仍需注意效能。GetLastError,常見錯誤:1428 (ERROR_HOOK_NEEDS_HMOD)、1429 (ERROR_GLOBAL_ONLY_HOOK)。UnhookWindowsHookEx 執行。