【问题标题】:SetWindowsHookEx creates a local hook. How to make it global?SetWindowsHookEx 创建一个本地挂钩。如何让它全球化?
【发布时间】:2012-03-21 22:43:05
【问题描述】:

在 Delphi XE 应用程序中,我试图设置一个全局挂钩来监视焦点更改。钩子是在 dll 中创建的:

focusHook := SetWindowsHookEx( WH_CBT, @FocusHookProc, HInstance, 0 );
// dwThreadId (the last argument) set to 0 should create a global hook

在同一个 dll 中,我有将消息发布到主机应用程序窗口的钩子程序:

function FocusHookProc( code : integer; wParam: WPARAM; lParam: LPARAM ) : LResult; stdcall;
begin
  if ( code < 0 ) then
  begin
    result := CallNextHookEx( focusHook, code, wParam, lParam );
    exit;
  end;

  result := 0;

  if ( code = HCBT_SETFOCUS ) then
  begin
    if ( hostHWND <> INVALID_HANDLE_VALUE ) then
      PostMessage( hostHWND, cFOCUSMSGID, wParam, lParam );
  end;
end;

这可行,但主机只接收有关应用程序本身焦点更改的通知。主窗体上有一个备忘录和几个 TButton,在它们之间切换焦点会产生预期的消息。但是,从不报告应用程序本身之外的任何焦点更改。

我想这与将 DLL 的多个实例注入其他进程有关。有一个类似的问题,已接受回复here,但它是针对 C 的,我不太明白如何在 Delphi dll 中做同样的事情(例如设置共享内存的 pragma 语句)。

(这主要是一个概念证明,但我仍然想让它工作。我需要知道在我的应用程序通过点击激活之前哪个窗口处于活动状态,alt+tab,激活热键等问题是,如果使用鼠标或 alt+tab,GetForegroundWindow 总是返回我自己的应用程序的窗口句柄,不管我多早放,比如通过钩子应用程序的主消息队列。所以钩子看起来像唯一可行的解​​决方案,虽然我不太喜欢这个想法。)

【问题讨论】:

    标签: delphi winapi hook setwindowshookex


    【解决方案1】:

    由于 DLL 被注入到另一个进程中,您将不会为除您正在调试的进程之外的任何东西设置任何断点。此外,其他进程中的每个 DLL 实例也会获得自己的全局/静态数据。如果 hostHWND 是全局的,那么它在另一个进程中的值与在这个进程中的值不同。事实上,它甚至不会被初始化。您需要使用共享内存块在进程之间共享值。可能需要使用共享互斥锁和其他同步对象来确保任何共享内存写入受到保护。最后,如果您使用的是 Windows Vista+,则只有具有相同访问级别及以下的进程才会注入 DLL。 IOW,如果您以登录用户身份运行进程,则只有以登录用户身份运行的进程才会注入该 DLL。

    【讨论】:

    • +1 在每个 DLL 上都有自己的数据可能是这里的问题。但是,在这种特定情况下,OP 可能不需要使用共享内存:对上面代码的简单修复是给目标窗口一个特定的类名,并让钩子代码使用 FindWindow 找到该目标。另外,钩子的另一个限制是:32 位钩子只能钩入 32 位代码;同样使用 64 位;所以挂钩 32 位和 64 位进程可能会有问题。
    【解决方案2】:

    尝试使用 WinEvents 而不是 CBT 挂钩:SetWinEventHook 寻找 EVENT_OBJECT_FOCUS 作为最小和最大事件,带有 WINEVENT_OUTOFPROC 标志,idThread 和 idProcess 为 0。这将为您提供一个挂钩,该挂钩可以监听来自同一桌面中任何进程的焦点事件,而无需单独的 DLL,并且它适用于 32 位和 64 位应用程序。

    有几点需要注意:一个是事件不是即时的;由于它们本质上是发布到您的进程中的(这就是避免需要 DLL 的进程外选项的工作方式),因此存在轻微的延迟,但它们可能足够快以供您使用。 (无论如何,如果您在 DLL 挂钩中使用 PostMessage,您也会遇到同样的问题!)

    此外,您将获得比实际 HWND 焦点更改更多的事件:各种控件发送这些焦点更改事件以发出 internal 焦点更改的信号 - 例如,焦点在列表框中的项目之间移动。您可以通过仅在回调中过滤 idObject=OBJID_WINDOW 和 idChild=0 的那些来过滤掉这些。

    或者,如果您侦听 EVENT_SYSTEM_FOREGROUND 事件而不是 EVENT_OBJECT_FOCUS (see MSDN for the full list of events),那么您似乎应该只获得顶级窗口前台事件,这听起来就像您在此处实际需要的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 1970-01-01
      • 2018-07-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多