微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

窗口 – 我必须做什么来使我的WH_SHELL或WH_CBT挂钩过程从其他进程接收事件?

我试图使用Set WindowsHookEx设置一个WH_SHELL钩子,以获得系统范围的HSHELL_WINDOWCREATED和HSHELL_WINDOWDESTROYED事件的通知.我通过0为最终的dwThreadId参数,根据 the docs,应该“将挂接过程与在同一台桌面上与调用线程运行的所有现有线程相关联”.我也将我的DLL(HInstance in Delphi)的句柄传递给hMod参数,就像我所看到的所有例子一样.

然而,我只会收到我自己的应用程序创建的窗口的通知,通常我的测试结果导致桌面程序在火焰中关闭了我的应用程序.在你问之前,我打电话给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.
问题是你的hook DLL实际上被加载到几个不同的地址空间.任何时候,Windows检测到一些外部进程中必须由钩子处理的事件,它会将钩子DLL加载到该进程中(如果尚未加载).

但是,每个进程都有自己的地址空间.这意味着您在InitHook()中传递的回调函数指针只在您的EXE上下文中有意义(这就是为什么它适用于您的应用程序中的事件).在任何其他进程,指针是垃圾;它可能指向一个无效的内存位置,或者(更糟)到一些随机代码部分.结果可能是访问冲突或静内存损坏.

一般来说,解决方案是使用某种类型的interprocess communication(IPC)来正确地通知你的EXE.对于您的案例,最无痛的方式是发布消息并将所需的信息(事件和HWND)填充到其WParaM / LParaM中.您可以使用WM_APP n或使用RegisterWindowMessage()创建一个.确保消息已发布并且不发送,以避免任何死锁.

原文地址:https://www.jb51.cc/bash/385936.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