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

Delphi 鉤子開發實戰 (6):DLL 鉤子雙向資料傳遞與訊息機制優化

     編輯:Delphi

延續前兩篇鉤子教學,本文解決兩個關鍵問題:(1) 如何從 EXE 向 DLL 傳遞資料 (如視窗句柄);(2) 如何避免使用 Timer 輪詢改用 Windows 訊息機制主動推送資料。完整示範低層級鍵盤/滑鼠鉤子的雙向通訊架構。

架構設計重點

  • DLL 端:匯出 SetHWnd 接收目標視窗句柄,鉤子回調中用 PostMessage 發送自訂訊息 (WM_USER+1) 推送資料。
  • EXE 端:定義 message WM_MyMessage 訊息處理函數,直接在訊息迴圈中接收資料,無需 Timer 輪詢。
  • 全域滑鼠鉤子:使用 WH_MOUSE_LL = 14 (低層級),不需 DLL 注入即可全域攔截。

DLL 完整原始碼 (MyHook.dpr)

library MyHook;

uses
  SysUtils, Windows, Messages, Classes;

{$R *.res}

const
  WM_MyMessage = WM_USER + 1;  // 自訂訊息

var
  hook: HHOOK;
  info: string;
  h: HWND;  // 目標視窗句柄

{ 供 EXE 呼叫設定目標視窗句柄 }
function SetHWnd(hwnd: HWND): Boolean; stdcall;
begin
  h := hwnd;
  Result := True;
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]);
  
  { 關鍵:用 PostMessage 非同步推送資料到目標視窗 }
  PostMessage(h, WM_MyMessage, 0, Integer(PChar(info)));
  
  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);
  Result := hook <> 0;
end;

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

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

begin
end.

EXE 測試程式完整原始碼

unit Unit1;

interface

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

const
  WM_MyMessage = WM_USER + 1;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure MyMessage(var msg: TMessage); message WM_MyMessage;  // 訊息處理
  end;

function SetHook: Boolean; stdcall;
function DelHook: Boolean; stdcall;
function SetHWnd(hwnd: HWND): Boolean; stdcall;

var
  Form1: TForm1;

implementation

{$R *.dfm}

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

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetHook;           // 安裝鉤子
  SetHWnd(Handle);   // 傳遞本視窗句柄給 DLL
end;

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

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Caption := '安裝鉤子';
  Button2.Caption := '移除鉤子';
  FormStyle := fsStayOnTop;  // 方便測試保持最上層
end;

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

{ 訊息處理函數:直接接收 DLL 推送的資料 }
procedure TForm1.MyMessage(var msg: TMessage);
begin
  Caption := PChar(msg.LParam);  // 顯示滑鼠資訊於標題列
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
end

執行效果展示

滑鼠動作即時顯示於視窗標題,無 Timer 輪詢延遲

關鍵技術優勢分析

傳統 Timer 輪詢訊息機制 (本範例)
固定間隔檢查 (如 100ms)事件驅動,即時觸發
CPU 持續佔用無事件時零負擔
資料延遲高達間隔時間微秒級延遲
需共享記憶體/全域變數訊息參數直接傳遞字串指標

實務開發注意事項

  1. 字串指標生命週期:DLL 中 info 為全域 string,PChar(info) 指標有效直到下次修改。若需傳遞多筆資料,建議用 GlobalAddAtom/GlobalGetAtomName 或共享記憶體。
  2. Unicode 相容性:Delphi 2009+ string 為 Unicode,PChar 為 PWideChar,接收端 PChar(msg.LParam) 需對應 WideChar 或用 string(msg.LParam) 自動轉換。
  3. 鉤子效能:WH_MOUSE_LL 回調在系統進程執行,避免耗時操作,否則導致全系統滑鼠延遲。
  4. 錯誤處理:SetWindowsHookEx 失敗回傳 0,需檢查 GetLastError (常見原因:DLL 未簽名、權限不足、系統關鍵進程保護)。
  5. 協助除錯:在 DLL 中用 OutputDebugString 輸出日誌,配合 DebugView 監控。

擴展應用方向

  • 鍵盤記錄/熱鍵:改用 WH_KEYBOARD_LL = 13 攔截鍵盤。
  • 跨進程資料交換:結合 WM_COPYDATA 結構化傳遞複雜資料。
  • 防注入保護:監控 SetWindowsHookEx 呼叫偵測惡意鉤子。

掌握 DLL 鉤子與訊息機制的雙向通訊模式,是開發系統級監控工具、熱鍵管理器、自動化測試框架的核心基礎。

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