如何在带有自定义数据的外线程上安装钩子程序?

How to install hook procedure on external thread with custom data?

在我的场景中,我有一个应用程序(我不拥有)有多个 windows 并且同一个线程拥有多个 windows。因此,当我使用 SetWindowsHookEx 安装挂钩程序(从我自己的应用程序,使用包含此程序的 DLL 文件)时,它将安装一个挂钩来监视同一线程拥有的所有 windows目标应用程序。

我要实现的是安装的钩子程序会有一个变量,例如targetHwnd。一旦拥有它,它将忽略任何不是 targetHwnd.

的东西

比如我会有这样的东西:

LRESULT CALLBACK GetMsgHookProc(int code, WPARAM wParam, LPARAM lParam)
{

    // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644981(v=vs.85)#parameters
    if (code == HC_ACTION)
    {
        const auto msg = (MSG*)lParam;

        if (msg->hwnd != targetHwnd)
            // We skip processing any other window that owned by this thread
            return CallNextHookEx(hHook, code, wParam, lParam);


        // Here I will do specific processing for the window I want
        // ...
        // ...
        // ...
    }

    return CallNextHookEx(hHook, code, wParam, lParam);
}

问题是: 我如何得出这样的结论,即带有钩子过程的 DLL 被注入了这个外部信息,在我们的例子中是 targetHwnd.

我没看到函数 SetWindowsHookEx 有自定义数据或类似参数的参数。

我想到的另一种方法是将 WM_COPYDATA 发送到我需要的 windows 之一(并且由我为其安装挂钩的外部应用程序线程拥有)。 然后,GetMsgHookProc 将通过 WM_COPYDATA 消息获取此数据。 如果在 DLL 中,targetHwndNULL,它将从 lParam 参数中获取此数据。

但是我怎么知道是我的应用程序发送了它,还是只是为了以防万一另一个应用程序也向 window 发送了 WM_COPYDATA?听起来不安全.. 所以我认为我应该使用 wParam(“window 传递数据的句柄”),然后通过调用 GetClassName 验证它是从我的应用程序发送的,并检查是否class name 是我期望的独一无二的名字。

但我不确定从 DLL 使用这些 API 是否安全,因为根据 MS,它可能不安全。

所以如果你能给我一个线索,我应该做什么,如果我认为这样做是正确的方法,这将对我有帮助。

谢谢。

WM_COPYDATA 跨线程边界和 DLL 边界使用是完全安全的。您可以使用 COPYDATASTRUCT::dwData 字段来识别消息是否属于您,不属于您,只需在该字段中存储一个唯一值即可。通常,RegisterWindowMessage() 用于该目的。

然而,WM_COPYDATA 对于这个任务来说有点矫枉过正。由于 HWND 直接适合 WPARAMLPARAM(由 WM_COPYDATA 证明),您可以发送(甚至 post)自定义消息,只有您的 DLL 挂钩知道如何处理的一个。同样,RegisterWindowMessage() 可用于该目的。

另一种选择是使用共享内存,例如通过 CreateFileMapping()MapViewOfFile()(或者,通过 #pragma data_seg,如果您的编译器支持的话)。安装钩子的应用程序可以分配一块共享内存并将所需的 HWND 存储在其中,然后注入的 DLL 可以访问它。参见 Using Shared Memory in a Dynamic-Link Library

非常感谢 Remy Lebeau 帮助我找到完整解决方案的方向。但是,他的回答中有遗漏的部分,所以这是我找到的完整答案:

以下代码是从我的应用程序中复制的。 Win32Native class.

中的某些部分可能会丢失,例如方法
  • Win32Native class 中的所有方法只是 DllImport 本机 Win32 API 的集合。所以你可以在pvpoke网站上找到它。
  • 有一个名为 WindowItem 的习俗 class。你不会在任何地方找到它。 你应该知道的唯一相关信息是这个 class 有成员: Hwnd - 我们挂钩的目标 window 的句柄,而 ProcessId 只是pid 拥有 window 的进程。我只在这段代码中使用这些成员

安装挂钩的主机应用:

using System;
using System.Runtime.InteropServices;
using WindowTop.Helpers;
using WindowTop.Managers;

namespace WindowTop.UI.Features.ShrinkBox
{
    public class ShrinkInteractHookNative
    {
        [DllImport("ShrinkInteractHook.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr InstallHook(uint threadId);
    }


    public class ShrinkInteractHook : ShrinkInteractHookNative
    {
        public static IntPtr InstallHook(WindowItem windowItem)
        {
            var processId = windowItem.ProcessId;
            var windowThread = Win32Native.GetWindowThreadProcessId(windowItem.Hwnd, ref processId);
            var hHook = InstallHook(windowThread);

            var WM_SEND_TARGET_HWND = Win32Native.RegisterWindowMessage("WINDOWTOP_WM_SEND_TARGET_HWND");

            Win32Native.PostThreadMessage(windowThread, WM_SEND_TARGET_HWND, UIntPtr.Zero,
                windowItem.Hwnd);

            return hHook;
        }

        public static void UnInstallHook(IntPtr hHook)
        {
            Win32Native.UnhookWindowsHookEx(hHook);
        }
    }
}

DLL

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"


HINSTANCE hInstHookDll = NULL;
HHOOK hHook = NULL;


HWND targetHwnd = NULL;
UINT WM_SEND_TARGET_HWND = 0;




LRESULT CALLBACK GetMsgHookProc(int code, WPARAM wParam, LPARAM lParam)
{
    // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644981(v=vs.85)#parameters
    if (LOWORD(msg->message) == WM_SEND_TARGET_HWND)
    {
        targetHwnd = (HWND)msg->lParam; // Here we get the targetHwnd
    }
    
    // Specific processing goes here:
    // ...
    // ...

    // Pass this to the next hook
    return CallNextHookEx(hHook, code, wParam, lParam);
}


extern "C" {

__declspec(dllexport) HHOOK __stdcall InstallHook(unsigned int threadId)
{
    if (hHook != NULL)
    {
        UnhookWindowsHookEx(hHook);
        hHook = NULL;
    }

    return SetWindowsHookEx(WH_GETMESSAGE, GetMsgHookProc, hInstHookDll, threadId);
}
}


BOOL APIENTRY DllMain(HANDLE hModule, DWORD reasonForCall, LPVOID lpReserved)
{
    switch (reasonForCall)
    {
    case DLL_PROCESS_ATTACH:
        hInstHookDll = (HINSTANCE)hModule;
        WM_SEND_TARGET_HWND = RegisterWindowMessage(L"WINDOWTOP_WM_SEND_TARGET_HWND");
        break;
    case DLL_PROCESS_DETACH:
        // TODO: Logic in case the dll was unloaded 
        break;
    }
    return TRUE;
}