C# - 全局控制台应用程序鼠标和键盘控制?

C# - Global console application mouse and keyboard control?

有人知道如何做全局控制台应用程序控制器吗?
(我所说的控制器是指可以读取当前 keyboard/mouse 并模拟 keyboard/mouse 的东西)

P.S.: 如果你发现任何重复项,我认为它们中的大部分用于 winforms and/or 不是全局的,我已经看过了!

Windows Hooks!

windows 挂钩背后的概念是您的程序将自己插入到 OS 的消息链中,并且可以 read/intercept 这些消息。您还可以使用 windows 挂钩发送输入。

windows 钩子 api 是一个 C++ API 因此您将不得不使用一些 DLLImports 在您的项目中获取这些本机方法。如下图。这些是您接收消息所需的函数的导入。您还需要导入 SendInput 函数。

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

    [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, IntPtr wParam, IntPtr lParam);

您还需要一些常量和结构。

这些第一个常量将分别用于告诉 SetWindowsHookEx 函数您需要鼠标钩子或键盘钩子

    /// <summary>
    /// A lower level mouse hook
    /// </summary>
    public const int WH_MOUSE_LL = 14;
    /// <summary>
    /// A lower level keyboard hook
    /// </summary>
    public const int WH_KEYBOARD_LL = 13;

这些常量将用于识别鼠标正在做什么(单击、移动等)

    /// <summary>
    /// Indicates left mouse button down
    /// </summary>
    public const int WM_LBUTTONDOWN = 0x0201;
    /// <summary>
    /// Indicates left mouse button up
    /// </summary>
    public const int WM_LBUTTONUP = 0x0202;
    /// <summary>
    /// Indicates right mouse button down
    /// </summary>
    public const int WM_RBUTTONDOWN = 0x0204;
    /// <summary>
    /// Indicates the mouse is moving
    /// </summary>
    public const int WM_MOUSEMOVE = 0x0200;

此枚举将允许您识别键盘输入。还有本地方法可以为您进行这种转换。我可能遗漏了一些键,但您可以在 MSDN 上找到更完整的枚举列表。

public enum SystemHotKeys
    {
        [Description("ENTER key")]
        ENTER = 0x0D,

        [Description("SPACEBAR")]
        SPACE = 0x20,

        [Description("DOWN ARROW key")]
        DOWN = 0x28,

        [Description("Numeric keypad 0 key")]
        NUMPAD_ZERO = 0x60,

        [Description("Numeric keypad 1 key")]
        NUMPAD_ONE = 0x61,

        [Description("Numeric keypad 2 key")]
        NUMPAD_TWO = 0x62,

        [Description("Numeric keypad 3 key")]
        NUMPAD_THREE = 0x63,

        [Description("Numeric keypad 4 key")]
        NUMPAD_FOUR = 0x64,

        [Description("Numeric keypad 5 key")]
        NUMPAD_FIVE = 0x65,

        [Description("Numeric keypad 6 key")]
        NUMPAD_SIX = 0x66,

        [Description("Numeric keypad 7 key")]
        NUMPAD_SEVEN = 0x67,

        [Description("Numeric keypad 8 key")]
        NUMPAD_EIGHT = 0x68,

        [Description("Numeric keypad 9 key")]
        NUMPAD_NINE = 0x69,

        [Description("F4 key")]
        F4 = 0x73,

        [Description("F6 key")]
        F6 = 0x75
    }

    public enum WindowsVirtualKey
    {
        [Description("BACKSPACE key")]
        BACKSPACE = 0x08,

        [Description("TAB key")]
        TAB = 0x09,

        [Description("ENTER key")]
        ENTER = 0x0D,

        [Description("SPACEBAR")]
        SPACE = 0x20,

        [Description("LEFT ARROW key")]
        LEFT = 0x25,

        [Description("UP ARROW key")]
        UP = 0x26,

        [Description("RIGHT ARROW key")]
        RIGHT = 0x27,

        [Description("DOWN ARROW key")]
        DOWN = 0x28,

        [Description("0 key")]
        ZERO = 0x30,

        [Description("1 key")]
        ONE = 0x31,

        [Description("2 key")]
        TWO = 0x32,

        [Description("3 key")]
        THREE = 0x33,

        [Description("4 key")]
        FOUR = 0x34,

        [Description("5 key")]
        FIVE = 0x35,

        [Description("6 key")]
        SIX = 0x36,

        [Description("7 key")]
        SEVEN = 0x37,

        [Description("8 key")]
        EIGHT = 0x38,

        [Description("9 key")]
        NINE = 0x39,

        [Description("A key")]
        A = 0x41,

        [Description("B key")]
        B = 0x42,

        [Description("C key")]
        C = 0x43,

        [Description("D key")]
        D = 0x44,

        [Description("E key")]
        E = 0x45,

        [Description("F key")]
        F = 0x46,

        [Description("G key")]
        G = 0x47,

        [Description("H key")]
        H = 0x48,

        [Description("I key")]
        I = 0x49,

        [Description("J key")]
        J = 0x4A,

        [Description("K key")]
        K = 0x4B,

        [Description("L key")]
        L = 0x4C,

        [Description("M key")]
        M = 0x4D,

        [Description("N key")]
        N = 0x4E,

        [Description("O key")]
        O = 0x4F,

        [Description("P key")]
        P = 0x50,

        [Description("Q key")]
        Q = 0x51,

        [Description("R key")]
        R = 0x52,

        [Description("S key")]
        S = 0x53,

        [Description("T key")]
        T = 0x54,

        [Description("U key")]
        U = 0x55,

        [Description("V key")]
        V = 0x56,

        [Description("W key")]
        W = 0x57,

        [Description("X key")]
        X = 0x58,

        [Description("Y key")]
        Y = 0x59,

        [Description("Z key")]
        Z = 0x5A,

        [Description("Numeric keypad 0 key")]
        NUMPAD_ZERO = 0x60,

        [Description("Numeric keypad 1 key")]
        NUMPAD_ONE = 0x61,

        [Description("Numeric keypad 2 key")]
        NUMPAD_TWO = 0x62,

        [Description("Numeric keypad 3 key")]
        NUMPAD_THREE = 0x63,

        [Description("Numeric keypad 4 key")]
        NUMPAD_FOUR = 0x64,

        [Description("Numeric keypad 5 key")]
        NUMPAD_FIVE = 0x65,

        [Description("Numeric keypad 6 key")]
        NUMPAD_SIX = 0x66,

        [Description("Numeric keypad 7 key")]
        NUMPAD_SEVEN = 0x67,

        [Description("Numeric keypad 8 key")]
        NUMPAD_EIGHT = 0x68,

        [Description("Numeric keypad 9 key")]
        NUMPAD_NINE = 0x69,

        [Description("F1 key")]
        F1 = 0x70,

        [Description("F2 key")]
        F2 = 0x71,

        [Description("F3 key")]
        F3 = 0x72,

        [Description("F4 key")]
        F4 = 0x73,

        [Description("F5 key")]
        F5 = 0x74,

        [Description("F6 key")]
        F6 = 0x75,

        [Description("F7 key")]
        F7 = 0x76,

        [Description("F8 key")]
        F8 = 0x77,

        [Description("F9 key")]
        F9 = 0x78,

        [Description("F10 key")]
        F10 = 0x79,

        [Description("F11 key")]
        F11 = 0x7A,

        [Description("F12 key")]
        F12 = 0x7B
    }

