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

Delphi 鉤子開發實戰 (3):建立全域滑鼠鉤子 DLL 完整流程與動態載入技巧

     編輯:Delphi

全域鉤子**必須**置於 DLL 中,因為各 EXE 為獨立進程,唯有 DLL 為系統共享模組才能注入所有進程。本文完整示範從建立 DLL 專案、實作鉤子函數、編譯輸出,到 EXE 靜態/動態兩種載入調用方式的全流程。

為什麼全域鉤子非 DLL 不可?

  • EXE = 獨立進程空間,記憶體隔離。
  • DLL = 系統共享模組,可映射進所有進程位址空間。
  • SetWindowsHookEx 參數 hMod (模組句柄) 與 dwThreadId=0 (全域) 結合,系統會將 DLL 注入每個符合條件的進程執行回調。

步驟一:建立 DLL 專案 (MyHook.dpr)

library MyHook;

uses
  SysUtils, Windows, Messages, Classes;

{$R *.res}

var
  hook: HHOOK;

{ 滑鼠鉤子回調:僅攔截左鍵按下發出蜂鳴 }
function MouseHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  if wParam = WM_LBUTTONDOWN then
    MessageBeep(0);  // 系統預設提示音
  Result := CallNextHookEx(hook, nCode, wParam, lParam);
end;

{ 安裝全域滑鼠鉤子 }
function SetHook: Boolean; stdcall;
begin
  { WH_MOUSE=7 全域滑鼠鉤子;第4參數 0 表示全域 }
  hook := SetWindowsHookEx(WH_MOUSE, @MouseHook, HInstance, 0);
  Result := hook <> 0;
end;

{ 移除鉤子 }
function DelHook: Boolean; stdcall;
begin
  Result := UnhookWindowsHookEx(hook);
end;

exports
  SetHook name 'SetHook',
  DelHook name 'DelHook',
  MouseHook name 'MouseHook';

begin
end.

編譯:Ctrl+F9 生成 MyHook.dll 至專案目錄。

步驟二:EXE 靜態載入調用 (推薦)

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  end;

{ DLL 函數聲明:stdcall 必須一致 }
function SetHook: Boolean; stdcall;
function DelHook: Boolean; stdcall;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ 靜態連結:external 指定 DLL 與匯出名稱 }
function SetHook; external 'MyHook.dll' name 'SetHook';
function DelHook; external 'MyHook.dll' name 'DelHook';

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetHook;  // 安裝鉤子
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DelHook;  // 移除鉤子
end;

end.

步驟三:EXE 動態載入調用 (LoadLibrary/GetProcAddress)

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TDLLFun = function: Boolean; stdcall;  // 統一函數原型

var
  h: THandle;           // DLL 模組句柄
  SetHook, DelHook: TDLLFun;  // 函數指標變數

procedure TForm1.Button1Click(Sender: TObject);
begin
  h := LoadLibrary('MyHook.dll');  // 載入 DLL
  if h <> 0 then
  begin
    @SetHook := GetProcAddress(h, 'SetHook');  // 取得函數位址
    @DelHook := GetProcAddress(h, 'DelHook');
    SetHook;  // 呼叫
  end
  else
    ShowMessage('DLL 載入失敗');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DelHook;
  FreeLibrary(h);  // 釋放 DLL
end;

end.

測試驗證

點擊按鈕 1 安裝鉤子 → 在螢幕任意位置點擊滑鼠左鍵 → 聽到「嗶」聲 → 點擊按鈕 2 移除鉤子 → 左鍵無聲。

關鍵技術細節對照

項目靜態載入動態載入
程式碼複雜度簡單 (聲明即用)需手動 Load/Free/GetProc
啟動依賴DLL 必須存在否則啟動失敗可選擇性載入,DLL 缺失不影響主程式
版本熱插拔不支援支援 (FreeLibrary 後換新 DLL 再 Load)
除錯便利性IDE 可直接進入 DLL 斷點需附加進程除錯

常見陷阱與解決

  1. DLL 找不到:確保 MyHook.dll 複製到 EXE 同目錄,或系統路徑 (System32/SysWOW64)。
  2. 32/64 位元不匹配:32 位元 EXE 必須載入 32 位元 DLL (編譯目標平台一致)。
  3. 呼叫慣例錯誤:DLL 匯出與 EXE 聲明必須同為 stdcall。
  4. 全域鉤子權限:Vista+ UPI 隔離可能阻擋跨完整性層級注入,需以管理員權限執行或簽名 DLL。
  5. 封裝到包 (BPL):不可行。BPL 仍屬進程內模組,無法跨進程注入,全域鉤子**必須**是獨立 DLL。

進階:鉤子回調參數結構

滑鼠鉤子 (WH_MOUSE) 回調的 lParam 指向 MOUSEHOOKSTRUCT:

type
  PMouseHookStruct = ^TMouseHookStruct;
  TMouseHookStruct = record
    pt: TPoint;      // 螢幕座標
    hwnd: HWND;      // 目標視窗句柄
    wHitTestCode: UINT;  // 擊測代碼 (HTCLIENT, HTCAPTION...)
    dwExtraInfo: ULONG_PTR;
  end;

低層級鉤子 (WH_MOUSE_LL) 則指向 MSLLHOOKSTRUCT,包含時間戳記與注入旗標。

完成此流程即掌握 Delphi 全域鉤子開發的核心骨架,後續擴展鍵盤記錄、熱鍵、滑鼠手勢識別皆建立於此基礎之上。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved