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

Delphi 鉤子開發實戰 (5):DLL 鉤子資料回傳與 Timer 輪詢機制

     編輯:Delphi

鉤子開發的核心難點在於 DLL 如何將攔截資料傳回主程式。本文示範最基礎的方案:DLL 內部緩衝字串,匯出 GetInfo 函數供 EXE 定期用 Timer 輪詢讀取。雖非最佳效能,但邏輯清晰適合入門理解。

資料流向架構

  1. DLL 安裝全域滑鼠鉤子 (WH_MOUSE_LL=14)
  2. 鉤子回調將事件格式化存入全域 info: string
  3. EXE 按鈕啟動鉤子並啟用 Timer (100ms)
  4. Timer 週期呼叫 GetInfo 取得最新資料顯示

DLL 完整原始碼 (MyHook.dpr)

library MyHook;

uses
  SysUtils, Windows, Messages, Classes;

{$R *.res}

var
  hook: HHOOK;
  info: string;  // 全域緩衝區

{ 匯出函數:供 EXE 讀取最新資訊 }
function GetInfo: PChar; stdcall;
begin
  Result := PChar(info);
end;

{ 滑鼠鉤子回調 }
function MouseHook(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  case wParam of
    WM_MOUSEMOVE:  info := '滑鼠位置';
    WM_LBUTTONDOWN: info := '按下';
    WM_LBUTTONUP:   info := '放開';
  end;
  info := Format('%s: %d,%d', [info, PMouseHookStruct(lParam)^.pt.X, PMouseHookStruct(lParam)^.pt.Y]);
  { 資訊已寫入 info,等待 EXE 透過 GetInfo 取走 }
  
  Result := CallNextHookEx(hook, nCode, wParam, lParam);
end;

{ 安裝鉤子 }
function SetHook: Boolean; stdcall;
const
  WH_MOUSE_LL = 14;  // Delphi 缺少常數
begin
  hook := SetWindowsHookEx(WH_MOUSE_LL, @MouseHook, HInstance, 0);  // 0 = 全域
  Result := hook <> 0;
end;

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

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

begin
end.

EXE 測試程式完整原始碼

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

function SetHook: Boolean; stdcall;
function DelHook: Boolean; stdcall;
function GetInfo: PChar; stdcall;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function SetHook; external 'MyHook.dll';
function DelHook; external 'MyHook.dll';
function GetInfo; external 'MyHook.dll';

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetHook;
  Timer1.Enabled := True;  // 開始輪詢
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Timer1.Enabled := False;
  DelHook;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Caption := '安裝鉤子';
  Button2.Caption := '移除鉤子';
  Timer1.Interval := 100;  // 100ms 輪詢一次
  Timer1.Enabled := False;
  FormStyle := fsStayOnTop;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DelHook;
end;

{ Timer 輪詢讀取 DLL 緩衝資料 }
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Caption := GetInfo;  // 直接顯示於標題
end;

end.

DFM 視窗定義

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 78
  ClientWidth = 271
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 48
    Top = 32
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 144
    Top = 32
    Width = 75
    Height = 25
    Caption = 'Button2'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
    Left = 128
    Top = 8
  end
end

執行效果

Timer 輪詢顯示滑鼠座標,約 100ms 更新一次

方案優缺點分析

優點缺點
邏輯簡單,易理解實作Timer 輪詢浪費 CPU (即使無事件)
無需處理視窗訊息機制資料延遲 = Timer Interval (此例 100ms)
適合低頻事件監控高頻事件 (滑鼠移動) 會丟失中間狀態
DLL/EXE 介面極簡單字串緩衝區非執行緒安全 (單執行緒鉤子尚可)

改進方向 (下一篇將實作)

  • 用 PostMessage + 自訂訊息 (WM_USER+1) 替代 Timer,實現事件驅動即時推送。
  • 傳遞視窗句柄給 DLL (SetHWnd),避免硬編碼目標視窗。
  • 使用 WM_COPYDATA 傳遞結構化資料而非字串指標。

常見問題排查

  1. GetInfo 回傳亂碼/空值:確認 DLL 與 EXE 字串編碼一致 (ANSI/Unicode),Delphi 2009+ 需注意 PChar 為 PWideChar。
  2. 鉤子安裝失敗:檢查 SetWindowsHookEx 回傳值,常見原因:DLL 未置於同目錄、32/64 位元不匹配、系統策略禁止全域鉤子。
  3. 滑鼠卡頓:鉤子回調執行過久 (如字串格式化、檔案寫入) 會阻塞系統訊息迴圈,務必保持極簡邏輯。

此 Timer 輪詢模式雖有缺陷,但完整展示了「DLL 緩衝資料 → EXE 定期讀取」的基本資料流模型,是理解更高階訊息推送機制的基石。

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