我正在构建一个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中.
