电竞比分网-中国电竞赛事及体育赛事平台

分享

Delphi 鉤子(1)

 aaie_ 2016-07-22

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.

 

[delphi] view plain copy
  1. <span style="font-size:18px;">{聲明鍵盤鉤子回調函數; 其參數傳遞方式要用 API 的 stdcall}  
  2. function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;  
  3.   
  4. var  
  5.   Form1: TForm1;  
  6.   
  7. implementation  
  8.   
  9. {$R *.DFM}  
  10.   
  11. var  
  12.   hook: HHOOK; {定義一個鉤子句柄}  
  13.   
  14. {實現鍵盤鉤子回調函數}  
  15. function KeyHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT;  
  16. begin  
  17.   if (wParam = 65then Beep; {每攔截到字母 A 會發(fā)聲}  
  18.   Result := CallNextHookEx(hook, nCode, wParam, lParam);  
  19. end;  
  20.   
  21. {設置鍵盤鉤子}  
  22. procedure TForm1.FormCreate(Sender: TObject);  
  23. begin  
  24.   hook := SetWindowsHookEx(WH_KEYBOARD, @KeyHook, 0, GetCurrentThreadID);  
  25. end;  
  26.   
  27. {釋放鍵盤鉤子}  
  28. procedure TForm1.FormDestroy(Sender: TObject);  
  29. begin  
  30.   UnhookWindowsHookEx(hook);  
  31. end;  
  32.   
  33. </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ū)的聲明, 是直接實現的;
取消了攔截條件, 現在只要是鍵盤消息全都攔截.

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多