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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12761169/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:21:51  来源:igfitidea点击:

Send keys through SendInput in user32.dll

c#sendkeysuser32sendinput

提问by Tono Nam

I am using this boardas a keyboard for demo purposes.

将此板用作键盘用于演示目的。

Anyways to make the long story short everything works fine except for very few cases. I send keystrokes with the SendInputfunction located in user32.dll.

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

So my program looks like:

所以我的程序看起来像:

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();

}

And here are the structs that I found online that those methods use and also the key codes: (note it looks like a lot of code and is because there are a lot of key codes in an Enum)

以下是我在网上找到的这些方法使用的结构以及关键代码:(注意它看起来像很多代码,因为 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,

    }

So now my question iswhy when I send that key combination I do not get the same results as when I do it on a real keyboard? 98% of the things work. For example I am able to do:

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

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

And that will send a capital A.

这将发送一个大写字母 A。

Should I use a different library?

我应该使用不同的库吗?

The reason why I like this approach is because I do not know in advance if the user will send a key combinationFor example in windows forms If I do:

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

System.Windows.Forms.SendKeys.SendWait("+{end}");that will send shift + end but maybe the user just wants to send shift...

System.Windows.Forms.SendKeys.SendWait("+{end}");这将发送 shift + end 但也许用户只是想发送 shift ...

采纳答案by Kevin Cook

You aren't setting the flags and scan fields, depending on the keystrokes desired, you will need to set these correctly to get the OS to recognize the keys correctly.

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

You might consider using the Input Simulatorlibrary, since it already does what you want and you don't have to recreate the wheel. Just make sure to look through the forums as there are some good patches in there that need to be set, since the author abandoned the project in 2009. It's a good library nonetheless.

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

回答by user3902302

Another way of sending keyboard input to a window (I use this for UI testing) is to use the Unicode alternative in KEYBDINPUTwhich saves you from mapping each character to the virtual key:

将键盘输入发送到窗口的另一种方法(我将其用于 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)));             
}

回答by Grez

You could try this. It works for : Shift+ A, Ctrl+ LShiftv+ S, Ctrl+ A

你可以试试这个。它适用于:Shift+ ACtrl+ LShiftv+ SCtrl+A

The others I didn't try but I think you could send any key combination

其他的我没试过,但我想你可以发送任何组合键

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 });

/!\ the window that have the focus will get the keypress so you need to make sure the right window have the focus

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