1. 鉤子函數(shù) :
如果要設(shè)置系統(tǒng)級(jí)鉤子, 鉤子函數(shù)必須在 DLL 中.
1. SetWindowsHookEx - 設(shè)置鉤子
2. UnhookWindowsHookEx - 卸掉鉤子
UnhookWindowsHookEx(
hhk: HHOOK {鉤子句柄}
): BOOL;
{True/False}
3. CallNextHookEx - 調(diào)用下一個(gè)鉤子
CallNextHookEx(
hhk: HHOOK;
{當(dāng)前鉤子的句柄}
nCode: Integer;
{鉤子代碼; 就是給下一個(gè)鉤子要交待的}
wParam: WPARAM;
{要傳遞的參數(shù); 由鉤子類型決定是什么參數(shù)}
lParam: LPARAM
{要傳遞的參數(shù); 由鉤子類型決定是什么參數(shù)}
): LRESULT;
{會(huì)返回下一個(gè)鉤子執(zhí)行后的返回值; 0 表示失敗}
鉤子回調(diào)函數(shù)之 MsgFilterProc 鉤子回調(diào)函數(shù)之 JournalRecordProc
鉤子回調(diào)函數(shù)之 JournalPlaybackProc 鉤子回調(diào)函數(shù)之 GetMsgProc
鉤子回調(diào)函數(shù)之 CBTProc
目前對(duì)鉤子的理解:
譬如我們用鼠標(biāo)在某個(gè)窗口上雙擊了一次, 或者給某個(gè)窗口輸入了一個(gè)字母 A;
首先發(fā)現(xiàn)這些事件的不是窗口, 而是系統(tǒng)!
然后系統(tǒng)告訴窗口: 喂! 你讓人點(diǎn)了, 并且是連續(xù)點(diǎn)了兩鼠標(biāo), 你準(zhǔn)備怎么辦?
或者是系統(tǒng)告訴窗口: 喂! 有人向你家里扔磚頭了, 不信你看看, 那塊磚頭是 A.
這時(shí)窗口的對(duì)有些事件會(huì)忽略、對(duì)有些事件會(huì)做出反應(yīng):
譬如, 可能對(duì)鼠標(biāo)單擊事件忽略, 窗口想: 你單擊我不要緊, 累死你我不負(fù)責(zé);
但一旦誰要雙擊我, 我會(huì)馬上行動(dòng), 給你點(diǎn)顏色瞧瞧!
這里窗口準(zhǔn)備要采取的行動(dòng), 就是我們提前寫好的事件.
用 Windows 的話說, 窗口的事件就是系統(tǒng)發(fā)送給窗口的消息; 窗口要采取的行動(dòng)(事件代碼)就是窗口的回調(diào)函數(shù).
但是! 往往隔墻有耳. 系統(tǒng)要通知給窗口的"話"(消息), 可能會(huì)被另一個(gè)家伙(譬如是一個(gè)賊)提前聽到!
有可能這個(gè)賊就是專門在這等情報(bào)的, 賊知道后, 往往在窗口知道以前就采取了行動(dòng)!
并且這個(gè)賊對(duì)不同的消息會(huì)采取不同的行動(dòng)方案, 它的行動(dòng)方案一般也是早就準(zhǔn)備好的;
當(dāng)然這個(gè)賊也不是對(duì)什么消息都感興趣, 對(duì)不感興趣的消息也就無須制定相應(yīng)的行動(dòng)方案.
總結(jié): 這個(gè)"賊"就是我們要設(shè)置的鉤子; "賊"的"行動(dòng)方案"就是鉤子函數(shù), 或者叫鉤子的回調(diào)函數(shù).
鉤子分兩種, 一種是系統(tǒng)級(jí)的全局鉤子; 一種是線程級(jí)的鉤子.
全局鉤子函數(shù)需要定義在 DLL 中, 從線程級(jí)的鉤子開始比較簡單.
其實(shí)鉤子函數(shù)就三個(gè):
設(shè)置鉤子: SetWindowsHookEx
釋放鉤子: UnhookWindowsHookEx
繼續(xù)鉤子: CallNextHookEx
在線程級(jí)的鉤子中經(jīng)常用到 GetCurrentThreadID 函數(shù)來獲取當(dāng)前線程的 ID.
下面例子中設(shè)定了一個(gè)線程級(jí)的鍵盤鉤子, 專門攔截字母 A.
- <span style="font-size:18px;">{聲明鍵盤鉤子回調(diào)函數(shù); 其參數(shù)傳遞方式要用 API 的 stdcall}
- function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
-
- var
- Form1: TForm1;
-
- implementation
-
- {$R *.DFM}
-
- var
- hook: HHOOK; {定義一個(gè)鉤子句柄}
-
- {實(shí)現(xiàn)鍵盤鉤子回調(diào)函數(shù)}
- function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT;
- begin
- if (wParam = 65) then Beep; {每攔截到字母 A 會(huì)發(fā)聲}
- Result := CallNextHookEx(hook, nCode, wParam, lParam);
- end;
-
- {設(shè)置鍵盤鉤子}
- procedure TForm1.FormCreate(Sender: TObject);
- begin
- hook := SetWindowsHookEx(WH_KEYBOARD, @KeyHook, 0, GetCurrentThreadID);
- end;
-
- {釋放鍵盤鉤子}
- procedure TForm1.FormDestroy(Sender: TObject);
- begin
- UnhookWindowsHookEx(hook);
- end;
-
- </span>
先看看 SetWindowsHookEx 的聲明:
SetWindowsHookEx(
idHook: Integer; {鉤子類型}
lpfn: TFNHookProc; {函數(shù)指針}
hmod: HINST; {包含鉤子函數(shù)的模塊(EXE、DLL)的句柄}
dwThreadId: DWORD {關(guān)聯(lián)的線程}
): HHOOK;
第一個(gè)參數(shù)非常麻煩, 從后面說:
參數(shù)四 dwThreadId : 在設(shè)置全局鉤子時(shí)這個(gè)參數(shù)一般是 0, 表示關(guān)聯(lián)所有線程; 本例是線程級(jí)的鉤子, 所以是
GetCurrentThreadId.
參數(shù)三 hmod: 是模塊實(shí)例的句柄, 在 EXE 和 DLL 中都可以用 HInstance 得到當(dāng)前實(shí)例的句柄; 直接用 API 也可以:
GetModuleHandle(nil).
參數(shù)二 lpfn: 是鉤子函數(shù)的指針, 用 @ 和 Addr 函數(shù)都可以得到函數(shù)指針; 這里的關(guān)鍵是那個(gè)鉤子函數(shù):
首先不同的鉤子類型對(duì)應(yīng)著不同的鉤子函數(shù)結(jié)構(gòu), Win32 共有 14 種鉤子類型, 這是 詳細(xì)注釋;
本例用的是鍵盤鉤子, 鍵盤鉤子的回調(diào)函數(shù)的參數(shù)結(jié)構(gòu)在 這里, 我們定義的函數(shù)名無所謂, 參數(shù)必須按照Windows的規(guī)定來.
還有, 這個(gè)回調(diào)函數(shù)的調(diào)用慣例必須是: stdcall; 我們?cè)谏侠惺窍仍诮涌趨^(qū)聲明, 如果不要聲明直接實(shí)現(xiàn), 也不能忘了這個(gè) stdcall.
根據(jù)以上說明, 做如下修改:
SetWindowsHookEx 的參數(shù)有變通;
并且取消了鉤子函數(shù)在接口區(qū)的聲明, 是直接實(shí)現(xiàn)的;
取消了攔截條件, 現(xiàn)在只要是鍵盤消息全都攔截.
|