none
KEYEVENTF_KEYUP not releasing keys as expected

    Question

  • Hi,

    I'm using keybd_event from user32.dll to simulate Ctrl+Alt+Up keystrokes. Here is my code:


    public const int KEYEVENTF_EXTENDEDKEY = 0x0001; //Key down flag
            public const int KEYEVENTF_KEYUP = 0x0002; //Key up flag
            public const int VK_LCONTROL = 0xA2; //Left Control key code
            public const int VK_MENU = 0x12; //Alt key code
            public const int VK_UP = 0x26; //Up cursor key code
    
            [DllImport("user32.dll", SetLastError = true)]
            static extern void keybd_event(byte bVk, byte bScan, 
                int dwFlags, int dwExtraInfo);
    
            static void Main(string[] args)
            {
                keybd_event(VK_LCONTROL, 0x9D, KEYEVENTF_EXTENDEDKEY, 0); //lctrl down
                keybd_event(VK_MENU, 0xB8, KEYEVENTF_EXTENDEDKEY, 0); //lalt down
                keybd_event(VK_UP, 0, KEYEVENTF_EXTENDEDKEY, 0); //up cursor down
    
                keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0); //up cursor up
                keybd_event(VK_MENU, 0xB8, KEYEVENTF_KEYUP, 0); //lalt up
                keybd_event(VK_LCONTROL, 0x9D, KEYEVENTF_KEYUP, 0); //lctrl up
            }

    It works, at least the KEYEVENTF_EXTENDEDKEY part does, but KEYEVENTF_KEYUP doesn't seem to. After my program exits, it is as if the Ctrl and Alt keys are still held down. This produces all kinds of weird behaviour as you can imagine, and my only option is a system restart.

    Does anyone have any idea how I can properly release these keys? Running Windows 7 by the way, .NET 4.


    James Finch (MCDST) -- Please vote as helpful if you found this post helpful, or mark as answer if it answered your question.


    • Edited by Woohoooo Friday, September 13, 2013 9:50 AM typo
    Friday, September 13, 2013 9:48 AM

Answers

  • var just has the compiler figure out the type rather than you having to specify one explicitly.  The variable is still strongly typed and Intellisense will actually tell you the type it thinks it is for any usages after the declaration.

    I'd wager that the parameters going in aren't lining up.  I provided a single struct to combine everything but that wouldn't work for x64 because of padding.  I also didn't include the other 2 types in the union so the size is probably wrong.  Here's the updated struct layout code.

    static void Main ( string[] args )
    {
        var inputs = new INPUT[] {
    						INPUT.VirtualKeyDown(Keys.LWin),
    						INPUT.VirtualKeyDown(Keys.R),
    
    						INPUT.VirtualKeyUp(Keys.R),								
    						INPUT.VirtualKeyUp(Keys.LWin),
    	};
    
        var results = SendInput(inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
        var error = Marshal.GetLastWin32Error();
    }
    
    private enum INPUTTYPE : uint
    {
        Keyboard = 1
    }
    
    private enum KEYEVENTF : uint
    {
        KeyUp = 2,
    }
    
    [DllImport("User32.dll", SetLastError = true)]
    private static extern uint SendInput ( int nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] inputs, int cbSize );
            
    [StructLayout(LayoutKind.Sequential)]
    private struct INPUT
    {
        public INPUTTYPE type;
        public INPUT_U u;
                
        public static INPUT VirtualKeyDown ( Keys keyCode )
        {
            var input = new INPUT() { type = INPUTTYPE.Keyboard };
            input.u.ki = new KEYBDINPUT() { virtualKey = (ushort)keyCode };
    
            return input;
        }
    
        public static INPUT VirtualKeyUp ( Keys keyCode )
        {
            var input = new INPUT() { type = INPUTTYPE.Keyboard };
            input.u.ki = new KEYBDINPUT() { virtualKey = (ushort)keyCode, flags = KEYEVENTF.KeyUp };
    
            return input;
        }
    }
    
    [StructLayout(LayoutKind.Explicit)]
    private struct INPUT_U
    {
        [FieldOffset(0)]
        public KEYBDINPUT ki;
    
        [FieldOffset(0)]
        public MOUSEINPUT mi;
    
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBDINPUT
    {           
        public ushort virtualKey;
        public ushort scanCode;
        public KEYEVENTF flags;
        public uint time;
    
        public IntPtr extraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr extraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParam;
        public short lParam;
    }

    I'd recommend that you take a look at this project on Codeplex which seems to provide a much better implementation.

    • Marked as answer by Woohoooo Tuesday, September 17, 2013 8:35 AM
    Saturday, September 14, 2013 11:08 PM
    Moderator
  • I see you got good advice to use SendInput instead, but in case you're curious about your original approach, what you missed is that the flag words are flag bits that can be combined.  When you release the key, you need to specify BOTH flags:

        keybd_event(VK_UP, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);


    Tim Roberts, VC++ MVP Providenza & Boekelheide, Inc.

    • Marked as answer by Woohoooo Tuesday, September 17, 2013 8:35 AM
    Sunday, September 15, 2013 3:36 AM

All replies

  • Is there any particular reason why you have to use keybd_event rather than the more modern SendInput?  SendInput allows you to send a series of keyboard inputs and they'll all be processed together.  The only big downside is that any existing modifiers are still in effect so if you need to reset them before sending the input you have to call a separate method first.  Here's my basic attempt to replicate your code using SendInput.  To make it more full featured I'd probably hide all the native logic in a standard class and expose .NET-specific types but you get the gist of it.

    static void Main ( string[] args )
    {
    	var inputs = new KEYBOARDINPUT[] {
    						KEYBOARDINPUT.VirtualKeyDown(VirtualKey.LControl),
    						KEYBOARDINPUT.VirtualKeyDown(VirtualKey.Alt),
    						KEYBOARDINPUT.VirtualKeyDown(VirtualKey.ArrowUp),
    
    						KEYBOARDINPUT.VirtualKeyUp(VirtualKey.ArrowUp),								
    						KEYBOARDINPUT.VirtualKeyUp(VirtualKey.Alt),
    						KEYBOARDINPUT.VirtualKeyUp(VirtualKey.LControl),								
    	};
    
    	var results = SendInput(inputs.Length, inputs, Marshal.SizeOf(typeof(KEYBOARDINPUT)));
    }
    
    //Might already exist somewhere
    public enum VirtualKey
    {
    	LButton = 1,
    	RButton = 2,
    
    	LControl = 0xA2,
    	Alt = 0x12,
    	ArrowUp = 0x26,
    }
    
    [DllImport("User32.dll", SetLastError = true, ExactSpelling=true)]
    private static extern uint SendInput ( int nInputs, [MarshalAs(UnmanagedType.LPArray)] KEYBOARDINPUT[] inputs, int cbSize );
    
    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBOARDINPUT
    {
    	public static KEYBOARDINPUT VirtualKeyDown ( VirtualKey keyCode )
    	{
    		return new KEYBOARDINPUT() { type = 1, virtualKey = (ushort)keyCode };
    	}
    
    	public static KEYBOARDINPUT VirtualKeyUp ( VirtualKey keyCode )
    	{
    		return new KEYBOARDINPUT() { type = 1, virtualKey = (ushort)keyCode, flags = 2 };
    	}
    
    	public uint type;
    
    	public ushort virtualKey;
    	public ushort scanCode;
    	public uint flags;
    	public uint time;
    
    	public IntPtr extraInfo;
    }

    Michael Taylor
    http://msmvps.com/blogs/p3net

    Friday, September 13, 2013 6:03 PM
    Moderator
  • Hi Michael,

    Thanks for the reply. I did not know of this method. The method looks sound, but the code produces no effect on my test machine. Could it be a coding error? The var is bugging me for some reason.


    James Finch (MCDST) -- Please vote as helpful if you found this post helpful, or mark as answer if it answered your question.

    Saturday, September 14, 2013 8:57 PM
  • var just has the compiler figure out the type rather than you having to specify one explicitly.  The variable is still strongly typed and Intellisense will actually tell you the type it thinks it is for any usages after the declaration.

    I'd wager that the parameters going in aren't lining up.  I provided a single struct to combine everything but that wouldn't work for x64 because of padding.  I also didn't include the other 2 types in the union so the size is probably wrong.  Here's the updated struct layout code.

    static void Main ( string[] args )
    {
        var inputs = new INPUT[] {
    						INPUT.VirtualKeyDown(Keys.LWin),
    						INPUT.VirtualKeyDown(Keys.R),
    
    						INPUT.VirtualKeyUp(Keys.R),								
    						INPUT.VirtualKeyUp(Keys.LWin),
    	};
    
        var results = SendInput(inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
        var error = Marshal.GetLastWin32Error();
    }
    
    private enum INPUTTYPE : uint
    {
        Keyboard = 1
    }
    
    private enum KEYEVENTF : uint
    {
        KeyUp = 2,
    }
    
    [DllImport("User32.dll", SetLastError = true)]
    private static extern uint SendInput ( int nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT[] inputs, int cbSize );
            
    [StructLayout(LayoutKind.Sequential)]
    private struct INPUT
    {
        public INPUTTYPE type;
        public INPUT_U u;
                
        public static INPUT VirtualKeyDown ( Keys keyCode )
        {
            var input = new INPUT() { type = INPUTTYPE.Keyboard };
            input.u.ki = new KEYBDINPUT() { virtualKey = (ushort)keyCode };
    
            return input;
        }
    
        public static INPUT VirtualKeyUp ( Keys keyCode )
        {
            var input = new INPUT() { type = INPUTTYPE.Keyboard };
            input.u.ki = new KEYBDINPUT() { virtualKey = (ushort)keyCode, flags = KEYEVENTF.KeyUp };
    
            return input;
        }
    }
    
    [StructLayout(LayoutKind.Explicit)]
    private struct INPUT_U
    {
        [FieldOffset(0)]
        public KEYBDINPUT ki;
    
        [FieldOffset(0)]
        public MOUSEINPUT mi;
    
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct KEYBDINPUT
    {           
        public ushort virtualKey;
        public ushort scanCode;
        public KEYEVENTF flags;
        public uint time;
    
        public IntPtr extraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr extraInfo;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParam;
        public short lParam;
    }

    I'd recommend that you take a look at this project on Codeplex which seems to provide a much better implementation.

    • Marked as answer by Woohoooo Tuesday, September 17, 2013 8:35 AM
    Saturday, September 14, 2013 11:08 PM
    Moderator
  • I see you got good advice to use SendInput instead, but in case you're curious about your original approach, what you missed is that the flag words are flag bits that can be combined.  When you release the key, you need to specify BOTH flags:

        keybd_event(VK_UP, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);


    Tim Roberts, VC++ MVP Providenza & Boekelheide, Inc.

    • Marked as answer by Woohoooo Tuesday, September 17, 2013 8:35 AM
    Sunday, September 15, 2013 3:36 AM
  • Sorry for the late reply and many thanks to all of you. The two I have marked both work, although I prefer the outdated? but far more streamlined keybd_event way of things.

    James Finch (MCDST) -- Please vote as helpful if you found this post helpful, or mark as answer if it answered your question.

    Tuesday, September 17, 2013 8:35 AM