【问题标题】:SetWindowsHookEx in C#C# 中的 SetWindowsHookEx
【发布时间】:2009-11-28 02:47:25
【问题描述】:

我正在尝试连接第 3 方应用,以便在其屏幕上绘图。在屏幕上绘图很容易,我不需要帮助,但我似乎在使用SetWindowsHookEx 处理WH_GETMESSAGE 时遇到问题。我不知道最后两个参数要传递什么。

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;
    }
}

【问题讨论】:

    标签: c# setwindowshookex wm-paint


    【解决方案1】:

    SetWindowsHookEx 这样指定了最后两个参数:

    • hMod

    [in] 包含 DLL 的句柄 lpfn指向的钩子过程 范围。 hMod 参数必须是 如果 dwThreadId 设置为 NULL 参数指定创建的线程 由当前进程和如果钩子 程序在代码内 与当前进程相关联。

    • dwThreadId

    [in] 指定标识符 钩子程序使用的线程 是要关联的。如果这个参数 为零,钩子程序是 与所有现有线程相关联 在同一个桌面上运行 调用线程。

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

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

    【讨论】:

    • 我修改了上面的代码以反映您的想法。我仍然收到 SetWindowsHookEx(WH_GETMESSAGE, PaintHookProcedure, hMod, threadID) == 0;
    • 您很可能需要将 PaintHookProcedure 打包到单独的 DLL 中;这排除了 .NET 的使用。用 GetLastError() 获取错误信息代码,看看问题出在哪里。
    • GetLastError() 我的意思是 Marshal.GetLastWin32Error(); p/直接调用GetLastError()是不可靠的。
    • GetLastError 返回 1428。这是一个无效的 hMod。有什么想法吗?
    • 检查Marshal.GetHINSTANCE的返回值是否为-1。如果是这种情况,您需要将您的 hook proc 移动到一个 DLL 中,这意味着您需要在非托管代码中编写它。
    【解决方案2】:

    以下表明这是行不通的:

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

    来自"How to set a Windows hook in Visual C# .NET"

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • GetModuleHandle(string lpModuleName);我要传入什么? exe名称?
    • 通过NULL,它将从调用进程(您的可执行文件)中提取句柄。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      我知道这是一个老问题,但我希望仍然有人会觉得这很有用。我认为您混淆了intIntPtr

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

      【讨论】:

        【解决方案6】:

        这项工作为我使用 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);
                    }
                }
        

        【讨论】:

        • HookType.WH_KEYBOARD_LL = 13
        【解决方案7】:
          internal enum HookType : uint {
            WH_JOURNALRECORD = 0,
            WH_JOURNALPLAYBACK = 1,
            WH_KEYBOARD = 2,
            WH_GETMESSAGE = 3,
            WH_CALLWNDPROC = 4,
            WH_CBT = 5,
            WH_SYSMSGFILTER = 6,
            WH_MOUSE = 7,
            WH_HARDWARE = 8,
            WH_DEBUG = 9,
            WH_SHELL = 10,
            WH_FOREGROUNDIDLE = 11,
            WH_CALLWNDPROCRET = 12,
            WH_KEYBOARD_LL = 13,
            WH_MOUSE_LL = 14
          }
        

        .NET Framework 不支持全局挂钩

        除了 WH_KEYBOARD_LL 低级挂钩和 WH_MOUSE_LL 低级挂钩,您不能在 Microsoft .NET Framework 中实现全局挂钩。

        要安装全局挂钩,挂钩必须具有本机 DLL 导出,才能将自身注入另一个需要有效、一致的函数才能调用的进程。此行为需要 DLL 导出。

        .NET Framework 不支持 DLL 导出。托管代码没有函数指针一致值的概念,因为这些函数指针是动态构建的代理。

        在安装钩子的线程上调用低级钩子程序。低级挂钩不需要在 DLL 中实现挂钩过程。

        另请参阅: Snoop - WPF 间谍实用程序

        或者我的 WPF => WF => Win32 LL_Keyboard Hook Proxy w/ Gestures

        【讨论】:

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