鼠标和键盘操作的全局钩子库

注意只能用于AnyCPU、.NET版本一致、只能用于Winform。

简化后的代码如下:

using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace UserActivityMonitor
{
    /// <summary>
    /// This class monitors all mouse activities globally (also outside of the application) 
    /// and provides appropriate events.
    /// </summary>
    public static class HookManager
    {
        /// <summary>
        /// Occurs when the mouse pointer is moved. 
        /// </summary>
        public static event MouseEventHandler MouseMove
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                s_MouseMove += value;
            }

            remove
            {
                s_MouseMove -= value;
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when a click was performed by the mouse. 
        /// </summary>
        public static event MouseEventHandler MouseClick
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                s_MouseClick += value;
            }
            remove
            {
                s_MouseClick -= value;
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when the mouse a mouse button is pressed. 
        /// </summary>
        public static event MouseEventHandler MouseDown
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                s_MouseDown += value;
            }
            remove
            {
                s_MouseDown -= value;
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when a mouse button is released. 
        /// </summary>
        public static event MouseEventHandler MouseUp
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                s_MouseUp += value;
            }
            remove
            {
                s_MouseUp -= value;
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when the mouse wheel moves. 
        /// </summary>
        public static event MouseEventHandler MouseWheel
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                s_MouseWheel += value;
            }
            remove
            {
                s_MouseWheel -= value;
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when a double clicked was performed by the mouse. 
        /// </summary>
        public static event MouseEventHandler MouseDoubleClick
        {
            add
            {
                EnsureSubscribedToGlobalMouseEvents();
                if (s_MouseDoubleClick == null)
                {
                    s_DoubleClickTimer = new Timer
                        {
                            Interval = GetDoubleClickTime(),
                            Enabled = false
                        };
                    s_DoubleClickTimer.Tick += DoubleClickTimeElapsed;
                    MouseUp += OnMouseUp;
                }
                s_MouseDoubleClick += value;
            }
            remove
            {
                if (s_MouseDoubleClick != null)
                {
                    s_MouseDoubleClick -= value;
                    if (s_MouseDoubleClick == null)
                    {
                        MouseUp -= OnMouseUp;
                        s_DoubleClickTimer.Tick -= DoubleClickTimeElapsed;
                        s_DoubleClickTimer = null;
                    }
                }
                TryUnsubscribeFromGlobalMouseEvents();
            }
        }
        /// <summary>
        /// Occurs when a key is released. 
        /// </summary>
        public static event KeyEventHandler KeyUp
        {
            add
            {
                EnsureSubscribedToGlobalKeyboardEvents();
                s_KeyUp += value;
            }
            remove
            {
                s_KeyUp -= value;
                TryUnsubscribeFromGlobalKeyboardEvents();
            }
        }
        /// <summary>
        /// Occurs when a key is preseed. 
        /// </summary>
        public static event KeyEventHandler KeyDown
        {
            add
            {
                EnsureSubscribedToGlobalKeyboardEvents();
                s_KeyDown += value;
            }
            remove
            {
                s_KeyDown -= value;
                TryUnsubscribeFromGlobalKeyboardEvents();
            }
        }
        /// <summary>
        /// Occurs when a key is pressed.
        /// </summary>
        /// <remarks>
        /// Key events occur in the following order: 
        /// <list type="number">
        /// <item>KeyDown</item>
        /// <item>KeyPress</item>
        /// <item>KeyUp</item>
        /// </list>
        ///The KeyPress event is not raised by noncharacter keys; however, the noncharacter keys do raise the KeyDown and KeyUp events. 
        ///Use the KeyChar property to sample keystrokes at run time and to consume or modify a subset of common keystrokes. 
        ///To handle keyboard events only in your application and not enable other applications to receive keyboard events, 
        /// set the KeyPressEventArgs.Handled property in your form's KeyPress event-handling method to <b>true</b>. 
        /// </remarks>
        public static event KeyPressEventHandler KeyPress
        {
            add
            {
                EnsureSubscribedToGlobalKeyboardEvents();
                s_KeyPress += value;
            }
            remove
            {
                s_KeyPress -= value;
                TryUnsubscribeFromGlobalKeyboardEvents();
            }
        }
        private static event MouseEventHandler s_MouseMove;
        private static event MouseEventHandler s_MouseClick;
        private static event MouseEventHandler s_MouseDown;
        private static event MouseEventHandler s_MouseUp;
        private static event MouseEventHandler s_MouseWheel;
        private static event MouseEventHandler s_MouseDoubleClick;
        private static MouseButtons s_PrevClickedButton;
        private static Timer s_DoubleClickTimer;
        private static void DoubleClickTimeElapsed(object sender, EventArgs e)
        {
            s_DoubleClickTimer.Enabled = false;
            s_PrevClickedButton = MouseButtons.None;
        }
        private static void OnMouseUp(object sender, MouseEventArgs e)
        {
            if (e.Clicks < 1)
            {
                return;
            }
            if (e.Button.Equals(s_PrevClickedButton))
            {
                if (s_MouseDoubleClick != null)
                {
                    s_MouseDoubleClick.Invoke(null, e);
                }
                s_DoubleClickTimer.Enabled = false;
                s_PrevClickedButton = MouseButtons.None;
            }
            else
            {
                s_DoubleClickTimer.Enabled = true;
                s_PrevClickedButton = e.Button;
            }
        }
        private static event KeyPressEventHandler s_KeyPress;
        private static event KeyEventHandler s_KeyUp;
        private static event KeyEventHandler s_KeyDown;
        private const int WH_MOUSE_LL = 14;
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MOUSEWHEEL = 0x020A;
        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
        private const int WM_SYSKEYDOWN = 0x104;
        private const int WM_SYSKEYUP = 0x105;
        private const byte VK_SHIFT = 0x10;
        private const byte VK_CAPITAL = 0x14;
        [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(
            int idHook,
            int nCode,
            int wParam,
            IntPtr lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern int SetWindowsHookEx(
            int idHook,
            HookProc lpfn,
            IntPtr hMod,
            int dwThreadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern int UnhookWindowsHookEx(int idHook);
        [DllImport("user32")]
        private static extern int GetDoubleClickTime();
        [DllImport("user32")]
        private static extern int ToAscii(
            int uVirtKey,
            int uScanCode,
            byte[] lpbKeyState,
            byte[] lpwTransKey,
            int fuState);
        [DllImport("user32")]
        private static extern int GetKeyboardState(byte[] pbKeyState);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern short GetKeyState(int vKey);
        [StructLayout(LayoutKind.Sequential)]
        private struct Point
        {
            internal readonly int X;
            internal readonly int Y;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct MouseLLHookStruct
        {
            internal Point Point;
            internal readonly int MouseData;
            private readonly int Flags;
            private readonly int Time;
            private readonly int ExtraInfo;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct KeyboardHookStruct
        {
            internal readonly int VirtualKeyCode;
            internal readonly int ScanCode;
            internal readonly int Flags;
            private readonly int Time;
            private readonly int ExtraInfo;
        }
        private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
        private static HookProc s_MouseDelegate;
        private static int s_MouseHookHandle;
        private static int m_OldX;
        private static int m_OldY;
        private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                var mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
                var button = MouseButtons.None;
                short mouseDelta = 0;
                var clickCount = 0;
                var mouseDown = false;
                var mouseUp = false;

                switch (wParam)
                {
                    case WM_LBUTTONDOWN:
                        mouseDown = true;
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONUP:
                        mouseUp = true;
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONDBLCLK:
                        button = MouseButtons.Left;
                        clickCount = 2;
                        break;
                    case WM_RBUTTONDOWN:
                        mouseDown = true;
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONUP:
                        mouseUp = true;
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONDBLCLK:
                        button = MouseButtons.Right;
                        clickCount = 2;
                        break;
                    case WM_MOUSEWHEEL:
                        mouseDelta = (short)((mouseHookStruct.MouseData >> 16) & 0xffff);
                        break;
                }

                var e = new MouseEventExtArgs(
                    button,
                    clickCount,
                    mouseHookStruct.Point.X,
                    mouseHookStruct.Point.Y,
                    mouseDelta);

                if (s_MouseUp != null && mouseUp)
                {
                    s_MouseUp.Invoke(null, e);
                }

                if (s_MouseDown != null && mouseDown)
                {
                    s_MouseDown.Invoke(null, e);
                }

                if (s_MouseClick != null && clickCount > 0)
                {
                    s_MouseClick.Invoke(null, e);
                }

                if (s_MouseDoubleClick != null && clickCount == 2)
                {
                    s_MouseDoubleClick.Invoke(null, e);
                }

                if (s_MouseWheel != null && mouseDelta != 0)
                {
                    s_MouseWheel.Invoke(null, e);
                }

                if ((s_MouseMove != null) && (m_OldX != mouseHookStruct.Point.X || m_OldY != mouseHookStruct.Point.Y))
                {
                    m_OldX = mouseHookStruct.Point.X;
                    m_OldY = mouseHookStruct.Point.Y;
                    if (s_MouseMove != null)
                    {
                        s_MouseMove.Invoke(null, e);
                    }
                }

                if (e.Handled)
                {
                    return -1;
                }
            }

            return CallNextHookEx(s_MouseHookHandle, nCode, wParam, lParam);
        }
        private static void EnsureSubscribedToGlobalMouseEvents()
        {
            if (s_MouseHookHandle != 0)
            {
                return;
            }
            s_MouseDelegate = MouseHookProc;
            s_MouseHookHandle = SetWindowsHookEx(
                WH_MOUSE_LL,
                s_MouseDelegate,
                Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
                0);
            if (s_MouseHookHandle != 0)
            {
                return;
            }
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        private static void TryUnsubscribeFromGlobalMouseEvents()
        {
            if (s_MouseClick == null &&
                s_MouseDown == null &&
                s_MouseMove == null &&
                s_MouseUp == null &&
                s_MouseWheel == null)
            {
                ForceUnsunscribeFromGlobalMouseEvents();
            }
        }
        private static void ForceUnsunscribeFromGlobalMouseEvents()
        {
            if (s_MouseHookHandle == 0)
            {
                return;
            }
            var result = UnhookWindowsHookEx(s_MouseHookHandle);
            s_MouseHookHandle = 0;
            s_MouseDelegate = null;
            if (result != 0)
            {
                return;
            }
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        private static HookProc s_KeyboardDelegate;
        private static int s_KeyboardHookHandle;
        private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            var handled = false;
            if (nCode >= 0)
            {
                var MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                if (s_KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
                {
                    var keyData = (Keys)MyKeyboardHookStruct.VirtualKeyCode;
                    var e = new KeyEventArgs(keyData);
                    s_KeyDown.Invoke(null, e);
                    handled = e.Handled;
                }

                if (s_KeyPress != null && wParam == WM_KEYDOWN)
                {
                    var isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80);
                    var isDownCapslock = (GetKeyState(VK_CAPITAL) != 0);

                    var keyState = new byte[256];
                    GetKeyboardState(keyState);
                    var inBuffer = new byte[2];
                    if (ToAscii(MyKeyboardHookStruct.VirtualKeyCode,
                                MyKeyboardHookStruct.ScanCode,
                                keyState,
                                inBuffer,
                                MyKeyboardHookStruct.Flags) == 1)
                    {
                        var key = (char)inBuffer[0];
                        if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
                        var e = new KeyPressEventArgs(key);
                        s_KeyPress.Invoke(null, e);
                        handled = handled || e.Handled;
                    }
                }

                if (s_KeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
                {
                    var keyData = (Keys)MyKeyboardHookStruct.VirtualKeyCode;
                    var e = new KeyEventArgs(keyData);
                    s_KeyUp.Invoke(null, e);
                    handled = handled || e.Handled;
                }
            }

            if (handled)
            {
                return -1;
            }

            return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
        }
        private static void EnsureSubscribedToGlobalKeyboardEvents()
        {
            if (s_KeyboardHookHandle != 0)
            {
                return;
            }
            s_KeyboardDelegate = KeyboardHookProc;
            s_KeyboardHookHandle = SetWindowsHookEx(
                WH_KEYBOARD_LL,
                s_KeyboardDelegate,
                Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
                0);
            if (s_KeyboardHookHandle != 0)
            {
                return;
            }
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        private static void TryUnsubscribeFromGlobalKeyboardEvents()
        {
            if (s_KeyDown != null || s_KeyUp != null || s_KeyPress != null)
            {
                return;
            }
            if (s_KeyboardHookHandle == 0)
            {
                return;
            }
            var result = UnhookWindowsHookEx(s_KeyboardHookHandle);
            s_KeyboardHookHandle = 0;
            s_KeyboardDelegate = null;
            if (result != 0)
            {
                return;
            }
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
    internal class MouseEventExtArgs : MouseEventArgs
    {
        internal MouseEventExtArgs(MouseButtons buttons, int clicks, int x, int y, int delta)
            : base(buttons, clicks, x, y, delta)
        { }
        internal bool Handled { get; set; }
    }
}

 

posted on 2013-08-14 06:46  yao2yao4  阅读(698)  评论(0编辑  收藏  举报

导航