c# - 有什么方法可以像我挂接键盘键一样全局挂接鼠标操作吗?

标签 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)
        {

        }
    }
}

我想知道是否有一种方法可以使用该类或其他一些方法来连接鼠标?例如,当我单击鼠标中键时。或者,如果我滚动鼠标滚轮,然后给出一条消息,我滚动了滚轮,但滚轮不是那么重要,更多的是左、右、中按钮。

最佳答案

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

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
}

要使用它,您必须注册鼠标 Hook 。 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");
    }
}

清除鼠标钩子(Hook):

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

关于c# - 有什么方法可以像我挂接键盘键一样全局挂接鼠标操作吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44990335/

相关文章:

winforms - ReportViewer - LocalReport - 合并报告?

UserControl 的自定义 Text 属性的 C# 自定义 TextChanged 事件处理程序?

c# - 使用谷歌翻译和我自己的翻译

c# 如何转换泛型类型

c# - 如何使用 C# 从 PDF 或 XPS 中提取具有格式的文本?

c# - 如何为可能为 null 的对象执行 ToString?

c# - 更改不会永久保存到 SQL Server Compact 版本中

c# - 当 .NET 控制台应用程序在 Console.ReadLine() 上阻塞时会发生什么?

c# - C#4.0 编译器生成的 IL 可以在 CLR 2.0 上运行吗?

c# - 如何关闭 this.form