Delphi SetWindowsHookEx创建一个本地钩子。如何使其全球化?

Delphi SetWindowsHookEx创建一个本地钩子。如何使其全球化?,delphi,winapi,hook,setwindowshookex,Delphi,Winapi,Hook,Setwindowshookex,在delphixe应用程序中,我试图设置一个全局钩子来监视焦点的变化。钩子是在dll中创建的: focusHook := SetWindowsHookEx( WH_CBT, @FocusHookProc, HInstance, 0 ); // dwThreadId (the last argument) set to 0 should create a global hook 在同一个dll中,我有一个钩子过程,它将消息发布到主机应用程序窗口: function FocusHookProc(

在delphixe应用程序中,我试图设置一个全局钩子来监视焦点的变化。钩子是在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;
函数FocusHookProc(代码:整数;wParam:wParam;lParam:lParam):LResult;stdcall;
开始
如果(代码<0),则
开始
结果:=CallNextHookEx(focusHook、code、wParam、lParam);
出口
结束;
结果:=0;
如果(代码=HCBT_SETFOCUS),则
开始
如果(hostHWND无效\u句柄\u值),则
PostMessage(hostHWND、cFOCUSMSGID、wParam、lParam);
结束;
结束;
这是可行的,但主机只接收应用程序本身内焦点更改的通知。主窗体上有一个备忘录和几个t按钮,在它们之间切换焦点会产生预期的消息。但是,不会报告应用程序本身之外的任何焦点更改

我想这与DLL的多个实例被注入其他进程有关。有一个类似的问题得到了公认的回答,但它是针对C的,我不太明白如何在Delphi dll中做到这一点(例如,设置共享内存的pragma语句)


(这主要是一个概念证明,但我仍然希望它能够正常工作。我需要知道在我的应用程序通过单击、alt+tab、激活热键等方式被激活之前,哪个窗口处于活动状态。问题是,如果使用鼠标或alt+tab,GetForegroundWindow总是返回我自己应用程序的窗口句柄,无论我放得多早,这样一个因此,钩子似乎是唯一可行的解决方案,尽管我并不喜欢这个想法。)

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

尝试使用WinEvents而不是CBT钩子:使用WINEVENT\u OBJECT\u FOCUS作为最小和最大事件,使用WINEVENT\u OUTOFPROC标志,0作为idThread和idProcess。这将为您提供一个可以侦听f的钩子ocus事件来自同一桌面上的任何进程,不需要单独的DLL,它将在32位和64位应用程序中工作

有两个警告:一个是事件不是瞬时的;有一个轻微的延迟,因为它们本质上是发布到您的进程中的(这就是避免需要DLL的out-of-proc选项的工作方式),但是它们可能足够快,可以供您使用。(如果您在DLL钩子中使用PostMessage,无论如何都会遇到同样的问题!)

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


或者,如果您侦听事件\系统\前台事件而不是事件\对象\焦点(),那么您似乎应该只获取顶级窗口前台事件,这听起来像您在这里实际要做的事情。

+1在每个DLL上都有自己的数据,这可能是这里的问题。不过,在这种特定情况下,OP可能不需要使用共享内存:对于上面的代码,一个简单的修复方法是为目标窗口提供一个特定的类e、 另外,钩子的另一个限制是:32位钩子只能钩住32位代码;同样是64位钩子;因此钩住32位和64位进程可能会有问题。