SetWindowsHookEx不能在另一个进程线程ID上与WH_CBT一起使用但可以在自己的线程ID上使用 定义回调的DLLinject.dll:用于设置CBT挂钩DLLwrapper.dll的DLL:

问题描述

几天来,我一直在绞尽脑汁,可以使用一些帮助!将SetwindowsHookEx钩到WPF应用程序的线程ID时,我能够使用它检测到CBT钩子,但是当该窗口成为前台应用程序时,我无法使其钩接到另一个进程窗口的Thread ID。

图片1:表明我可以使用CBT钩子来检测主应用程序的线程ID最大化的窗口

Image 1: Shows that I CAN get the CBT hook for detecting window maximize on the main app's Thread ID

图像2:表明在侦听另一个应用程序的线程ID时我无法获得CBT钩子,它将使该应用程序崩溃!

Image 2: Shows that I CANNOT get the CBT hook when listening to another app's Thread ID,and it will crash the app!

我想避免发送ThreadId = 0并使其成为完整的全局钩子,因为我知道我只想听前台应用程序,而不是桌面上的所有应用程序。我希望能够在当前具有前景焦点的任何窗口发生之前,先监听一些窗口事件(根据我的理解,WH_CBT会这样做)。

同样,以下代码在当前WPF应用成为前台应用时起作用,但在另一个应用的窗口成为前台(例如记事本,Internet Explorer,文件浏览器,Chrome等)时失败。

完整代码(link to github zip file)

以下是一些代码片段,以显示我的工作:

定义回调的DLL(inject.dll):

inject.h:摘要
typedef void(__stdcall* MYFUNCPTR)(int code,WParaM wparam,LParaM lparam);
extern "C" __declspec(dllexport) void Init(MYFUNCPTR funcPtr);
extern "C" __declspec(dllexport) LRESULT CALLBACK CbtProcCallback(int code,LParaM lparam);
WParaM wparam,LParaM lparam);
MYFUNCPTR _handler = 0;
inject.cpp:代码
void Init(MYFUNCPTR funcPtr)
{
    _handler = funcPtr;
}
LRESULT CALLBACK CbtProcCallback(int code,LParaM lparam)
{
    if (code >= 0)
    {
        // Only send the code if you are about to MAXIMIZE
        if (code == HCBT_MINMAX)
        {
            if (lparam == SW_MAXIMIZE)
            {
                _handler(0,wparam,lparam);
            }
        }
    }

    return CallNextHookEx(NULL,code,lparam);
}

用于设置CBT挂钩(DLLwrapper.dll)的DLL:

dllwrapper.cpp:代码
    // Load library in which we'll be hooking our functions.
    HMODULE dll = LoadLibrary(L"inject.dll");
    if (dll == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage,"ERR-LoadLibrary Failed! ErrorCode=%d",GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("LoadLibrary passed!");

    // Get the address of the function inside the DLL.
    MYPROC iaddr = (MYPROC)GetProcAddress(dll,"Init");
    if (iaddr == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage,"ERR-GetProcAddress for Init Failed! ErrorCode=%d",GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("GetProcAddress for Init passed!");
    iaddr(OnInjectionCallback);

    // Get the address of the function inside the DLL.
    HOOKPROC cbtProcAddress = (HOOKPROC)GetProcAddress(dll,"CbtProcCallback");
    if (cbtProcAddress == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage,"ERR-GetProcAddress for CbtProcCallback Failed! ErrorCode=%d",GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("GetProcAddress for CbtProcCallback passed!");

    // Hook the function
    cbtProcHook = SetwindowsHookEx(WH_CBT,cbtProcAddress,dll,_threadId);
    if (cbtProcHook == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage,"ERR-SetwindowsHookEx cbtProcAddress Failed! ErrorCode=%d",GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("SetwindowsHookEx for cbtProcAddress passed!");
片段导出到C#
typedef void(__stdcall* CodeCallback)(int code,LParaM lparam);
typedef void(__stdcall* MessageCallback)(const char* message);
#ifdef __cplusplus
extern "C" {  // only need to export C interface if
              // used by C++ source code
#endif

    __declspec(dllexport) bool StartHooks(unsigned int threadId,MessageCallback messageCallback,CodeCallback codeCallback);
    __declspec(dllexport) void StopHooks();

#ifdef __cplusplus
}
#endif
NativeMethods.cs:C#dll的片段导入到WPF应用程序
public delegate void MessageCallback(string message);
public delegate void CodeCallback(int code,IntPtr wParam,IntPtr lParam);
[DllImport("dllwrapper.dll")]
public extern static bool StartHooks(uint threadId,MessageCallback messageHandler,CodeCallback codeHandler);
[DllImport("dllwrapper.dll")]
public extern static void StopHooks();

从WPF应用程序窗口中的消息中可以看到,挂钩正在传递并且未返回任何Win32错误,但是当另一个窗口具有焦点(即使使用调试器)时,它只是不执行回调。

任何帮助将不胜感激!

开发环境:

  • Windows 10 1909
  • VS2019 16.7.4
  • C#.NET Framework 4.7.2,C ++

解决方法

当主应用程序调用{​​{1}}时,它正在调用自己的DLL实例,因此也设置了自己的Init()副本。

将挂钩DLL注入另一个进程后,该进程将获得其自己的DLL副本,并因此获得其自己的_handler副本。但是_handler从未在该DLL实例上被调用,因此当您在该进程内调用hook函数时,其Init()的副本仍为0。这就是进程崩溃的原因,因为该钩子没有检查该条件。

您在另一个进程中的DLL无法跨进程边界调用主应用程序中的函数。当_handler为0时,您将不得不更改钩子函数以改为使用您选择的IPC机制与主应用程序进程进行通信。可以将窗口消息,管道,邮槽,套接字等用于该过程。交流。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...