适用于Citrix的SendKeys替代方案

我最近为客户开发了一个虚拟键盘应用程序。 该程序几乎适用于所有程序,但某些命令如{ENTER}{DEL}不适用于Citrix。 是否有解决方法或替代SendKeys

编辑1:我尝试了SendInput方法(Windows输入模拟器使用SendInput),DEL键和箭头键仍然无法正常工作。 然而,ENTER键有效。

编辑2:解决了它。 测试了两个不同版本的Citrix。 这个问题给了我很多帮助。 :

Citrix瘦客户机使用keybd_event的扫描码参数,即使MS表示它未使用且应为0.您还需要提供物理扫描码以供Citrix客户端使用。 Citrix客户端也存在使用SendInput API生成的键盘输入的主要问题。

我修补了Windows输入模拟器中的代码:

 // 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调用w-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)); } } } 

我还试图使用Windows InputSimulator库来控制citrix应用程序。 上面的代码看起来很有前途,所以我更新了它以使用最新版本的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; } 

通过这些更改, TextEntryKeyPressModifiedKeyStroke将发送与传入的虚拟键关联的扫描代码。