鉤子函數雖然不多, 但其參數復雜, 應該從參數入手才能深入進去.
UnhookWindowsHookEx 只需要 SetWindowsHookEx 返回的鉤子句柄作參數, 這個簡單;
先看看 SetWindowsHookEx 的聲明:
SetWindowsHookEx(
idHook: Integer; {鉤子類型}
lpfn: TFNHookProc; {函數指針}
hmod: HINST; {包含鉤子函數的模塊(EXE、DLL)的句柄}
dwThreadId: DWORD {關聯的線程}
): HHOOK;
第一個參數非常麻煩, 從後面說:
參數四 dwThreadId : 在設置全局鉤子時這個參數一般是 0, 表示關聯所有線程; 本例是線程級的鉤子, 所以是
GetCurrentThreadId.
參數三 hmod: 是模塊實例的句柄, 在 EXE 和 DLL 中都可以用 HInstance 得到當前實例的句柄; 直接用 API 也可以:
GetModuleHandle(nil).
參數二 lpfn: 是鉤子函數的指針, 用 @ 和 Addr 函數都可以得到函數指針; 這裡的關鍵是那個鉤子函數:
首先不同的鉤子類型對應著不同的鉤子函數結構, Win32 共有 14 種鉤子類型, 這是 詳細注釋;
本例用的是鍵盤鉤子, 鍵盤鉤子的回調函數的參數結構在 這裡, 我們定義的函數名無所謂, 參數必須按照Windows的規定來.
還有, 這個回調函數的調用慣例必須是: stdcall; 我們在上例中是先在接口區聲明, 如果不要聲明直接實現, 也不能忘了這個 stdcall.
根據以上說明, 做如下修改:
SetWindowsHookEx 的參數有變通;
並且取消了鉤子函數在接口區的聲明, 是直接實現的;
取消了攔截條件, 現在只要是鍵盤消息全都攔截.
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;
var
Form1: TForm1;
implementation
{$R *.DFM}
var
hook: HHOOK; {定義一個鉤子句柄}
{現在這個鉤子函數沒有在接口區聲明, 這裡必須指定參數調用方式: stdcall}
function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
Beep;
Result := CallNextHookEx(hook, nCode, wParam, lParam);
end;
{設置鍵盤鉤子}
procedure TForm1.FormCreate(Sender: TObject);
begin
hook := SetWindowsHookEx(WH_KEYBOARD, Addr(KeyHook), HInstance, GetCurrentThreadId);
end;
{釋放鍵盤鉤子}
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnhookWindowsHookEx(hook);
end;
end.
鉤子函數為什麼非得使用 stdcall 調用機制? 因為鉤子函數不是被應用程序調用, 而是被系統調用的.