您还需要一些结构才能在 C++ API 和您的代码之间进行通信。

        //Declare the wrapper managed POINT class.
    [StructLayout(LayoutKind.Sequential)]
    public class POINT
    {
        public int x;
        public int y;
    }

    //Declare the wrapper managed MouseHookStruct class.
    [StructLayout(LayoutKind.Sequential)]
    public class MouseHookStruct
    {
        public POINT pt;
        public int hwnd;
        public int wHitTestCode;
        public int dwExtraInfo;
    }

从那里您可以使用 SetWindowsHookEx 设置鼠标或键盘的 windows 挂钩。 (你一次可以有多个钩子)你将传递一个代表你的 HookProcedure。只要触发 windows 消息,就会调用此过程。要使挂钩成为全局挂钩,您需要将 IntPtr.Zero 作为最后一个参数传递给 SetWindowsHookEx。这个函数returns一个钩子句柄。您需要保存它并在以后使用它来解开。

要停止挂钩,您将调用 UnhookWindowsHookEx 并传入挂钩句柄。

既然我们已经介绍了从键盘和鼠标听到消息,我将讨论发送消息。

为此,您将使用相同 API 中的 SendInput 方法。但是,您将再次需要一些结构来帮助您进行交流。

        [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBOARDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public uint uMsg;
        public ushort wParamL;
        public ushort wParamH;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public InputUnion union;
    }

您将使用需要发送的信息填充这些结构。将它们组合在一个 InputUnion 中,然后将它们放入一个 INPUT 结构中以发送到系统。或多或少,您将使用与上述相同的常量来告诉键盘或鼠标发送特定信号的内容和位置。然而,不幸的是,这都是基于 x 和 y 坐标的,因此应始终根据应用程序在屏幕上的大小和位置来读取这些坐标。

挂钩 API 参考可以在这里找到:https://msdn.microsoft.com/en-us/library/windows/desktop/ms632589(v=vs.85).aspx

可以在此处找到有关如何使用挂钩的概述:https://msdn.microsoft.com/en-us/library/windows/desktop/ms644960(v=vs.85).aspx

可在此处找到发送输入参考:https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx

希望对您有所帮助!如果您对我的回答有疑问,请随时给我留言。我经常使用 windows 钩子。