【问题标题】:SetWindowsHookEx not working with WH_CBT on another process thread ID (but works on own thread ID)SetWindowsHookEx 不适用于另一个进程线程 ID 上的 WH_CBT(但适用于自己的线程 ID)
【发布时间】:2021-01-21 14:46:42
【问题描述】:

这几天我一直在绞尽脑汁,希望能得到一些帮助!当 CBT 钩子挂接到我的 WPF 应用程序的线程 ID 时,我能够使用 SetWindowsHookEx 检测到它,但是当该窗口成为前台应用程序时,我无法让它挂接到另一个进程窗口的线程 ID .

图 1:显示我可以获得 CBT 挂钩,用于检测主应用程序线程 ID 上的窗口最大化

图 2:显示我在监听另一个应用的线程 ID 时无法获取 CBT 钩子,它会导致应用崩溃!

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

同样,以下代码在当前 WPF 应用程序变为前台应用程序时有效,但当另一个应用程序的窗口变为前台时(例如记事本、Internet Explorer、文件资源管理器、Chrome 等),它会失败。

完整代码:(link to github zip file)

这里有一些代码的 sn-ps 来显示我做了什么:

定义回调的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, WPARAM wparam, LPARAM lparam);
WPARAM wparam, LPARAM lparam);
MYFUNCPTR _handler = 0;
inject.cpp:片段
void Init(MYFUNCPTR funcPtr)
{
    _handler = funcPtr;
}
LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, 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, wparam, lparam);
}

设置 CBT 挂钩的 DLL (dllwrapper.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, WPARAM wparam, 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:导入 WPF 应用程序的 C# dll 片段
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++

【问题讨论】:

    标签: c++ winapi hook setwindowshookex


    【解决方案1】:

    当主应用调用Init() 时,它调用的是它自己的DLL 实例,因此设置它自己的_handler 副本。

    当您的挂钩 DLL 被注入到另一个进程中时,该进程将获得它自己的 DLL 副本,从而获得它自己的 _handler 副本。但是Init() 永远不会在该DLL 实例上调用,因此当在该进程中调用您的钩子函数时,它的_handler 副本仍然为0。这就是进程崩溃的原因,因为钩子没有检查那个条件。

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多