Oli*_*sen 12 windows delphi hook winapi
我正在尝试使用SetWindowsHookEx设置一个WH_SHELL钩子来获得系统范围HSHELL_WINDOWCREATED和HSHELL_WINDOWDESTROYED事件的通知.我传递0作为最终dwThreadId参数,根据文档,它应该"将钩子过程与在与调用线程相同的桌面上运行的所有现有线程相关联".我也将参数传递给我的DLL(HInstance在Delphi中),hMod就像我看到的所有例子一样.
然而,我只收到由我自己的应用程序创建的窗口的通知 - 通常情况下 - 我的测试导致桌面进程在关闭我的应用程序后熄火.在你问之前,我打电话UnhookWindowsHookEx.我也经常CallNextHookEx在我的经纪人内部打电话.
我正在从一个有限的用户帐户运行我的测试应用程序,但到目前为止我还没有找到任何提示,表明这将起作用...(虽然这实际上让我感到惊讶)
AFAICT,我按书完成了一切(显然我没有,但到目前为止,我没有看到哪里).
我正在使用Delphi(2007),但我认为这不应该真的重要.
编辑:也许我之前应该提到这个:我确实下载并尝试了几个例子(尽管很遗憾没有那么多可用于Delphi - 特别是没有用于WH_SHELL或者WH_CBT).虽然它们不会像我的测试应用程序一样崩溃系统,但它们仍然不会捕获来自其他进程的事件(即使我可以使用ProcessExplorer验证它们是否可以加载到它们中).所以我的系统配置似乎有问题,或者示例错误,或者根本无法从其他进程捕获事件.任何人都可以开导我吗?
编辑2:好的,这是我的测试项目的来源.
包含钩子过程的DLL:
library HookHelper;
uses
Windows;
{$R *.res}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
var
WndHookCallback: THookCallback;
Hook: HHook;
function HookProc(ACode, AWParam, ALParam: Integer): Integer; stdcall;
begin
Result := CallNextHookEx(Hook, ACode, AWParam, ALParam);
if ACode < 0 then Exit;
try
if Assigned(WndHookCallback)
// and (ACode in [HSHELL_WINDOWCREATED, HSHELL_WINDOWDESTROYED]) then
and (ACode in [HCBT_CREATEWND, HCBT_DESTROYWND]) then
WndHookCallback(ACode, AWParam, ALParam);
except
// plop!
end;
end;
procedure InitHook(ACallback: THookCallback); register;
begin
// Hook := SetWindowsHookEx(WH_SHELL, @HookProc, HInstance, 0);
Hook := SetWindowsHookEx(WH_CBT, @HookProc, HInstance, 0);
if Hook = 0 then
begin
// ShowMessage(SysErrorMessage(GetLastError));
end
else
begin
WndHookCallback := ACallback;
end;
end;
procedure UninitHook; register;
begin
if Hook <> 0 then
UnhookWindowsHookEx(Hook);
WndHookCallback := nil;
end;
exports
InitHook,
UninitHook;
begin
end.
Run Code Online (Sandbox Code Playgroud)
使用钩子的应用程序的主要形式:
unit MainFo;
interface
uses
Windows, SysUtils, Forms, Dialogs, Classes, Controls, Buttons, StdCtrls;
type
THookTest_Fo = class(TForm)
Hook_Btn: TSpeedButton;
Output_Lbx: TListBox;
Test_Btn: TButton;
procedure Hook_BtnClick(Sender: TObject);
procedure Test_BtnClick(Sender: TObject);
public
destructor Destroy; override;
end;
var
HookTest_Fo: THookTest_Fo;
implementation
{$R *.dfm}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
procedure InitHook(const ACallback: THookCallback); register; external 'HookHelper.dll';
procedure UninitHook; register; external 'HookHelper.dll';
procedure HookCallback(ACode, AWParam, ALParam: Integer); stdcall;
begin
if Assigned(HookTest_Fo) then
case ACode of
// HSHELL_WINDOWCREATED:
HCBT_CREATEWND:
HookTest_Fo.Output_Lbx.Items.Add('created handle #' + IntToStr(AWParam));
// HSHELL_WINDOWDESTROYED:
HCBT_DESTROYWND:
HookTest_Fo.Output_Lbx.Items.Add('destroyed handle #' + IntToStr(AWParam));
else
HookTest_Fo.Output_Lbx.Items.Add(Format('code: %d, WParam: $%x, LParam: $%x', [ACode, AWParam, ALParam]));
end;
end;
procedure THookTest_Fo.Test_BtnClick(Sender: TObject);
begin
ShowMessage('Boo!');
end;
destructor THookTest_Fo.Destroy;
begin
UninitHook; // just to make sure
inherited;
end;
procedure THookTest_Fo.Hook_BtnClick(Sender: TObject);
begin
if Hook_Btn.Down then
InitHook(HookCallback)
else
UninitHook;
end;
end.
Run Code Online (Sandbox Code Playgroud)
问题是你的钩子DLL实际上被加载到几个不同的地址空间.每当Windows检测到必须由钩子处理的某个外部进程中的事件时,它就会将钩子DLL加载到该进程中(当然,如果它尚未加载).
但是,每个进程都有自己的地址空间.这意味着您在InitHook()中传递的回调函数指针仅在EXE的上下文中有意义(这就是它适用于您的应用程序中的事件的原因).在指针是垃圾的任何其他进程中; 它可能指向无效的内存位置或(更糟)进入某个随机代码段.结果可能是访问冲突或静默内存损坏.
通常,解决方案是使用某种进程间通信(IPC)来正确通知您的EXE.对您的案例来说,最无痛的方法是发布消息并将所需信息(事件和HWND)塞入其WPARAM/LPARAM中.您可以使用WM_APP + n或使用RegisterWindowMessage()创建一个.确保邮件已发布但未发送,以避免任何死锁.