我正在构建一个dll内部的日志系统,其主要任务是捕获目标应用程序的一些特定 Windows消息并执行一些任务.但遗憾的是,WndProc方法(WM_GETMINMAXINFO)只捕获了一条消息.我错过了什么?为什么不
这是解决问题的最小示例代码
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中.