SendKeys alternative that works on Citrix

Try using Windows Input Simulator. Not sure if it supports Citrix but it is much more powerfull compared to SendKeys.


I'm also attempting to control a citrix application using the windows InputSimulator library. Your code above looked promising, so I updated it to work with the latest version of InputSimulator (where you use sim.Keyboard.Keypress rather than InputSimulator.SimulateKeyPress). Here is the code that I added to InputSimulator, and I am delighted to report that it works as expected, and solves a problem that I previously thought was not possible. Thanks so much.

In IKeyboardSimulator.cs:

    /// <summary>
    /// Simulates the key press gesture for the specified key.
    /// </summary>
    /// <param name="keyCode">The <see cref="VirtualKeyCode"/> for the key.</param>
    IKeyboardSimulator CITRIXKeyPress(VirtualKeyCode keyCode);

In KeyboardSimulator.cs:

    using System.Runtime.InteropServices;

    .
    .
    .

    // CITRIX HACK
    // Function used to get the scan code
    [DllImport("user32.dll")]
    static extern uint MapVirtualKey(uint uCode, uint uMapType);

    [DllImport("User32.dll")]
    private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] input, int structSize);


    /// <summary>
    /// Calls the Win32 SendInput method ...
    /// </summary>
    /// <param name="keyCode">The VirtualKeyCode to press</param>
    public IKeyboardSimulator CITRIXKeyPress(VirtualKeyCode keyCode) //prev public static void
    {
        var down = new INPUT();
        down.Type = (UInt32)InputType.Keyboard;
        down.Data.Keyboard = new KEYBDINPUT();
        down.Data.Keyboard.KeyCode = (UInt16)keyCode; //prev .Keyboard.Vk
        // Scan Code here, was 0
        down.Data.Keyboard.Scan = (ushort)MapVirtualKey((UInt16)keyCode, 0);
        down.Data.Keyboard.Flags = 0;
        down.Data.Keyboard.Time = 0;
        down.Data.Keyboard.ExtraInfo = IntPtr.Zero;

        var up = new INPUT();
        up.Type = (UInt32)InputType.Keyboard;
        up.Data.Keyboard = new KEYBDINPUT();
        up.Data.Keyboard.KeyCode = (UInt16)keyCode;
        // Scan Code here, was 0
        up.Data.Keyboard.Scan = (ushort)MapVirtualKey((UInt16)keyCode, 0);
        up.Data.Keyboard.Flags = (UInt32)KeyboardFlag.KeyUp;
        up.Data.Keyboard.Time = 0;
        up.Data.Keyboard.ExtraInfo = IntPtr.Zero;

        INPUT[] inputList = new INPUT[2];
        inputList[0] = down;
        inputList[1] = up;

        var numberOfSuccessfulSimulatedInputs = SendInput(2,
             inputList, Marshal.SizeOf(typeof(INPUT)));
        if (numberOfSuccessfulSimulatedInputs == 0)
            throw new Exception(
            string.Format("The key press simulation for {0} was not successful.",
            keyCode));
        return this;
    }

Try to utilize API call wia P-Invoke signature (Content edited: this is now working example - I'm sending character 'a' to the textBox, on the click of a button) :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime;
using System.Runtime.InteropServices;

namespace Test2
{
    public partial class Form1 : Form
    {
        [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);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Focus();
            INPUT Input = new INPUT();

            Input.type = 1;
            Input.ki.wVk = 0x41;  //ASCII for letter 'A'
            Input.ki.dwFlags = 0;  //Key is pressed down
            Input.ki.dwExtraInfo = IntPtr.Zero;
            IntPtr pInput;
            pInput = Marshal.AllocHGlobal(Marshal.SizeOf(Input));

            Marshal.StructureToPtr(Input, pInput, false);
            SendInput(1, pInput, Marshal.SizeOf(Input));
            Input.ki.dwFlags = 2;  //Key is released on the keyboard

            Marshal.StructureToPtr(Input, pInput, false);
            SendInput(1, pInput, Marshal.SizeOf(Input));
        }
    }
}

