SendKeys alternative that works on Citrix

后端 未结 4 1383
萌比男神i
萌比男神i 2020-12-05 08:29

I recently developed a virtual keyboard application for a customer. The program is working fine with almost all programs, but certain commands like {ENTER} or <

相关标签:
4条回答
  • 2020-12-05 08:41

    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));
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 08:53

    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;
        }
    
    0 讨论(0)
  • 2020-12-05 08:54

    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.

    0 讨论(0)
  • 2020-12-05 09:04

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

    0 讨论(0)
提交回复
热议问题