首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 鉤子開發實戰 (2):線程級鍵盤鉤子參數詳解與 stdcall 呼叫慣例必要性

     編輯:Delphi

從最簡單的線程級鍵盤鉤子切入,深入剖析 SetWindowsHookEx 四個參數含義、鍵盤鉤子回調函數參數結構、以及為什麼回調**必須**使用 stdcall 呼叫慣例。

SetWindowsHookEx 參數完整解析

SetWindowsHookEx(
  idHook: Integer;       // 鉤子類型
  lpfn: TFNHookProc;     // 回調函數指標
  hmod: HINST;           // 包含回調函數的模組句柄
  dwThreadId: DWORD      // 關聯線程 ID
): HHOOK;

鍵盤鉤子回調函數標準原型

function KeyHook(
  nCode: Integer;      // 處理代碼 (HC_ACTION=0 表示正常處理)
  wParam: WPARAM;      // 虛擬鍵碼
  lParam: LPARAM       // 鍵盤訊息詳細資訊 (掃描碼、擴展鍵旗標、轉換狀態等)
): LRESULT; stdcall;   // **強制 stdcall**

為什麼必須 stdcall? 回調函數由**系統**呼叫,非應用程式主動呼叫。Windows API 規範所有回調均為 stdcall (參數由被呼叫者清理堆疊,右至左壓棧),確保跨模組/跨語言二進位相容。

完整可執行範例:攔截字母 A 發聲

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

{ 介面區聲明:指定 stdcall }
function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  hook: HHOOK;

{ 實作鍵盤鉤子回調 }
function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
  if (wParam = 65) then  // 虛擬鍵碼 65 = 'A'
    Beep;                // 發出蜂鳴
  Result := CallNextHookEx(hook, nCode, wParam, lParam);  // 傳遞鏈
end;

{ 視窗建立時安裝線程級鍵盤鉤子 }
procedure TForm1.FormCreate(Sender: TObject);
begin
  hook := SetWindowsHookEx(WH_KEYBOARD, @KeyHook, HInstance, GetCurrentThreadId);
end;

{ 視窗銷毀時移除鉤子 }
procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnhookWindowsHookEx(hook);
end;

end.

關鍵細節補充

  1. 介面區聲明 vs 直接實作:
    • 介面區聲明 + 實作區實作:編譯器自動處理呼叫慣例。
    • 直接實作區實作:**必須手寫 stdcall**,否則預設 register 導致堆疊破壞崩潰。
  2. 參數取址:@KeyHook、Addr(KeyHook)、直接 KeyHook (函數名即位址) 效果相同。
  3. 模組句柄:線程級鉤子 hmod 可為 0 (系統自動用當前模組),但明確給 HInstance 更清晰。
  4. 攔截條件:範例僅攔截 wParam=65,實務中常結合 lParam 判斷按下/釋放、擴展鍵、Alt 鍵狀態。

lParam 位元結構 (鍵盤訊息詳細資訊)

Bit 0-15:  掃描碼
Bit 16-23: 擴展鍵旗標 (1=擴展鍵如右 Ctrl/Alt)
Bit 24:    保留
Bit 25-28: 保留
Bit 29:    內容代碼 (1=Alt 鍵按下時的字元鍵)
Bit 30:    前次鍵狀態 (1=按下前已按住)
Bit 31:    轉換狀態 (0=按下 WM_KEYDOWN, 1=釋放 WM_KEYUP)  **關鍵區分依據**

常見問題排查

參數線程級鉤子設定說明
idHookWH_KEYBOARD (2)鍵盤訊息鉤子
lpfn@KeyHook 或 Addr(KeyHook)取得函數進入點位址
hmodHInstance 或 GetModuleHandle(nil)當前模組 (EXE/DLL) 句柄
dwThreadIdGetCurrentThreadId**只關聯當前線程**,0 才是全域

下一步學習路徑

  1. 掌握 lParam 位元解析,精確區分按下/釋放、控制鍵組合。
  2. 進階至全域鉤子 (需 DLL,WH_KEYBOARD_LL=13)。
  3. 實作熱鍵系統、按鍵記錄器、遊戲防外掛偵測。

線程級鉤子雖範圍有限,但無需 DLL、除錯容易、適合單一應用程式內部訊息監控與熱鍵處理,是鉤子技術入門的最佳起點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
現象原因解決
程式啟動即崩潰回調函數缺少 stdcall聲明/實作加上 stdcall
鉤子不觸發idHook 錯誤或線程 ID 錯誤確認 WH_KEYBOARD=2, GetCurrentThreadId
系統按鍵失效返回非 0 攔截所有鍵僅目標鍵返回非 0,其餘返回 CallNextHookEx
Beep 重複兩次KeyDown + KeyUp 皆觸發檢查 lParam Bit 31 區分