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