C# 在Citrix上工作的SendKeys替代方案

C# 在Citrix上工作的SendKeys替代方案,c#,.net,sendkeys,citrix,virtual-keyboard,C#,.net,Sendkeys,Citrix,Virtual Keyboard,我最近为一位客户开发了一个虚拟键盘应用程序。该程序在几乎所有程序中都能正常工作,但某些命令,如{ENTER}或{DEL}不能在Citrix中工作。是否有解决方法或替代方法来取代SendKeys 编辑1:我尝试了SendInput方法(Windows输入模拟器使用SendInput),DEL键和箭头键仍然不起作用。但是,回车键起作用 编辑2:解决了它。使用两种不同版本的Citrix进行测试: Citrix瘦客户端使用扫描代码 即使MS说 它未使用,应为0。你需要 同时提供物理扫描码 让citrix

我最近为一位客户开发了一个虚拟键盘应用程序。该程序在几乎所有程序中都能正常工作,但某些命令,如
{ENTER}
{DEL}
不能在Citrix中工作。是否有解决方法或替代方法来取代
SendKeys

编辑1:我尝试了SendInput方法(Windows输入模拟器使用SendInput),DEL键和箭头键仍然不起作用。但是,回车键起作用

编辑2:解决了它。使用两种不同版本的Citrix进行测试:

Citrix瘦客户端使用扫描代码 即使MS说 它未使用,应为0。你需要 同时提供物理扫描码 让citrix客户端获得它。 Citrix客户端也存在重大问题 使用生成的键盘输入 SendInputAPI

我将代码修补到:

//用于获取扫描代码的函数
[DllImport(“user32.dll”)]
静态外部uint映射VirtualKey(uint uCode、uint uMapType);
/// 
///调用Win32 SendInput方法。。。
/// 
///要按的VirtualKeyCode
公共静态void SimulateKeyPress(VirtualKeyCode密钥码)
{
var down=新输入();
down.Type=(UInt32)InputType.KEYBOARD;
down.Data.Keyboard=newkeybdinput();
down.Data.Keyboard.Vk=(UInt16)键码;
//此处的扫描代码为0
down.Data.Keyboard.Scan=(ushort)MapVirtualKey((UInt16)键码,0);
down.Data.Keyboard.Flags=0;
down.Data.Keyboard.Time=0;
down.Data.Keyboard.ExtraInfo=IntPtr.Zero;
var up=新输入();
up.Type=(UInt32)InputType.KEYBOARD;
up.Data.Keyboard=new KEYBDINPUT();
up.Data.Keyboard.Vk=(UInt16)键码;
//此处的扫描代码为0
up.Data.Keyboard.Scan=(ushort)MapVirtualKey((UInt16)键码,0);
up.Data.Keyboard.Flags=(UInt32)KeyboardFlag.KEYUP;
up.Data.Keyboard.Time=0;
up.Data.Keyboard.ExtraInfo=IntPtr.Zero;
输入[]输入列表=新输入[2];
inputList[0]=向下;
输入列表[1]=向上;
var numberOfSuccessfulSimulatedInputs=SendInput(2,
inputList,Marshal.SizeOf(typeof(INPUT));
if(numberOfSuccessfulSimulatedInputs==0)
抛出新异常(
Format(“对{0}的按键模拟未成功。”,
键码);
}

尝试使用不确定它是否支持Citrix,但它比SendKeys功能强大得多。

尝试使用API调用wia p-Invoke签名(内容编辑:这是一个工作示例-单击按钮,我将字符“a”发送到文本框):


我还试图使用windows InputSimulator库控制citrix应用程序。上面的代码看起来很有前途,所以我将其更新为最新版本的InputSimulator(您使用sim.Keyboard.Keypress而不是InputSimulator.SimulateKeyPress)。下面是我添加到InputSimulator的代码,我很高兴地报告它按预期工作,并解决了我以前认为不可能解决的问题。非常感谢

在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);
    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;
    }
//
///模拟指定键的按键手势。
/// 
///钥匙的钥匙。
IKeyboardSimulator CITRIXKeyPress(VirtualKeyCode keyCode);
在KeyboardSimulator.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);
    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;
    }
使用System.Runtime.InteropServices;
.
.
.
//CITRIX黑客
//用于获取扫描代码的函数
[DllImport(“user32.dll”)]
静态外部uint映射VirtualKey(uint uCode、uint uMapType);
[DllImport(“User32.dll”)]
私有静态外部uint SendInput(uint numberOfInputs,[Marshallas(UnmanagedType.LPArray,SizeConst=1)]输入[]输入,int structSize);
/// 
///调用Win32 SendInput方法。。。
/// 
///要按的VirtualKeyCode
public IKeyboardSimulator CITRIXKeyPress(VirtualKeyCode keyCode)//prev public static void
{
var down=新输入();
down.Type=(UInt32)InputType.Keyboard;
down.Data.Keyboard=newkeybdinput();
down.Data.Keyboard.KeyCode=(UInt16)KeyCode;//prev.Keyboard.Vk
//此处的扫描代码为0
down.Data.Keyboard.Scan=(ushort)MapVirtualKey((UInt16)键码,0);
down.Data.Keyboard.Flags=0;
down.Data.Keyboard.Time=0;
down.Data.Keyboard.ExtraInfo=IntPtr.Zero;
var up=新输入();
up.Type=(UInt32)InputType.Keyboard;
up.Data.Keyboard=new KEYBDINPUT();
up.Data.Keyboard.KeyCode=(UInt16)KeyCode;
//此处的扫描代码为0
up.Data.Keyboard.Scan=(ushort)MapVirtualKey((UInt16)键码,0);
up.Data.Keyboard.Flags=(UInt32)KeyboardFlag.KeyUp;
up.Data.Keyboard.Time=0;
up.Data.Keyboard.ExtraInfo=IntPtr.Zero;
输入[]输入列表=新输入[2];
inputList[0]=向下;
输入列表[1]=向上;
var numberOfSuccessfulSimulatedInputs=SendInput(2,
inputList,Marshal.SizeOf(typeof(INPUT));
if(numberOfSuccessfulSimulatedInputs==0)
抛出新异常(
Format(“对{0}的按键模拟未成功。”,
键码);
归还这个;
}

对于windows input simulator解决方案,您可以直接修改源代码,以便内置函数使用虚拟键发送扫描代码

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
将发送与传入的虚拟键相关的扫描代码。

感谢您的快速响应。我将尝试并告诉您结果。谢谢xsl!!!伟大的发现!我也在使用InputSimulator(顺便说一句,fab工具),但我需要您提供的这个调整(ushort)MapVirtualKey((UInt16)keyCode,0);出色的工作-非常感谢您有没有一种方法可以发送整个字符串而不是按键?这可以在Python脚本中使用吗?我也在尝试同样的方法