C#中的SetWindowsHookEx

我正试图挂钩第三方应用程序,以便我可以绘制到它的屏幕。 绘制到屏幕很容易,我不需要帮助,但我似乎遇到使用SetWindowsHookEx来处理WH_GETMESSAGE的问题。 我无法弄清楚最后2个参数要传递什么。

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WindowDrawer { public partial class Form1 : Form { private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); static IntPtr hHook; IntPtr windowHandle; uint processHandle; HookProc PaintHookProcedure; [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName); [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)] static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); [System.Runtime.InteropServices.DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter [System.Runtime.InteropServices.DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet =System.Runtime.InteropServices.CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { PaintHookProcedure = new HookProc(PaintHookProc); windowHandle = FindWindowByCaption(0, "Untitled - Notepad"); uint threadID = GetWindowThreadProcessId(windowHandle, out processHandle); IntPtr hMod = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(Form1).Module); // HERE IS THE PROBLEM. WHAT THE HECK DO I PASS INTO THE LAST 2 PARAMS? I get a null pointer hHook = SetWindowsHookEx(WH_GETMESSAGE, PaintHookProcedure, hMod, threadID); } public int PaintHookProc(int nCode, IntPtr wParam, IntPtr lParam) { // Do some painting here. return CallNextHookEx(hHook, nCode, wParam, lParam); } private const int WM_PAINT = 15; private const int WH_GETMESSAGE = 3; } } 

SetWindowsHookEx特此指定最后两个参数:

  • hMod

[in]处理包含lpfn参数指向的钩子过程的DLL。 如果dwThreadId参数指定由当前进程创建的线程,并且钩子过程在与当前进程关联的代码内,则必须将hMod参数设置为NULL。

  • dwThreadId

[in]指定与钩子过程关联的线程的标识符。 如果此参数为零,则挂钩过程与在与调用线程相同的桌面中运行的所有现有线程关联。

我不确定您是否可以按照所需的方式使用.NET dll,但您当然可以尝试。

通过Mars.GetHINSTANCE(typeof(Form1).Module)和dwThreadId通过Process.Threads 获取 hMod 。 或者,如果您想要一个全局挂钩(即当前桌面中所有GetMessage()调用的挂钩GetMessage() ,请将dwThreadId设置为0,但要注意性能损失。

以下表明这不起作用:

“.NET Framework不支持全局挂钩。除了WH_KEYBOARD_LL低级挂钩和WH_MOUSE_LL低级别挂钩之外,您无法在Microsoft .NET Framework中实现全局挂钩。”

从“如何在Visual C#.NET中设置Windows挂钩”

我不知道,但如果您正在使用指定您想要的参数值,如API帮助所说,“将DLL注入另一个进程”,那么对于所有我知道它只有在您编写非托管DLL时才有效从中称呼它。

我相信你需要P / Invoke GetModuleHandle并使用它为SetWindowsHookEx的第三个参数返回的句柄。 我还认为0对于第四个参数是正确的,因为您不想挂钩第三方应用程序中的任何一个特定线程。

如果这对您不起作用,MSDN上的SetWindowsHookEx可能会指向正确的方向。

我知道这是一个老问题,但我希望还有人会发现这个有用。 我认为你混合intIntPtr

 [System.Runtime.InteropServices.DllImport("user32.dll")] static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

这项工作对我来说使用13 ……

  private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(13, proc, GetModuleHandle(curModule.ModuleName), 0); } }