| 返回值 | 語意 | 結果 |
| 0 | 「我處理完了,請繼續傳遞」 | 訊息**放行**,目標視窗會收到 |
| 非 0 (如 1) | 「我處理完了,**攔截結束**」 | 訊息**吞噬**,目標視窗**不會**收到 |
關鍵口訣:鍵盤/滑鼠鉤子返回 0 = 放行,非 0 = 攔截銷毀。
驗證範例:鍵盤鉤子攔截控制
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
hook: HHOOK;
{ 鍵盤鉤子回調:記錄按鍵碼並根據返回值決定攔截 }
function MyKeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Form1.Memo1.Lines.Add(IntToStr(wParam)); // 記錄虛擬鍵碼
Result := 0; // 修改此處測試:0=放行, 1=攔截
end;
{ 安裝線程級鍵盤鉤子 }
procedure TForm1.Button1Click(Sender: TObject);
begin
hook := SetWindowsHookEx(WH_KEYBOARD, @MyKeyHook, HInstance, GetCurrentThreadId);
Memo1.Clear;
Caption := '鉤子啟動 (返回 0: 放行)';
end;
{ 移除鉤子 }
procedure TForm1.Button2Click(Sender: TObject);
begin
UnhookWindowsHookEx(hook);
Caption := '鉤子關閉';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if hook <> 0 then UnhookWindowsHookEx(hook);
end;
end.
實測現象對照

- 無鉤子:
Memo 正常輸入 'a'。
- 鉤子返回 0:
Memo 記錄鍵碼 (65),**且**輸入框正常出現 'a' (放行)。
- 鉤子返回 1:
Memo 記錄鍵碼 (65),**但**輸入框**無任何字元** (攔截銷毀)。
隱藏細節:按鍵雙重訊息
按一次鍵產生兩個訊息:WM_KEYDOWN (按下) + WM_KEYUP (釋放),鉤子都會攔截,故 Memo 記錄兩次鍵碼。區分方法在 lParam 高位旗標 (第 31 位: 0=Down, 1=Up),下篇再詳述。
關於函數指標傳遞的小秘密
SetWindowsHookEx(WH_KEYBOARD, @MyKeyHook, ...); // 用 @ 取址
SetWindowsHookEx(WH_KEYBOARD, MyKeyHook, ...); // 直接用函數名也可!
原因:Delphi 中函數名稱本身即代表其進入點位址,@ 與 Addr 僅為語法糖,編譯器自動處理。
最佳實務建議
- 永遠呼叫 CallNextHookEx:除非明確要攔截訊息 (如熱鍵處理),否則務必傳遞鏈,維護系統穩定。
- 返回值傳遞:
Result := CallNextHookEx(...) 將下游返回值原樣回傳,符合 Windows 規範。
- 攔截慎重:全域鍵盤鉤子返回非 0 會導致該鍵全系統失效,極度影響使用者體驗,僅限極特定場景 (如密碼輸入保護、遊戲防外掛)。
- 執行緒安全:全域鉤子回調在系統進程執行,避免 VCL/UI 操作,僅做極簡邏輯 (記錄、發訊息)。
鉤子類型對照表 (節錄)