我正在构建一个dll内部的日志系统,其主要任务是捕获目标应用程序的一些特定Windows消息并执行一些任务。但遗憾的是,WndProc
方法(WM_GETMINMAXINFO
)只捕获了一条消息。我错过了什么?为什么不将所有Windows消息写入日志?
这是解决问题的最小示例代码
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.
答案 0 :(得分:3)
您的测试用例过早更换窗口过程。操作系统'在将第一个消息传递到实际窗口的窗口过程之前通过窗口创建通知挂钩导致在VCL最终设置窗体的窗口过程之前替换窗口过程。以下是关于如何用VCL替换窗口过程的关键事件的摘要:
InitWndProc
。CreateWindowHandle
,然后调用CreateWindowEx
。WM_GETMINMAXINFO
)调用替换的窗口过程。InitWndProc
会传递相同的消息,其中窗口过程将替换为表单的MainWndProc
方法。在SetWindowLongPtr
上的dll中设置一个断点,并在'controls.pas'的SetWindowLong
中的InitWndProc
项目中设置一个断点,以查看它的实际效果。
使用WH_CALLWNDPROC
挂钩,您可能不需要对窗口进行子类化以便能够记录发送给它的消息。 E.g:
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
。