Альтернатива SendKeys, которая работает на Citrix

Недавно я разработал приложение для виртуальной клавиатуры для клиента. Программа работает нормально почти со всеми программами, но некоторые команды, такие как {ENTER} или {DEL} , не работают с Citrix. Есть ли обходной путь или альтернатива SendKeys ?

Редактирование 1: я попробовал метод SendInput (симулятор ввода Windows использует SendInput), а клавиша DEL, а также клавиши со стрелками все еще не работают. Однако работает клавиша ENTER.

Редактировать 2: Решил. Протестировано с двумя разными версиями Citrix. Этот вопрос мне очень помог. :

В тонких клиентах Citrix используется параметр scancode keybd_event, даже когда MS говорит, что он не используется и должен быть равен 0. Вам нужно предоставить физический scancode, а также клиент citrix для его получения. Клиент Citrix также имеет серьезную проблему с клавиатурным вводом, созданным с помощью API SendInput.

Я закрепил код в Windows Input Simulator :

 // Function used to get the scan code [DllImport("user32.dll")] static extern uint MapVirtualKey(uint uCode, uint uMapType); ///  /// Calls the Win32 SendInput method ... ///  /// The VirtualKeyCode to press public static void SimulateKeyPress(VirtualKeyCode keyCode) { var down = new INPUT(); down.Type = (UInt32)InputType.KEYBOARD; down.Data.Keyboard = new KEYBDINPUT(); down.Data.Keyboard.Vk = (UInt16)keyCode; // 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.Vk = (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)); } 

Попробуйте использовать симулятор ввода Windows. Не уверен, поддерживает ли он Citrix, но он намного более мощный по сравнению с SendKeys.

Попробуйте использовать API-вызов wia P-Invoke подписи ( редактируемый контент : теперь это рабочий пример – я посылаю символ «a» в текстовое поле одним нажатием кнопки):

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

Я также пытаюсь контролировать приложение citrix, используя библиотеку Windows InputSimulator. Ваш код выше выглядел многообещающим, поэтому я обновил его для работы с последней версией InputSimulator (где вы используете sim.Keyboard.Keypress, а не InputSimulator.SimulateKeyPress). Вот код, который я добавил в InputSimulator, и я рад сообщить, что он работает так, как ожидалось, и решает проблему, которую я считал ранее невозможной. Спасибо.

В IKeyboardSimulator.cs:

  ///  /// Simulates the key press gesture for the specified key. ///  /// The  for the key. IKeyboardSimulator CITRIXKeyPress(VirtualKeyCode keyCode); 

В 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); ///  /// Calls the Win32 SendInput method ... ///  /// The VirtualKeyCode to press 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; } 

Для решения симулятора входа в Windows вы можете напрямую изменить исходный код, чтобы встроенные функции отправляли коды сканирования с помощью виртуальных клавиш.

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

С помощью этих изменений TextEntry , KeyPress и ModifiedKeyStroke отправят коды сканирования, связанные с переданными виртуальными ключами.