有没有办法全局挂钩鼠标操作,比如我挂钩键盘键?

时间:2017-07-08 20:11:33

标签: c# .net winforms

这是我用来挂钩键盘的课程。

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

public class Hook : IDisposable
{
    bool Global = false;

    public delegate void LocalKeyEventHandler(Keys key, bool Shift, bool Ctrl, bool Alt);
    public event LocalKeyEventHandler KeyDown;
    public event LocalKeyEventHandler KeyUp;

    public delegate int CallbackDelegate(int Code, int W, int L);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct KBDLLHookStruct
    {
        public Int32 vkCode;
        public Int32 scanCode;
        public Int32 flags;
        public Int32 time;
        public Int32 dwExtraInfo;
    }

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)]
    private static extern int SetWindowsHookEx(HookType idHook, CallbackDelegate lpfn, int hInstance, int threadId);

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)]
    private static extern bool UnhookWindowsHookEx(int idHook);

    [DllImport("user32", CallingConvention = CallingConvention.StdCall)]
    private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam);

    [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern int GetCurrentThreadId();

    public enum HookType : int
    {
        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
    }

    private int HookID = 0;
    CallbackDelegate TheHookCB = null;

    //Start hook
    public Hook(bool Global)
    {
        this.Global = Global;
        TheHookCB = new CallbackDelegate(KeybHookProc);
        if (Global)
        {
            HookID = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, TheHookCB,
                0, //0 for local hook. eller hwnd til user32 for global
                0); //0 for global hook. eller thread for hooken
        }
        else
        {
            HookID = SetWindowsHookEx(HookType.WH_KEYBOARD, TheHookCB,
                0, //0 for local hook. or hwnd to user32 for global
                GetCurrentThreadId()); //0 for global hook. or thread for the hook
        }
    }

    bool IsFinalized = false;
    ~Hook()
    {
        if (!IsFinalized)
        {
            UnhookWindowsHookEx(HookID);
            IsFinalized = true;
        }
    }
    public void Dispose()
    {
        if (!IsFinalized)
        {
            UnhookWindowsHookEx(HookID);
            IsFinalized = true;
        }
    }

    //The listener that will trigger events
    private int KeybHookProc(int Code, int W, int L)
    {
        KBDLLHookStruct LS = new KBDLLHookStruct();
        if (Code < 0)
        {
            return CallNextHookEx(HookID, Code, W, L);
        }
        try
        {
            if (!Global)
            {
                if (Code == 3)
                {
                    IntPtr ptr = IntPtr.Zero;

                    int keydownup = L >> 30;
                    if (keydownup == 0)
                    {
                        if (KeyDown != null) KeyDown((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed());
                    }
                    if (keydownup == -1)
                    {
                        if (KeyUp != null) KeyUp((Keys)W, GetShiftPressed(), GetCtrlPressed(), GetAltPressed());
                    }
                    //System.Diagnostics.Debug.WriteLine("Down: " + (Keys)W);
                }
            }
            else
            {
                KeyEvents kEvent = (KeyEvents)W;

                Int32 vkCode = Marshal.ReadInt32((IntPtr)L); //Leser vkCode som er de første 32 bits hvor L peker.

                if (kEvent != KeyEvents.KeyDown && kEvent != KeyEvents.KeyUp && kEvent != KeyEvents.SKeyDown && kEvent != KeyEvents.SKeyUp)
                {
                }
                if (kEvent == KeyEvents.KeyDown || kEvent == KeyEvents.SKeyDown)
                {
                    if (KeyDown != null) KeyDown((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed());
                }
                if (kEvent == KeyEvents.KeyUp || kEvent == KeyEvents.SKeyUp)
                {
                    if (KeyUp != null) KeyUp((Keys)vkCode, GetShiftPressed(), GetCtrlPressed(), GetAltPressed());
                }
            }
        }
        catch (Exception)
        {
            //Ignore all errors...
        }

        return CallNextHookEx(HookID, Code, W, L);

    }

    public enum KeyEvents
    {
        KeyDown = 0x0100,
        KeyUp = 0x0101,
        SKeyDown = 0x0104,
        SKeyUp = 0x0105
    }

    [DllImport("user32.dll")]
    static public extern short GetKeyState(System.Windows.Forms.Keys nVirtKey);

    public static bool GetCapslock()
    {
        return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.CapsLock)) & true;
    }
    public static bool GetNumlock()
    {
        return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.NumLock)) & true;
    }
    public static bool GetScrollLock()
    {
        return Convert.ToBoolean(GetKeyState(System.Windows.Forms.Keys.Scroll)) & true;
    }
    public static bool GetShiftPressed()
    {
        int state = GetKeyState(System.Windows.Forms.Keys.ShiftKey);
        if (state > 1 || state < -1) return true;
        return false;
    }
    public static bool GetCtrlPressed()
    {
        int state = GetKeyState(System.Windows.Forms.Keys.ControlKey);
        if (state > 1 || state < -1) return true;
        return false;
    }
    public static bool GetAltPressed()
    {
        int state = GetKeyState(System.Windows.Forms.Keys.Menu);
        if (state > 1 || state < -1) return true;
        return false;
    }
}

