然而,我只会收到我自己的应用程序创建的窗口的通知,通常我的测试结果导致桌面程序在火焰中关闭了我的应用程序.在你问之前,我打电话给UnhookWindowsHookEx.我也总是从我的处理程序中调用CallNextHookEx.
我从有限的用户帐户运行我的测试应用程序,但到目前为止,我还没有发现任何提示,表明这将扮演一个角色…(虽然这真的令我惊讶)
AFAICT,我做了一切这本书(显然我没有,我没有看到哪里).
我正在使用Delphi(2007),但是我不应该这么想.
编辑:也许我应该提到这一点:我下载并尝试了几个例子(尽管不幸的是不是很多可用于Delphi – 特别是WH_SHELL或WH_CBT).虽然它们不像我的测试应用程序那样崩溃系统,但是它们仍然不会捕获来自其他进程的事件(即使我可以使用ProcessExplorer验证它们是否被加载到它们中).所以似乎我的系统配置有问题,或者示例是错误的,或者根本无法从其他进程捕获事件.任何人都可以启发我吗?
EDIT2:好的,这是我的测试项目的源头.
该DLL包含挂钩过程:
library HookHelper; uses Windows; {$R *.res} type THookCallback = procedure(ACode,AWParam,ALParam: Integer); stdcall; var WndHookCallback: THookCallback; Hook: HHook; function HookProc(ACode,ALParam: Integer): Integer; stdcall; begin Result := CallNextHookEx(Hook,ACode,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,ALParam); except // plop! end; end; procedure InitHook(ACallback: THookCallback); register; begin // Hook := SetWindowsHookEx(WH_SHELL,@HookProc,HInstance,0); Hook := SetWindowsHookEx(WH_CBT,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.
并且使用钩子的主要形式的应用程序:
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,ALParam: Integer); stdcall; procedure InitHook(const ACallback: THookCallback); register; external 'HookHelper.dll'; procedure UninitHook; register; external 'HookHelper.dll'; procedure HookCallback(ACode,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,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.
但是,每个进程都有自己的地址空间.这意味着您在InitHook()中传递的回调函数指针只在您的EXE上下文中有意义(这就是为什么它适用于您的应用程序中的事件).在任何其他进程,指针是垃圾;它可能指向一个无效的内存位置,或者(更糟)到一些随机代码部分.结果可能是访问冲突或静默内存损坏.
一般来说,解决方案是使用某种类型的interprocess communication(IPC)来正确地通知你的EXE.对于您的案例,最无痛的方式是发布消息并将所需的信息(事件和HWND)填充到其WPARAM / LPARAM中.您可以使用WM_APP n或使用RegisterWindowMessage()创建一个.确保消息已发布并且不发送,以避免任何死锁.