For the windows input simulator solution, you can modify the source code directly so that the built in functions will send scan codes with the virtual keys.

InputBuilder.cs:

using System.Runtime.InteropServices;
.
.
.
[DllImport("user32.dll")]
static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
.
.
.
public InputBuilder AddKeyDown(VirtualKeyCode keyCode)
{
    var down =
        new INPUT
        {
            Type = (UInt32)InputType.Keyboard,
            Data =
                    {
                        Keyboard =
                            new KEYBDINPUT
                                {
                                    KeyCode = (UInt16) keyCode,
                                    Scan = (UInt16)MapVirtualKeyEx((UInt16)keyCode, 0, IntPtr.Zero),
                                    Flags = IsExtendedKey(keyCode) ? (UInt32) KeyboardFlag.ExtendedKey : (UInt32) KeyboardFlag.ScanCode,
                                    Time = 0,
                                    ExtraInfo = IntPtr.Zero
                                }
                    }
            };

    _inputList.Add(down);
    return this;
}
.
.
.
public InputBuilder AddKeyUp(VirtualKeyCode keyCode)
{
    var up =
        new INPUT
            {
                Type = (UInt32) InputType.Keyboard,
                Data =
                    {
                        Keyboard =
                            new KEYBDINPUT
                                {
                                    KeyCode = (UInt16) keyCode,
                                    Scan = (UInt16)MapVirtualKeyEx((UInt16)keyCode, 0,IntPtr.Zero),
                                    Flags = (UInt32) (IsExtendedKey(keyCode)
                                                          ? KeyboardFlag.KeyUp | KeyboardFlag.ExtendedKey
                                                          : KeyboardFlag.KeyUp | KeyboardFlag.ScanCode),
                                    Time = 0,
                                    ExtraInfo = IntPtr.Zero
                                }
                    }
            };

    _inputList.Add(up);
    return this;
}
.
.
.
public InputBuilder AddCharacter(char character)
{
    bool shiftChr = ((UInt16)VkKeyScanEx(character, IntPtr.Zero) >> 8).Equals(1);
    if (shiftChr)
    {
        AddKeyDown(VirtualKeyCode.VK_SHIFT);
    }

    UInt16 scanCode = shiftChr ? (UInt16)MapVirtualKeyEx((UInt16)(VkKeyScanEx(character, IntPtr.Zero) & 0xff),0,IntPtr.Zero) : (UInt16)MapVirtualKeyEx((UInt16)VkKeyScanEx(character, IntPtr.Zero), 0, IntPtr.Zero);

    var down = new INPUT
                   {
                       Type = (UInt32)InputType.Keyboard,
                       Data =
                           {
                               Keyboard =
                                   new KEYBDINPUT
                                       {
                                           KeyCode = 0,
                                           Scan = scanCode,
                                           Flags = (UInt32)KeyboardFlag.ScanCode,
                                           Time = 0,
                                           ExtraInfo = IntPtr.Zero
                                       }
                           }
                   };

    var up = new INPUT
                 {
                     Type = (UInt32)InputType.Keyboard,
                     Data =
                         {
                             Keyboard =
                                 new KEYBDINPUT
                                     {
                                         KeyCode = 0,
                                         Scan = scanCode,
                                         Flags =
                                             (UInt32)(KeyboardFlag.KeyUp | KeyboardFlag.ScanCode),
                                         Time = 0,
                                         ExtraInfo = IntPtr.Zero
                                     }
                         }
                 };

    _inputList.Add(down);
    _inputList.Add(up);

    if (shiftChr)
    {
        AddKeyUp(VirtualKeyCode.VK_SHIFT);
    }

    return this;
}

With these changes TextEntry, KeyPress, and ModifiedKeyStroke will send the scan codes associated with the virtual keys passed in.