简体   繁体   English

全局鼠标事件处理程序

[英]Global mouse event handler

I have the following code which I got from somewhere to capture mouse events.我有以下代码,我从某个地方获得了用于捕获鼠标事件的代码。 I modified it and made an event handler so that I can subscribe to it.我修改了它并制作了一个事件处理程序,以便我可以订阅它。 The mouse events are captured correctly.鼠标事件被正确捕获。 But it never fires the event-handler.但它永远不会触发事件处理程序。 Can anybody figure out whats wrong with the code?任何人都可以弄清楚代码有什么问题吗?

public static class MouseHook
{
    public static event EventHandler MouseAction = delegate { };

    public static void Start() => _hookID = SetHook(_proc);
    public static void stop() => UnhookWindowsHookEx(_hookID);

    private static LowLevelMouseProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

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

    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
        {
           MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));  
           MouseAction(null,new EventArgs());
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private const int WH_MOUSE_LL = 14;

    private enum MouseMessages
    {
        WM_LBUTTONDOWN = 0x0201,
        WM_LBUTTONUP   = 0x0202,
        WM_MOUSEMOVE   = 0x0200,
        WM_MOUSEWHEEL  = 0x020A,
        WM_RBUTTONDOWN = 0x0204,
        WM_RBUTTONUP   = 0x0205
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData, flags, time;
        public IntPtr dwExtraInfo;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
      LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
      IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

I subscribe to it like this.我像这样订阅它。

MouseHook.Start();  
MouseHook.MouseAction += new EventHandler(Event);

Function receiving the event.接收事件的函数。

private void Event(object sender, EventArgs e) => Console.WriteLine("Left mouse click!"); 

Update: I put together the working code in to a open source nuget package for user action hooks.更新:我将工作代码放在一个用于用户操作挂钩的开源 nuget 包中。

return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);

This code will fail when you run it on .NET 4 on a Windows version earlier than Windows 8. The CLR no longer simulates unmanaged module handles for managed assemblies.当您在早于 Windows 8 的 Windows 版本上的 .NET 4 上运行此代码时,此代码将失败。CLR 不再模拟托管程序集的非托管模块句柄。 You can't detect this failure in your code because it is missing the required error checking.您无法在代码中检测到此故障,因为它缺少所需的错误检查。 Both on GetModuleHandle and SetWindowsHookEx.在 GetModuleHandle 和 SetWindowsHookEx 上。 Never skip error checking when you pinvoke, the winapi doesn't throw exceptions. pinvoke 时不要跳过错误检查,winapi 不会抛出异常。 Check if they return IntPtr.Zero and simply throw a Win32Exception when they do.检查它们是否返回 IntPtr.Zero 并在返回时简单地抛出 Win32Exception。

The fix is simple, SetWindowsHookEx() requires a valid module handle but doesn't actually use it when you set a low-level mouse hook.修复很简单,SetWindowsHookEx() 需要一个有效的模块句柄,但在设置低级鼠标钩子时实际上并不使用它。 So any handle will do, you can pass the handle for user32.dll, always loaded in a .NET application.所以任何句柄都可以,你可以传递 user32.dll 的句柄,它总是加载在 .NET 应用程序中。 Fix:使固定:

IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
if (hook == IntPtr.Zero) 
{
    throw new System.ComponentModel.Win32Exception();
}
return hook;

I just copied your code into a simple windows form and its working as you described it should.我只是将您的代码复制到一个简单的 Windows 窗体中,并且它的工作方式应符合您的描述。 How are you using it exactly?你是如何使用它的? Where are you starting and attaching the event?你在哪里开始和附加事件?

And for completeness sake this is the code I ended up with - started from a simple C# form template为了完整起见,这是我最终得到的代码 - 从一个简单的 C# 表单模板开始

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            MouseHook.Start();
            MouseHook.MouseAction += new EventHandler(Event);
        }

        private void Event(object sender, EventArgs e) { Console.WriteLine("Left mouse click!"); }
    }

    public static class MouseHook
    {
        public static event EventHandler MouseAction = delegate { };

        public static void Start()
        {
            _hookID = SetHook(_proc);


        }
        public static void stop()
        {
            UnhookWindowsHookEx(_hookID);
        }

        private static LowLevelMouseProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

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

        private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr HookCallback(
          int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
            {
                MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                MouseAction(null, new EventArgs());
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        private const int WH_MOUSE_LL = 14;

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
          LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
          IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);


    }
}

For any future visitor:对于任何未来的访客:

I've implemented a thread level mouse hook.我已经实现了一个线程级鼠标钩子。

在此处输入图片说明

_process = Process.Start(@"c:\windows\notepad.exe");
//_process = Process.Start(@"c:\windows\syswow64\notepad.exe"); // works also with 32-bit

_mouseHook = new MouseHook(_process.Id);
_mouseHook.MessageReceived += MouseHook_MessageReceived;
_mouseHook.Install();

...

private void MouseHook_MessageReceived(object sender, MouseMessageEventArgs e)
{
    Debug.WriteLine($"Mouse Message Code: {e.MessageCode}; X: {e.X}; Y: {e.Y}; Delta: {e.Delta}");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM