通过user32.dll中的SendInput发送密钥

时间:2012-10-06 15:37:29

标签: c# sendkeys user32 sendinput

我使用this board作为键盘用于演示目的。

无论如何,长话短说一切都很好,除了很少的情况。我使用位于user32.dll中的 SendInput 函数发送击键。

所以我的程序看起来像:

static void Main(string[] args)
{
    Console.Write("Press enter an on the next secont the key combination shift+end will be send");
    Console.Read();

    Thread.Sleep(1000);

    SendKeyDown(KeyCode.SHIFT);
    SendKeyPress(KeyCode.END);
    SendKeyUp(KeyCode.SHIFT);

    Console.Read(); 
    Console.Read();
}

[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputStructure);

/// <summary>
/// simulate key press
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyPress(KeyCode keyCode)
{
    INPUT input = new INPUT {
        Type = 1
    };
    input.Data.Keyboard = new KEYBDINPUT() {
        Vk = (ushort)keyCode,
        Scan = 0,
        Flags = 0,
        Time = 0,
        ExtraInfo = IntPtr.Zero,
    };

    INPUT input2 = new INPUT {
        Type = 1
    };
    input2.Data.Keyboard = new KEYBDINPUT() {
        Vk = (ushort)keyCode,
        Scan = 0,
        Flags = 2,
        Time = 0,
        ExtraInfo = IntPtr.Zero
    };
    INPUT[] inputs = new INPUT[] { input, input2 };
    if (SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
        throw new Exception();            
}

/// <summary>
/// Send a key down and hold it down until sendkeyup method is called
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyDown(KeyCode keyCode)
{
    INPUT input = new INPUT{
        Type = 1
    };
    input.Data.Keyboard = new KEYBDINPUT();
    input.Data.Keyboard.Vk = (ushort)keyCode;
    input.Data.Keyboard.Scan = 0;
    input.Data.Keyboard.Flags = 0;
    input.Data.Keyboard.Time = 0;
    input.Data.Keyboard.ExtraInfo = IntPtr.Zero;
    INPUT[] inputs = new INPUT[] { input };
    if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
    {
        throw new Exception();
    }
}

/// <summary>
/// Release a key that is being hold down
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyUp(KeyCode keyCode)
{
    INPUT input = new INPUT {
        Type = 1
    };
    input.Data.Keyboard = new KEYBDINPUT();
    input.Data.Keyboard.Vk = (ushort)keyCode;
    input.Data.Keyboard.Scan = 0;
    input.Data.Keyboard.Flags = 2;
    input.Data.Keyboard.Time = 0;
    input.Data.Keyboard.ExtraInfo = IntPtr.Zero;
    INPUT[] inputs = new INPUT[] { input };
    if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
        throw new Exception();

}

以下是我在网上找到的那些方法使用的结构以及关键代码:(注意它看起来像很多代码,因为Enum中有很多关键代码)

    /// <summary>
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct INPUT
    {
        public uint Type;
        public MOUSEKEYBDHARDWAREINPUT Data;
    }

    /// <summary>
    /// http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/f0e82d6e-4999-4d22-b3d3-32b25f61fb2a
    /// </summary>
    [StructLayout(LayoutKind.Explicit)]
    internal struct MOUSEKEYBDHARDWAREINPUT
    {
        [FieldOffset(0)]
        public HARDWAREINPUT Hardware;
        [FieldOffset(0)]
        public KEYBDINPUT Keyboard;
        [FieldOffset(0)]
        public MOUSEINPUT Mouse;
    }

    /// <summary>
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct HARDWAREINPUT
    {
        public uint Msg;
        public ushort ParamL;
        public ushort ParamH;
    }

    /// <summary>
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct KEYBDINPUT
    {
        public ushort Vk;
        public ushort Scan;
        public uint Flags;
        public uint Time;
        public IntPtr ExtraInfo;
    }

    /// <summary>
    /// http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/2abc6be8-c593-4686-93d2-89785232dacd
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct MOUSEINPUT
    {
        public int X;
        public int Y;
        public uint MouseData;
        public uint Flags;
        public uint Time;
        public IntPtr ExtraInfo;
    }

    public enum KeyCode : ushort
    {
        #region Media

        /// <summary>
        /// Next track if a song is playing
        /// </summary>
        MEDIA_NEXT_TRACK = 0xb0,

        /// <summary>
        /// Play pause
        /// </summary>
        MEDIA_PLAY_PAUSE = 0xb3,

        /// <summary>
        /// Previous track
        /// </summary>
        MEDIA_PREV_TRACK = 0xb1,

        /// <summary>
        /// Stop
        /// </summary>
        MEDIA_STOP = 0xb2,

        #endregion

        #region math

        /// <summary>Key "+"</summary>
        ADD = 0x6b,
        /// <summary>
        /// "*" key
        /// </summary>
        MULTIPLY = 0x6a,

        /// <summary>
        /// "/" key
        /// </summary>
        DIVIDE = 0x6f,

        /// <summary>
        /// Subtract key "-"
        /// </summary>
        SUBTRACT = 0x6d,

        #endregion

        #region Browser
        /// <summary>
        /// Go Back
        /// </summary>
        BROWSER_BACK = 0xa6,
        /// <summary>
        /// Favorites
        /// </summary>
        BROWSER_FAVORITES = 0xab,
        /// <summary>
        /// Forward
        /// </summary>
        BROWSER_FORWARD = 0xa7,
        /// <summary>
        /// Home
        /// </summary>
        BROWSER_HOME = 0xac,
        /// <summary>
        /// Refresh
        /// </summary>
        BROWSER_REFRESH = 0xa8,
        /// <summary>
        /// browser search
        /// </summary>
        BROWSER_SEARCH = 170,
        /// <summary>
        /// Stop
        /// </summary>
        BROWSER_STOP = 0xa9,
        #endregion

        #region Numpad numbers
        /// <summary>
        /// 
        /// </summary>
        NUMPAD0 = 0x60,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD1 = 0x61,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD2 = 0x62,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD3 = 0x63,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD4 = 100,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD5 = 0x65,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD6 = 0x66,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD7 = 0x67,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD8 = 0x68,
        /// <summary>
        /// 
        /// </summary>
        NUMPAD9 = 0x69,

        #endregion

        #region Fkeys
        /// <summary>
        /// F1
        /// </summary>
        F1 = 0x70,
        /// <summary>
        /// F10
        /// </summary>
        F10 = 0x79,
        /// <summary>
        /// 
        /// </summary>
        F11 = 0x7a,
        /// <summary>
        /// 
        /// </summary>
        F12 = 0x7b,
        /// <summary>
        /// 
        /// </summary>
        F13 = 0x7c,
        /// <summary>
        /// 
        /// </summary>
        F14 = 0x7d,
        /// <summary>
        /// 
        /// </summary>
        F15 = 0x7e,
        /// <summary>
        /// 
        /// </summary>
        F16 = 0x7f,
        /// <summary>
        /// 
        /// </summary>
        F17 = 0x80,
        /// <summary>
        /// 
        /// </summary>
        F18 = 0x81,
        /// <summary>
        /// 
        /// </summary>
        F19 = 130,
        /// <summary>
        /// 
        /// </summary>
        F2 = 0x71,
        /// <summary>
        /// 
        /// </summary>
        F20 = 0x83,
        /// <summary>
        /// 
        /// </summary>
        F21 = 0x84,
        /// <summary>
        /// 
        /// </summary>
        F22 = 0x85,
        /// <summary>
        /// 
        /// </summary>
        F23 = 0x86,
        /// <summary>
        /// 
        /// </summary>
        F24 = 0x87,
        /// <summary>
        /// 
        /// </summary>
        F3 = 0x72,
        /// <summary>
        /// 
        /// </summary>
        F4 = 0x73,
        /// <summary>
        /// 
        /// </summary>
        F5 = 0x74,
        /// <summary>
        /// 
        /// </summary>
        F6 = 0x75,
        /// <summary>
        /// 
        /// </summary>
        F7 = 0x76,
        /// <summary>
        /// 
        /// </summary>
        F8 = 0x77,
        /// <summary>
        /// 
        /// </summary>
        F9 = 120,

        #endregion

        #region Other
        /// <summary>
        /// 
        /// </summary>
        OEM_1 = 0xba,
        /// <summary>
        /// 
        /// </summary>
        OEM_102 = 0xe2,
        /// <summary>
        /// 
        /// </summary>
        OEM_2 = 0xbf,
        /// <summary>
        /// 
        /// </summary>
        OEM_3 = 0xc0,
        /// <summary>
        /// 
        /// </summary>
        OEM_4 = 0xdb,
        /// <summary>
        /// 
        /// </summary>
        OEM_5 = 220,
        /// <summary>
        /// 
        /// </summary>
        OEM_6 = 0xdd,
        /// <summary>
        /// 
        /// </summary>
        OEM_7 = 0xde,
        /// <summary>
        /// 
        /// </summary>
        OEM_8 = 0xdf,
        /// <summary>
        /// 
        /// </summary>
        OEM_CLEAR = 0xfe,
        /// <summary>
        /// 
        /// </summary>
        OEM_COMMA = 0xbc,
        /// <summary>
        /// 
        /// </summary>
        OEM_MINUS = 0xbd,
        /// <summary>
        /// 
        /// </summary>
        OEM_PERIOD = 190,
        /// <summary>
        /// 
        /// </summary>
        OEM_PLUS = 0xbb,

        #endregion

        #region KEYS

        /// <summary>
        /// 
        /// </summary>
        KEY_0 = 0x30,
        /// <summary>
        /// 
        /// </summary>
        KEY_1 = 0x31,
        /// <summary>
        /// 
        /// </summary>
        KEY_2 = 50,
        /// <summary>
        /// 
        /// </summary>
        KEY_3 = 0x33,
        /// <summary>
        /// 
        /// </summary>
        KEY_4 = 0x34,
        /// <summary>
        /// 
        /// </summary>
        KEY_5 = 0x35,
        /// <summary>
        /// 
        /// </summary>
        KEY_6 = 0x36,
        /// <summary>
        /// 
        /// </summary>
        KEY_7 = 0x37,
        /// <summary>
        /// 
        /// </summary>
        KEY_8 = 0x38,
        /// <summary>
        /// 
        /// </summary>
        KEY_9 = 0x39,
        /// <summary>
        /// 
        /// </summary>
        KEY_A = 0x41,
        /// <summary>
        /// 
        /// </summary>
        KEY_B = 0x42,
        /// <summary>
        /// 
        /// </summary>
        KEY_C = 0x43,
        /// <summary>
        /// 
        /// </summary>
        KEY_D = 0x44,
        /// <summary>
        /// 
        /// </summary>
        KEY_E = 0x45,
        /// <summary>
        /// 
        /// </summary>
        KEY_F = 70,
        /// <summary>
        /// 
        /// </summary>
        KEY_G = 0x47,
        /// <summary>
        /// 
        /// </summary>
        KEY_H = 0x48,
        /// <summary>
        /// 
        /// </summary>
        KEY_I = 0x49,
        /// <summary>
        /// 
        /// </summary>
        KEY_J = 0x4a,
        /// <summary>
        /// 
        /// </summary>
        KEY_K = 0x4b,
        /// <summary>
        /// 
        /// </summary>
        KEY_L = 0x4c,
        /// <summary>
        /// 
        /// </summary>
        KEY_M = 0x4d,
        /// <summary>
        /// 
        /// </summary>
        KEY_N = 0x4e,
        /// <summary>
        /// 
        /// </summary>
        KEY_O = 0x4f,
        /// <summary>
        /// 
        /// </summary>
        KEY_P = 80,
        /// <summary>
        /// 
        /// </summary>
        KEY_Q = 0x51,
        /// <summary>
        /// 
        /// </summary>
        KEY_R = 0x52,
        /// <summary>
        /// 
        /// </summary>
        KEY_S = 0x53,
        /// <summary>
        /// 
        /// </summary>
        KEY_T = 0x54,
        /// <summary>
        /// 
        /// </summary>
        KEY_U = 0x55,
        /// <summary>
        /// 
        /// </summary>
        KEY_V = 0x56,
        /// <summary>
        /// 
        /// </summary>
        KEY_W = 0x57,
        /// <summary>
        /// 
        /// </summary>
        KEY_X = 0x58,
        /// <summary>
        /// 
        /// </summary>
        KEY_Y = 0x59,
        /// <summary>
        /// 
        /// </summary>
        KEY_Z = 90,

        #endregion

        #region volume
        /// <summary>
        /// Decrese volume
        /// </summary>
        VOLUME_DOWN = 0xae,

        /// <summary>
        /// Mute volume
        /// </summary>
        VOLUME_MUTE = 0xad,

        /// <summary>
        /// Increase volue
        /// </summary>
        VOLUME_UP = 0xaf,

        #endregion


        /// <summary>
        /// Take snapshot of the screen and place it on the clipboard
        /// </summary>
        SNAPSHOT = 0x2c,

        /// <summary>Send right click from keyboard "key that is 2 keys to the right of space bar"</summary>
        RightClick = 0x5d,

        /// <summary>
        /// Go Back or delete
        /// </summary>
        BACKSPACE = 8,

        /// <summary>
        /// Control + Break "When debuging if you step into an infinite loop this will stop debug"
        /// </summary>
        CANCEL = 3,
        /// <summary>
        /// Caps lock key to send cappital letters
        /// </summary>
        CAPS_LOCK = 20,
        /// <summary>
        /// Ctlr key
        /// </summary>
        CONTROL = 0x11,

        /// <summary>
        /// Alt key
        /// </summary>
        ALT = 18,

        /// <summary>
        /// "." key
        /// </summary>
        DECIMAL = 110,

        /// <summary>
        /// Delete Key
        /// </summary>
        DELETE = 0x2e,


        /// <summary>
        /// Arrow down key
        /// </summary>
        DOWN = 40,

        /// <summary>
        /// End key
        /// </summary>
        END = 0x23,

        /// <summary>
        /// Escape key
        /// </summary>
        ESC = 0x1b,

        /// <summary>
        /// Home key
        /// </summary>
        HOME = 0x24,

        /// <summary>
        /// Insert key
        /// </summary>
        INSERT = 0x2d,

        /// <summary>
        /// Open my computer
        /// </summary>
        LAUNCH_APP1 = 0xb6,
        /// <summary>
        /// Open calculator
        /// </summary>
        LAUNCH_APP2 = 0xb7,

        /// <summary>
        /// Open default email in my case outlook
        /// </summary>
        LAUNCH_MAIL = 180,

        /// <summary>
        /// Opend default media player (itunes, winmediaplayer, etc)
        /// </summary>
        LAUNCH_MEDIA_SELECT = 0xb5,

        /// <summary>
        /// Left control
        /// </summary>
        LCONTROL = 0xa2,

        /// <summary>
        /// Left arrow
        /// </summary>
        LEFT = 0x25,

        /// <summary>
        /// Left shift
        /// </summary>
        LSHIFT = 160,

        /// <summary>
        /// left windows key
        /// </summary>
        LWIN = 0x5b,


        /// <summary>
        /// Next "page down"
        /// </summary>
        PAGEDOWN = 0x22,

        /// <summary>
        /// Num lock to enable typing numbers
        /// </summary>
        NUMLOCK = 0x90,

        /// <summary>
        /// Page up key
        /// </summary>
        PAGE_UP = 0x21,

        /// <summary>
        /// Right control
        /// </summary>
        RCONTROL = 0xa3,

        /// <summary>
        /// Return key
        /// </summary>
        ENTER = 13,

        /// <summary>
        /// Right arrow key
        /// </summary>
        RIGHT = 0x27,

        /// <summary>
        /// Right shift
        /// </summary>
        RSHIFT = 0xa1,

        /// <summary>
        /// Right windows key
        /// </summary>
        RWIN = 0x5c,

        /// <summary>
        /// Shift key
        /// </summary>
        SHIFT = 0x10,

        /// <summary>
        /// Space back key
        /// </summary>
        SPACE_BAR = 0x20,

        /// <summary>
        /// Tab key
        /// </summary>
        TAB = 9,

        /// <summary>
        /// Up arrow key
        /// </summary>
        UP = 0x26,

    }

所以现在我的问题是为什么当我发送该组合键时,我得到的结果与在真正的键盘上进行时不一样? 98%的事情都有效。例如,我能够做到:

    SendKeyDown(KeyCode.SHIFT);
    SendKeyPress(KeyCode.KEY_A );
    SendKeyUp(KeyCode.SHIFT);

这将发送一个大写A.

我应该使用不同的库吗?

我喜欢这种方法的原因是因为我事先并不知道用户是否会发送密钥组合例如在Windows窗体中如果我这样做:

System.Windows.Forms.SendKeys.SendWait("+{end}");将发送shift + end,但用户可能只想发送shift ...

3 个答案:

答案 0 :(得分:18)

您没有设置标志和扫描字段,具体取决于所需的击键,您需要正确设置这些以使操作系统正确识别键。

您可以考虑使用Input Simulator库,因为它已经完成了您想要的操作,您无需重新创建轮子。只要确保通过论坛查看,因为那里需要设置一些好的补丁,因为作者在2009年放弃了该项目。尽管如此,它仍然是一个很好的库。

答案 1 :(得分:1)

将键盘输入发送到窗口的另一种方法(我将其用于UI测试)是使用KEYBDINPUT中的Unicode替代方法,这样可以避免将每个字符映射到虚拟键:

public static void SendString(string inputStr)
{
    var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
    WinAPI.SetForegroundWindow(hWnd);           
    List<WinAPI.INPUT> keyList = new List<WinAPI.INPUT>();
    foreach (short c in inputStr)
    {
        switch (c)
        {
            case 8: // Translate \t to VK_TAB
                {
                    WinAPI.INPUT keyDown = new WinAPI.INPUT();
                    keyDown.type = 1; //Keyboard
                    keyDown.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_TAB;
                    keyDown.union.keyboardInput.dwFlags = 0;
                    keyDown.union.keyboardInput.wScan = 0; //use VirtualKey
                    keyList.Add(keyDown);
                    WinAPI.INPUT keyUp = new WinAPI.INPUT();
                    keyUp.type = 1; //Keyboard
                    keyUp.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_TAB;
                    keyUp.union.keyboardInput.dwFlags = 0x0002;
                    keyUp.union.keyboardInput.wScan = 0; //use VirtualKey
                    keyList.Add(keyUp);
                }
                break;
            case 10: // Translate \n to VK_RETURN
                {
                    WinAPI.INPUT keyDown = new WinAPI.INPUT();
                    keyDown.type = 1; //Keyboard
                    keyDown.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_RETURN;
                    keyDown.union.keyboardInput.dwFlags = 0;
                    keyDown.union.keyboardInput.wScan = 0; //use VirtualKey
                    keyList.Add(keyDown);
                    WinAPI.INPUT keyUp = new WinAPI.INPUT();
                    keyUp.type = 1; //Keyboard
                    keyUp.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_RETURN;
                    keyUp.union.keyboardInput.dwFlags = 0x0002; 
                    keyUp.union.keyboardInput.wScan = 0; //use VirtualKey
                    keyList.Add(keyUp);
                }
                break;                  
            default:
                {
                    WinAPI.INPUT keyDown = new WinAPI.INPUT();
                    keyDown.type = 1; //Keyboard
                    keyDown.union.keyboardInput.wVk = 0; //Use unicode
                    keyDown.union.keyboardInput.dwFlags = 0x0004; //Unicode Key Down
                    keyDown.union.keyboardInput.wScan = c;
                    keyList.Add(keyDown);
                    WinAPI.INPUT keyUp = new WinAPI.INPUT();
                    keyUp.type = 1; //Keyboard
                    keyUp.union.keyboardInput.wVk = 0; //Use unicode
                    keyUp.union.keyboardInput.dwFlags = 0x0004 | 0x0002; //Unicode Key Up
                    keyUp.union.keyboardInput.wScan = c;
                    keyList.Add(keyUp);
                }
                break;
        }
    }
    WinAPI.SendInput((uint)keyList.Count, keyList.ToArray(), Marshal.SizeOf(typeof(WinAPI.INPUT)));             
}

答案 2 :(得分:0)

您可以尝试一下。它适用于: Shift + A Ctrl + LShiftv + S ,< kbd> Ctrl + A

其他我没有尝试过但我认为您可以发送任何组合键

public static void MultiKeyPress(KeyCode[] keys){
    INPUT[] inputs = new INPUT[keys.Count() * 2];
    for(int a = 0; a < keys.Count(); ++a){
        for(int b = 0; b < 2; ++b){
            inputs[(b == 0) ? a : inputs.Count() - 1 - a].Type = 1;
            inputs[(b == 0) ? a : inputs.Count() - 1 - a].Data.Keyboard = new KEYBDINPUT() {
                Vk = (ushort)keys[a],
                Scan = 0,
                Flags = Convert.ToUInt32((b == 0)?0:2),
                Time = 0,
                ExtraInfo = IntPtr.Zero,
            };
        }
    }
    if (SendInput(Convert.ToUInt32(inputs.Count()), inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
        throw new Exception();
}
//call with this :
MultiKeyPress(new virtualInputs.KeyCode[] { KeyCode.LSHIFT, KeyCode.KEY_A });

/!\具有焦点的窗口将获得按键,因此您需要确保右侧窗口具有焦点