在form1中,如何使用它的示例:

using System;
using System.Windows.Forms;

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

            Hook kh = new Hook(true);
            kh.KeyDown += Kh_KeyDown;
        }

        private void Kh_KeyDown(Keys key, bool Shift, bool Ctrl, bool Alt)
        {
            MessageBox.Show(key.ToString());
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }
}

我想知道是否有办法使用该类或其他方式来挂钩鼠标?例如,当我单击鼠标中键时。或者,如果我滚动鼠标滚轮然后给出一个消息我滚动轮子但轮子不是那么重要更多的是按钮左,右,中。

1 个答案:

答案 0 :(得分:3)

是的,我在我的项目中使用了以下代码,它允许直接访问大多数Windows鼠标事件:

using System;
using System.Runtime.InteropServices;

/// <summary>
///     The CallWndProc hook procedure is an application-defined or library-defined
///     callback function used with the SetWindowsHookEx function. The HOOKPROC type
///     defines a pointer to this callback function. CallWndProc is a placeholder for
///     the application-defined or library-defined function name.
/// </summary>
/// <param name="nCode">
///     Specifies whether the hook procedure must process the message.
/// </param>
/// <param name="wParam">
///     Specifies whether the message was sent by the current thread.
/// </param>
/// <param name="lParam">
///     Pointer to a CWPSTRUCT structure that contains details about the message.
/// </param>
/// <returns>
///     If nCode is less than zero, the hook procedure must return the value returned
///     by CallNextHookEx. If nCode is greater than or equal to zero, it is highly
///     recommended that you call CallNextHookEx and return the value it returns;
///     otherwise, other applications that have installed WH_CALLWNDPROC hooks will
///     not receive hook notifications and may behave incorrectly as a result. If the
///     hook procedure does not call CallNextHookEx, the return value should be zero.
/// </returns>
internal delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

internal class NativeMethods
{
    /// <summary>
    ///     The SetWindowsHookEx function installs an application-defined hook
    ///     procedure into a hook chain. You would install a hook procedure to monitor
    ///     the system for certain types of events. These events are associated either
    ///     with a specific thread or with all threads in the same desktop as the
    ///     calling thread.
    /// </summary>
    /// <param name="hookType">
    ///     Specifies the type of hook procedure to be installed
    /// </param>
    /// <param name="callback">Pointer to the hook procedure.</param>
    /// <param name="hMod">
    ///     Handle to the DLL containing the hook procedure pointed to by the lpfn
    ///     parameter. The hMod parameter must be set to NULL if the dwThreadId
    ///     parameter specifies a thread created by the current process and if the
    ///     hook procedure is within the code associated with the current process.
    /// </param>
    /// <param name="dwThreadId">
    ///     Specifies the identifier of the thread with which the hook procedure is
    ///     to be associated.
    /// </param>
    /// <returns>
    ///     If the function succeeds, the return value is the handle to the hook
    ///     procedure. If the function fails, the return value is 0.
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetWindowsHookEx(HookType hookType,
        HookProc callback, IntPtr hMod, uint dwThreadId);

    /// <summary>
    ///     The UnhookWindowsHookEx function removes a hook procedure installed in
    ///     a hook chain by the SetWindowsHookEx function.
    /// </summary>
    /// <param name="hhk">Handle to the hook to be removed.</param>
    /// <returns>
    ///     If the function succeeds, the return value is true.
    ///     If the function fails, the return value is false.
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool UnhookWindowsHookEx(IntPtr hhk);

    /// <summary>
    ///     The CallNextHookEx function passes the hook information to the next hook
    ///     procedure in the current hook chain. A hook procedure can call this
    ///     function either before or after processing the hook information.
    /// </summary>
    /// <param name="idHook">Handle to the current hook.</param>
    /// <param name="nCode">
    ///     Specifies the hook code passed to the current hook procedure.
    /// </param>
    /// <param name="wParam">
    ///     Specifies the wParam value passed to the current hook procedure.
    /// </param>
    /// <param name="lParam">
    ///     Specifies the lParam value passed to the current hook procedure.
    /// </param>
    /// <returns>
    ///     This value is returned by the next hook procedure in the chain.
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);
}

internal static class HookCodes
{
    public const int HC_ACTION = 0;
    public const int HC_GETNEXT = 1;
    public const int HC_SKIP = 2;
    public const int HC_NOREMOVE = 3;
    public const int HC_NOREM = HC_NOREMOVE;
    public const int HC_SYSMODALON = 4;
    public const int HC_SYSMODALOFF = 5;
}

internal enum HookType
{
    WH_KEYBOARD = 2,
    WH_MOUSE = 7,
    WH_KEYBOARD_LL = 13,
    WH_MOUSE_LL = 14
}

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

/// <summary>
///     The MSLLHOOKSTRUCT structure contains information about a low-level keyboard
///     input event.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEHOOKSTRUCT
{
    public POINT pt; // The x and y coordinates in screen coordinates
    public int hwnd; // Handle to the window that'll receive the mouse message
    public int wHitTestCode;
    public int dwExtraInfo;
}

