简体   繁体   English

适用于Citrix的SendKeys替代品

[英]SendKeys alternative that works on Citrix

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 {DEL} are not working with Citrix. 该程序几乎可以在所有程序上正常运行,但是某些命令(如{ENTER}{DEL}不适用于Citrix。 Is there are workaround or an alternative to SendKeys ? 是否有替代方法或SendKeys的替代方法?

Edit 1: I tried the SendInput method (Windows Input Simulator uses SendInput) and the DEL key as well as the arrow keys are still not working. 编辑1:我尝试了SendInput方法(Windows输入模拟器使用SendInput)和DEL键以及箭头键仍然无法正常工作。 The ENTER key works however. ENTER键起作用。

Edit 2: Solved it. 编辑2:解决它。 Tested with two different versions of Citrix. 经过两个不同版本的Citrix测试。 This question helped me a lot. 这个问题对我有很大帮助。 :

Citrix thin clients uses the scancode param of keybd_event even when MS says it is unused and should be 0. You need to supply the physical scancode aswell for the citrix client to get it. Citrix瘦客户端使用keybd_event的扫描代码参数,即使MS表示它未使用且应为0。您也需要提供物理扫描代码,以供citrix客户端获取。 Citrix client also has major problem with keyboard input generated with the SendInput API. Citrix客户端在使用SendInput API生成的键盘输入方面也存在重大问题。

I patched the code in Windows Input Simulator : 我在Windows Input Simulator中修补了代码:

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


/// <summary>
/// Calls the Win32 SendInput method ...
/// </summary>
/// <param name="keyCode">The VirtualKeyCode to press</param>
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));
}

Try using Windows Input Simulator. 尝试使用Windows输入模拟器。 Not sure if it supports Citrix but it is much more powerfull compared to SendKeys. 不确定它是否支持Citrix,但与SendKeys相比,它的功能要强大得多。

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) : 尝试通过P-Invoke签名利用API调用( 内容已编辑 :此示例现在正在工作-单击按钮,我将向文本框发送字符“ 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));
        }
    }
}

I'm also attempting to control a citrix application using the windows InputSimulator library. 我还尝试使用Windows InputSimulator库控制citrix应用程序。 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). 上面的代码看起来很有希望,因此我对其进行了更新,以使其可以与最新版本的InputSimulator配合使用(在此版本中,您使用sim.Keyboard.Keypress而不是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. 这是我添加到InputSimulator的代码,我很高兴地报告它可以按预期运行,并解决了以前认为不可能的问题。 Thanks so much. 非常感谢。

In IKeyboardSimulator.cs: 在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: 在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;
    }

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. 对于Windows输入模拟器解决方案,您可以直接修改源代码,以便内置函数将使用虚拟键发送扫描代码。

InputBuilder.cs: 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. 经过这些更改, TextEntryKeyPressModifiedKeyStroke将发送与传入的虚拟键关联的扫描代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM