Search code examples
c#inputsimulationrdp

How to use InputSimulator to simulate specific keys on Remote Desktop?


Im currently using the InputSimulator v0.1.0.0 to simulate keypresses and/or mouse events over Remote Desktop. Basic keypresses (for example pressing 'a') works, but special characters, like 'tab', 'enter' dont. I simulate entering texts with:

InputSimulator.SimulateTextEntry("blabla");

but the following only works locally:

InputSimulator.SimulateKeyPress(VirtualKeyCode.TAB);

or

InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);

I searched over the net for working examples but i havent found anything useful. Anyone has idea how to make it work?

Thanks in advance!

-------------------------OWN ANSWER----------------------------------

After googling some more, i have found this article:

http://www.pinvoke.net/default.aspx/user32.keybd_event

in which there is a good code, that does not solve the InputSimulator problem, but does exactly that i need. Here is the code, and how i used that:

[StructLayout(LayoutKind.Sequential)]
    public struct KEYBOARD_INPUT
    {
        public const uint Type = 1;
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

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

    [StructLayout(LayoutKind.Explicit)]
    struct KEYBDINPUT
    {
        [FieldOffset(0)]
        public ushort wVk;
        [FieldOffset(2)]
        public ushort wScan;
        [FieldOffset(4)]
        public uint dwFlags;
        [FieldOffset(8)]
        public uint time;
        [FieldOffset(12)]
        public IntPtr dwExtraInfo;
    };

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

    [StructLayout(LayoutKind.Explicit)]
    struct INPUT
    {
        [FieldOffset(0)]
        public int type;
        [FieldOffset(4)]
        public MOUSEINPUT mi;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
        [FieldOffset(4)]
        public HARDWAREINPUT hi;
    };
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint SendInput(uint nInputs, IntPtr pInput, int cbSize);

And this is how i called the 'press TAB' event:

keybd_event(0x09, 0x0f, 0, 0); // Tab Press

keybd_event(0x09, 0x0f, 0x0002, 0);

Solution

  • As suggested i copy my solution as an answer. I hope these will help for anyone working on similar problems. :) The solution is a bit long, but the problem was not only 'how-to-press a button programatically', but also 'how-to-make it work via remote desktop' and 'how-to-make a general solution for different keyboards'. Well, im not 100% sure that the last problem is completely solved, but the solution below may be used for further developing. I also know that the code is not optimal and sometimes ugly, but im still testing and developing it! :)

    //m_text is the whole text i want to write. It may contain special characters,
    //like 'enter', 'tab', lower/upper-case chars, and chars with shit/alt is
    //pressed, like ';'.
    
                    //Test with this string, its difficult-enough. :)
                    string m_text = "123qweQWE;{tab}!{eNTer}*|";
    
                    IntPtr keyboardLayout = GetKeyboardLayout(0);
    
                    while (!string.IsNullOrWhiteSpace(m_text))
                    {
                        int m_Index = 0;
    
                        //Enter, tab and similar keys are in {} brackets
                        //(for example {tab}). We get the 'tab' from the
                        //string and pass this to our method. Key combinations
                        //are separated by a '+' like {alt+tab+tab}, from this
                        //we will get 'press the alt, then press the tab, then
                        //press the tab again'.
                        if (m_text[m_Index] == '{')
                        {
                            #region [ Special chars ]
                            string m_SubString = m_text.Substring(
                                                        m_Index + 1, m_text.IndexOf("}") - 1);
    
                            string[] m_Splitted = m_SubString.Split(new char[] { '+' });
    
                            for (int i = 0; i < m_Splitted.Length; i++)
                            {
                                //If the string is longer than 1 char it means we are processing a tab-like key.
                                if (m_Splitted[i].Length > 1)
                                    PressSpecial(m_Splitted[i]);
                                else
                                {
                                    //If the char is 1-char-long, it means we previously pressed a tab-like key,
                                    //and now we press a simple key, like in the case of {altgr+w}.
    
                                    //Get the virtual key of the char.
                                    short vKey = VkKeyScanEx(
                                        char.Parse(m_Splitted[i]), keyboardLayout);
    
                                    //Get the low byte from the virtual key.
                                    byte m_LOWBYTE = (Byte)(vKey & 0xFF);
    
                                    //Get the scan code of the key.
                                    byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
    
                                    //Press the key.
                                    //Key down event, as indicated by the 3rd parameter that is 0.
                                    keybd_event(m_LOWBYTE, sScan, 0, 0);
                                }
                            }
    
                            Application.DoEvents();
    
                            //We have pressed all the keys we wanted, now release them in backward-order
                            //when pressing alt+tab we beed to release them in tab-alt order! The logic is the same.
                            for (int i = m_Splitted.Length - 1; i > -1; i--)
                            {
                                if (m_Splitted[i].Length > 1)
                                    ReleaseSpecial(m_Splitted[i]);
                                else
                                {
                                    short vKey = VkKeyScanEx(
                                        char.Parse(m_Splitted[i]), keyboardLayout);
    
                                    byte m_LOWBYTE = (Byte)(vKey & 0xFF);
    
                                    byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
    
                                    //Key up event, as indicated by the 3rd parameter that is 0x0002.
                                    keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
                                }
                            }
    
                            Application.DoEvents();
    
                            #endregion
    
                            //We do not use the '{' and '}' brackets, thats why the '+2'. :)
                            m_Index = m_SubString.Length + 2;
                        }
                        else
                        {
                            #region [ One char ]
                            short vKey = VkKeyScanEx(m_text[m_Index], keyboardLayout);
    
                            //Hi-byte indicates if we need to press shift, alt or other similar keys.
                            byte m_HIBYTE = (Byte)(vKey >> 8);
                            byte m_LOWBYTE = (Byte)(vKey & 0xFF);
    
                            byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
    
                            //Press the special key if needed.
                            if ((m_HIBYTE == 1))
                                PressShift();
                            else if ((m_HIBYTE == 2))
                                PressControl();
                            else if ((m_HIBYTE == 4))
                                PressAlt();
                            else if ((m_HIBYTE == 6))
                                PressAltGr();
    
                            //Press, then release the key.
                            keybd_event(m_LOWBYTE, sScan, 0, 0); //Key down
                            keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
    
                            //Release the special key if needed.
                            if ((m_HIBYTE == 1))
                                ReleaseShift();
                            else if ((m_HIBYTE == 2))
                                ReleaseControl();
                            else if ((m_HIBYTE == 4))
                                ReleaseAlt();
                            else if ((m_HIBYTE == 6))
                                ReleaseAltGr();
                            #endregion
    
                            //Get the next char from the string.
                            m_Index++;
                        }
    
                        //Remove the already processed chars from the string.
                        if (m_Index < m_text.Length)
                            m_text = m_text.Substring(m_Index);
                        else
                            m_text = string.Empty;
                    }
    

    So, this was the logic that processes a string. Lets see the helper methods that will handle the events:

    Press and release special keys are the same, only the first two parameters are different. Check msdn to get the virtual and scan codes for enter, tab, alt, altgr, etc...

    #region [ Press shift ]
    private void PressShift()
    {
        //0xA0 is the virtual key of 'shift'.
        //0x2A is the scan code of 'shift'.
        keybd_event(0xA0, 0x2A, 0, 0);
    } 
    #endregion
    
    #region [ Release shift ]
    private void ReleaseShift()
    {
        keybd_event(0xA0, 0x2A, 0x0002, 0);
    } 
    #endregion
    

    PressSpecial is similar to the code above, so it can be used for shift as well. I separated some of them into different methods as for me its easier to see what i use in the code (its easier for me to use 'PressShift();' instead of 'PressSpecial("shift")';). :)

    private void PressSpecial(string p_Special)
    {
        switch (p_Special.ToLower()) //<-- use lower version!
        {
            case "home":
                keybd_event(0x24, 0x47, 0, 0);
                break;
            case "end":
                keybd_event(0x23, 0x4F, 0, 0);
                break;
            //Do the same for any key you need (enter, tab, page up, etc...).
            //Remember to get the proper virtual- and scan codes for each keys!
        }
    }
    

    ReleaseSpecial is the same as PressSpecial, but the 3rd parameter is 0x0002.

    private void ReleaseSpecial(string p_Special)
    {
        switch (p_Special.ToLower())
        {
            case "home":
                keybd_event(0x24, 0x47, 0x0002, 0);
                break;
            case "end":
                keybd_event(0x23, 0x4F, 0x0002, 0);
                break;
        }
    }
    

    And finally, here are the dll import methods. You can put them into a static class, if you wish:

    [DllImport("user32.dll", EntryPoint = "keybd_event", CharSet = CharSet.Auto, 
    ExactSpelling = true)]
    public static extern void keybd_event(byte vk, byte scan, int flags, int extrainfo);
    
    [DllImport("user32.dll")]
    public static extern IntPtr GetKeyboardLayout(uint idThread);
    
    [DllImport("user32.dll")]
    public static extern short VkKeyScanEx(char ch, IntPtr dwhkl); 
    
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int MapVirtualKey(int uCode, int uMapType);