Альтернатива SendKeys, работающая на Citrix
недавно я разработал приложение виртуальной клавиатуры для клиента. Программа отлично работает практически со всеми программами, но некоторые команды, такие как {ENTER}
или {DEL}
не работают с Citrix. Есть ли обходной путь или альтернатива SendKeys
?
Edit 1: я попробовал метод SendInput (симулятор ввода Windows использует SendInput), а клавиша DEL, а также клавиши со стрелками все еще не работают. Однако ключ ENTER работает.
Edit 2: решено. Проверенный с двумя различными версиями Citrix. этот вопрос мне очень помогли.:
тонкие клиенты Citrix используют scancode парам keybd_event даже когда MS говорит он не используется и должен быть 0. Тебе нужно чтобы предоставить физический scancode для клиента citrix, чтобы получить его. Клиент Citrix также имеет серьезную проблему с клавиатурным вводом, генерируемым с помощью Sendinput из API-интерфейса.
я исправил код в Ввод Windows Симулятор:
// 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));
}
4 ответов
попробуйте использовать Симулятор Ввода Windows. не уверен, что он поддерживает Citrix, но он намного мощнее по сравнению с SendKeys.
попробуйте использовать API call wia P-Invoke signature (редактировать контент: Теперь это рабочий пример - я отправляю символ " 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.Клавиатура.Нажатие клавиши, а не InputSimulator.SimulateKeyPress). Вот код, который я добавил в InputSimulator, и я рад сообщить, что он работает так, как ожидалось, и решает проблему, которую я ранее считал невозможной. Большое спасибо.
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);
В 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;
}
для решения 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
отправит коды сканирования, связанные с виртуальными ключами, переданными.