【C#】解决MouseHook捕获鼠标动作,在有些电脑上SetWindowsHookEx失败返回0的问题

标签: c#MouseHookSetWindows
440人阅读 评论(0) 收藏 举报
分类:

最近在debug鼠标位置捕获的功能时发现在其中的一台开发电脑上,SetWindowsHookEx一直返回0,导致Hook设置失败,有时候调成Release模式又是正常的。代码如下:

hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure,Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);

为什么一直返回0呢?微软也没有告诉我们具体原因,只让我们查询System Error Code

Type:
Type: HHOOK
If the function succeeds, the return value is the handle to the hook procedure.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

通过文档里写的call GetLastError方法可以获取到error code。我这里的error code是126,查询对应文档发现详细错误是:

ERROR_MOD_NOT_FOUND
126 (0x7E)
The specified module could not be found.

即模块错误。

SetWindowHookEx中唯一跟模块有关的参数只有Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0])了。
在debug过程中,发现GetModules()[0]都是不为null的而且GetHINSTANCE也能获取到正确的值,实在不知道哪里的问题。不过经过不懈的搜索,发现StackOverflow里的大牛解决过这个问题(链接参考底部)。大概意思就是说在.Net4.0和Win8之前的版本中,CLR不再模拟托管程序集中的非托管句柄(我是.net4.0+win10不知为何也遇到了这个问题(lll¬ω¬))。建议我们用user32的句柄,而这个句柄会一直被.net加载。

所以 代码改动下就好了:

hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure,GetModuleHandle("user32"), 0);

完整代码参考:

using CommonUtils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Omni.Utils
{
    class MouseHook
    {
        private const int WM_MOUSEMOVE = 0x200;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_MBUTTONUP = 0x208;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDBLCLK = 0x209;

        public event MouseEventHandler OnMouseActivity;

        static int hMouseHook = 0;

        public const int WH_MOUSE_LL = 14;

        HookProc MouseHookProcedure;
        Log _log = new Log("MouseHook", true, Log4netWrapper.Default);
        [StructLayout(LayoutKind.Sequential)]
        public class POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class MouseHookStruct
        {
            public POINT pt;
            public int hWnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int GetLastError();

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

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);


        public MouseHook()
        {
        }

        ~MouseHook()
        {
            Stop();
        }

        public void Start()
        {
            if (hMouseHook == 0)
            {
                MouseHookProcedure = new HookProc(MouseHookProc);
                //_log.E("SetWindowsHookEx failed.error code:" + Marshal.GetLastWin32Error());
                //Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0])
                hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure,GetModuleHandle("user32"), 0);

                if (hMouseHook == 0)
                {
                    int errorCode = GetLastError();
                    _log.E("SetWindowsHookEx failed.error code:" + errorCode);
                    Stop();
                }
            }
        }

        public void Stop()
        {
            bool retMouse = true;
            if (hMouseHook != 0)
            {
                retMouse = UnhookWindowsHookEx(hMouseHook);
                hMouseHook = 0;
            }

            if (!(retMouse))
            {
                _log.E("UnhookWindowsHookEx failed.");
            }
        }

        private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            if ((nCode >= 0) && (OnMouseActivity != null))
            {
                MouseButtons button = MouseButtons.None;
                int clickCount = 0;

                switch (wParam)
                {
                    case WM_LBUTTONDOWN:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONUP:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONDBLCLK:
                        button = MouseButtons.Left;
                        clickCount = 2;
                        break;
                    case WM_RBUTTONDOWN:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONUP:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONDBLCLK:
                        button = MouseButtons.Right;
                        clickCount = 2;
                        break;
                }

                MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
                MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
                if (button == MouseButtons.Left && clickCount == 1)//只捕获鼠标左键单击动作
                    OnMouseActivity(this, e);
            }
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
        }
    }
}

使用方法:

MouseHook hook = new MouseHook();
hook.OnMouseActivity += Hook_OnMouseActivity;
hook.Start();

 private void Hook_OnMouseActivity(object sender, System.Windows.Forms.MouseEventArgs e)
{
  //e.X  e.Y   e.Button == System.Windows.Forms.MouseButtons.Left
}

参考链接

SetWindowsHookEx function
Runtime Error 126 - The specified module could not be found
Global mouse event handler

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:163947次
    • 积分:2361
    • 等级:
    • 排名:第17485名
    • 原创:87篇
    • 转载:18篇
    • 译文:1篇
    • 评论:32条
    文章分类
    最新评论