当前位置 : 主页 > 编程语言 > delphi >

delphi – 如何使用WH_CBT挂钩和WndProc捕获发送到窗口的所有消息?

来源:互联网 收集:自由互联 发布时间:2021-06-23
我正在构建一个dll内部的日志系统,其主要任务是捕获目标应用程序的一些特定 Windows消息并执行一些任务.但遗憾的是,WndProc方法(WM_GETMINMAXINFO)只捕获了一条消息.我错过了什么?为什么不
我正在构建一个dll内部的日志系统,其主要任务是捕获目标应用程序的一些特定 Windows消息并执行一些任务.但遗憾的是,WndProc方法(WM_GETMINMAXINFO)只捕获了一条消息.我错过了什么?为什么不将所有Windows消息写入日志?

这是解决问题的最小示例代码

DLL

library LogDll;

uses
  Winapi.Windows,
  Winapi.Messages,
  System.IOUtils,
  System.SysUtils,
  System.Classes;

{$R *.res}

type
  TLogInspector = class
  private
    WndHandle: THandle;
    ProcAddrInst: Pointer;
    OrgWndProc: Pointer;
  protected
    function CallOrgWndProc(Message: TMessage): LRESULT;
    procedure WndProc(var Message: TMessage); virtual;
  public
    constructor Create(AHandle: THandle); virtual;
  end;

var
  MainHook: HHook;
  Log : TLogInspector;

function TLogInspector.CallOrgWndProc(Message: TMessage): LRESULT;
begin
  Result := CallWindowProc(OrgWndProc, WndHandle, Message.Msg, Message.wParam,  Message.lParam);
end;

constructor TLogInspector.Create(AHandle: THandle);
begin
  OrgWndProc := Pointer(GetWindowLongPtr(AHandle, GWL_WNDPROC));
  ProcAddrInst := MakeObjectInstance(WndProc);
  WndHandle := AHandle;
  SetWindowLongPtr(WndHandle, GWL_WNDPROC, LONG_PTR(ProcAddrInst));
end;

procedure TLogInspector.WndProc(var Message: TMessage);
begin
  //log the current message
  TFile.AppendAllText('C:\Delphi\log.txt', 'WndProc '+IntToStr(Message.Msg)+sLineBreak);
  //call the org WndProc 
  Message.Result := CallOrgWndProc(Message);
end;


function HookCallBack(nCode: Integer;  _WPARAM: WPARAM; _LPARAM: LPARAM): LRESULT;  stdcall;
var
  lpClassName : array [0 .. 256] of Char;
begin
  if nCode = HCBT_CREATEWND then
    begin
      GetClassName(_WPARAM, lpClassName, 256);
      if lpClassName = 'TForm1' then
        Log:=  TLogInspector.Create(_WPARAM);
    end;

  Result := CallNextHookEx(MainHook, nCode, _WPARAM, _LPARAM);
end;


procedure InitLog; stdcall;
begin
  MainHook := SetWindowsHookEx(WH_CBT, @HookCallBack, 0, GetCurrentThreadId);
end;

procedure DoneLog; stdcall;
begin
  UnhookWindowsHookEx(MainHook);
end;

exports
   InitLog, DoneLog;

begin
end.

应用

type
  TForm1 = class(TForm)
    Button1: TButton;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

  procedure InitLog; stdcall;  external 'LogDll.dll' name 'InitLog';
  procedure DoneLog; stdcall;  external 'LogDll.dll' name 'DoneLog';

implementation

{$R *.dfm}



initialization
 InitLog;
finalization
 DoneLog;
end.
您的测试用例过早地替换了窗口过程.操作系统’在将第一个消息传递到实际窗口的窗口过程之前通过窗口创建通知挂钩导致在VCL最终设置窗体的窗口过程之前替换窗口过程.以下是关于如何用VCL替换窗口过程的关键事件的摘要:

>在项目中,VCL将表单的初始窗口过程设置为’controls’中的InitWndProc.
>在项目中,VCL调用CreateWindowHandle,后者又调用CreateWindowEx.
>在dll中,操作系统会通知您的钩子即将创建一个窗口,并且您将窗口过程子类化.
>在dll中,使用第一条消息(WM_GETMINMAXINFO)调用替换的窗口过程.
>在项目中,InitWndProc传递相同的消息,其中窗口过程被替换为窗体的MainWndProc方法.
>在dll中,永远不会再次调用重新替换的窗口过程.

在SetWindowLongPtr上的dll中设置一个断点,在’controls.pas’的InitWndProc中的SetWindowLong上设置一个断点,以查看它的运行情况.

 使用WH_CALLWNDPROC挂钩,您可能不需要对窗口进行子类化以便能够记录发送给它的消息.例如.:

function HookCallBack(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  lpClassName : array [0 .. 256] of Char;
begin
  Result := CallNextHookEx(MainHook, nCode, wParam, lParam);
  if nCode >= 0 then begin
    GetClassName(PCWPStruct(lParam).hwnd, lpClassName, 256);
    if lpClassName = 'TForm1' then
      TFile.AppendAllText('C:\Delphi\log.txt', 'WndProc ' +
          IntToStr(PCWPStruct(lParam).message) + sLineBreak);
  end;
end;

procedure InitLog; stdcall;
begin
  MainHook := SetWindowsHookEx(WH_CALLWNDPROC, @HookCallBack, 0, GetCurrentThreadId);
end;

但是,如果你必须,你可以做到f.i.在WM_NCCREATE中.

网友评论