/// <summary>
///     The MOUSEHOOKSTRUCT structure contains information about a mouse event passed
///     to a WH_MOUSE hook procedure, MouseProc.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct MSLLHOOKSTRUCT
{
    public POINT pt; // The x and y coordinates in screen coordinates. 
    public int mouseData; // The mouse wheel and button info.
    public int flags;
    public int time; // Specifies the time stamp for this message. 
    public IntPtr dwExtraInfo;
}

internal enum MouseMessage
{
    WM_MOUSEMOVE = 0x0200,
    WM_LBUTTONDOWN = 0x0201,
    WM_LBUTTONUP = 0x0202,
    WM_LBUTTONDBLCLK = 0x0203,
    WM_RBUTTONDOWN = 0x0204,
    WM_RBUTTONUP = 0x0205,
    WM_RBUTTONDBLCLK = 0x0206,
    WM_MBUTTONDOWN = 0x0207,
    WM_MBUTTONUP = 0x0208,
    WM_MBUTTONDBLCLK = 0x0209,

    WM_MOUSEWHEEL = 0x020A,
    WM_MOUSEHWHEEL = 0x020E,

    WM_NCMOUSEMOVE = 0x00A0,
    WM_NCLBUTTONDOWN = 0x00A1,
    WM_NCLBUTTONUP = 0x00A2,
    WM_NCLBUTTONDBLCLK = 0x00A3,
    WM_NCRBUTTONDOWN = 0x00A4,
    WM_NCRBUTTONUP = 0x00A5,
    WM_NCRBUTTONDBLCLK = 0x00A6,
    WM_NCMBUTTONDOWN = 0x00A7,
    WM_NCMBUTTONUP = 0x00A8,
    WM_NCMBUTTONDBLCLK = 0x00A9
}

/// <summary>
///     The structure contains information about a low-level keyboard input event.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct KBDLLHOOKSTRUCT
{
    public int vkCode; // Specifies a virtual-key code
    public int scanCode; // Specifies a hardware scan code for the key
    public int flags;
    public int time; // Specifies the time stamp for this message
    public int dwExtraInfo;
}

internal enum KeyboardMessage
{
    WM_KEYDOWN = 0x0100,
    WM_KEYUP = 0x0101,
    WM_SYSKEYDOWN = 0x0104,
    WM_SYSKEYUP = 0x0105
}

要使用它,您必须注册鼠标钩子。 LowLevelMouseProc是回调。每次发生新的鼠标事件时都会执行此方法。

private void SetUpHook()
{
    Logger.Debug("Setting up global mouse hook");

    // Create an instance of HookProc.
    _globalLlMouseHookCallback = LowLevelMouseProc;

    _hGlobalLlMouseHook = NativeMethods.SetWindowsHookEx(
        HookType.WH_MOUSE_LL,
        _globalLlMouseHookCallback,
        Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
        0);

    if (_hGlobalLlMouseHook == IntPtr.Zero)
    {
        Logger.Fatal("Unable to set global mouse hook");
        throw new Win32Exception("Unable to set MouseHook");
    }
}

清除鼠标钩:

private void ClearHook()
{
    Logger.Debug("Deleting global mouse hook");

    if (_hGlobalLlMouseHook != IntPtr.Zero)
    {
        // Unhook the low-level mouse hook
        if (!NativeMethods.UnhookWindowsHookEx(_hGlobalLlMouseHook))
            throw new Win32Exception("Unable to clear MouseHoo;");

        _hGlobalLlMouseHook = IntPtr.Zero;
    }
}

最后但并非最不重要的是LowLevelMouseProc的示例,您可以用来拦截鼠标事件的回调:

public int LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
        // Get the mouse WM from the wParam parameter
        var wmMouse = (MouseMessage) wParam;
        if (wmMouse == MouseMessage.WM_LBUTTONDOWN && LeftButtonState == ButtonState.Released)
        {
            Logger.Debug("Left Mouse down");
        }
        if (wmMouse == MouseMessage.WM_LBUTTONUP && LeftButtonState == ButtonState.Down)
        {
            Logger.Debug("Left Mouse up");
        }

        if (wmMouse == MouseMessage.WM_RBUTTONDOWN && RightButtonState == ButtonState.Released)
        {
            Logger.Debug("Right Mouse down");
        }
        if (wmMouse == MouseMessage.WM_RBUTTONUP && RightButtonState == ButtonState.Down)
        {
            Logger.Debug("Right Mouse up");
        }
    }

    // Pass the hook information to the next hook procedure in chain
    return NativeMethods.CallNextHookEx(_hGlobalLlMouseHook, nCode, wParam, lParam);
}

与所有直接的Windows调用一样,代码变得不必要地长。但您唯一需要做的就是致电SetUpHook并提供您自己的LowLevelMouseProc版本。

编辑:有更短的版本可以执行此操作。但是此方法允许您捕获全局鼠标事件。不只是发布到您的窗口的事件。系统范围内的所有鼠标事件都将通过管道传输到LowLevelMouseProc