Имитация клавиатуры с API-интерфейсом SendInput в приложениях DirectInput
Я пытаюсь имитировать команды клавиатуры для пользовательского приложения игрового контроллера. Поскольку мне нужно моделировать команды в среде DirectInput, большинство обычных методов не работают. Я знаю, что использование hook будет работать на 100%, но я пытаюсь найти более легкую реализацию.
Я довольно много искал и обнаружил, что с использованием API-интерфейса SendInput со Scancodes вместо виртуальных ключей нужно работать, но, похоже, он ведет себя так же, как ключ "прилипает". Я отправил оба события KEYDOWN и KEYUP, но когда я пытаюсь отправить сообщение в среде DirectInput, игра действует так, как будто ключ удерживается.
Например, если я имитирую "W" нажатие клавиши, и этот ключ, отображаемый в шутер от первого лица, будет действовать в действие "двигаться вперед", как только я буду в игре, функция ниже заставит персонажа двигаться вперед. Однако, просто выдав команду один раз, она будет перемещать символ вперед бесконечно.
Вот фрагмент кода (в С#) для функции SendInput, которую я вызываю:
[DllImport("user32.dll")]
static extern UInt32 SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, Int32 cbSize);
public static void Test_KeyDown()
{
INPUT[] InputData = new INPUT[2];
Key ScanCode = Microsoft.DirectX.DirectInput.Key.W;
InputData[0].type = 1; //INPUT_KEYBOARD
InputData[0].wScan = (ushort)ScanCode;
InputData[0].dwFlags = (uint)SendInputFlags.KEYEVENTF_SCANCODE;
InputData[1].type = 1; //INPUT_KEYBOARD
InputData[1].wScan = (ushort)ScanCode;
InputData[1].dwFlags = (uint)(SendInputFlags.KEYEVENTF_KEYUP | SendInputFlags.KEYEVENTF_UNICODE);
// send keydown
if (SendInput(2, InputData, Marshal.SizeOf(InputData[1])) == 0)
{
System.Diagnostics.Debug.WriteLine("SendInput failed with code: " +
Marshal.GetLastWin32Error().ToString());
}
}
Я не уверен, что этот метод является потерянной причиной, или если есть что-то глупое, я пропал без вести. Мне не нравится слишком усложнять мой код, если мне не нужно использовать крючки, но для меня это тоже новая территория.
Любая помощь, которую любой может дать, очень ценится.
Спасибо!
Ответы
Ответ 1
Я нашел решение своей проблемы. Итак, я думал, что отправлю сюда, чтобы помочь любому, у кого могут быть подобные проблемы в будущем.
Команда keyup не работает должным образом, поскольку при отправке кода сканирования клавиатура должна быть OR'ам с флагом кода сканирования (эффективно включающим оба флага), чтобы сообщить API SendInput(), что это как KEYUP и команду SCANCODE.
Например, следующий код будет правильно выдавать ключ сканирования:
INPUT[] InputData = new INPUT[1];
InputData[0].Type = (UInt32)InputType.KEYBOARD;
//InputData[0].Vk = (ushort)DirectInputKeyScanCode; //Virtual key is ignored when sending scan code
InputData[0].Scan = (ushort)DirectInputKeyScanCode;
InputData[0].Flags = (uint)KeyboardFlag.KEYUP | (uint)KeyboardFlag.SCANCODE;
InputData[0].Time = 0;
InputData[0].ExtraInfo = IntPtr.Zero;
// Send Keyup flag "OR"ed with Scancode flag for keyup to work properly
SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT)))
Спасибо Гансу за ответ. Я провел некоторое расследование и отправил два сообщения назад, как в оригинальном примере, действительно имитирует "keypress", но очень быстро. Это не сработало бы для команды движения, но было бы идеально, когда клавиши действия должны были "постучать" и не удерживаться.
Кроме того, поле виртуальной клавиши игнорируется при отправке кода сканирования. В MSDN было сказано следующее:
"Установите флаг KEYEVENTF_SCANCODE для определения ввода с клавиатуры в терминах кода сканирования. Это полезно для моделирования физического нажатия клавиши, независимо от того, какая клавиатура используется в данный момент. Значение виртуального ключа клавиши может изменяться в зависимости от текущей клавиатуры макет или какие другие клавиши были нажаты, но код сканирования всегда будет одним и тем же."
http://msdn.microsoft.com/en-us/library/ms646271%28v=VS.85%29.aspx
Ответ 2
Я пытался имитировать клавиатуру для презентации во Flash и имел ту же проблему. Он работал для таких приложений, как "Блокнот", но не для флеш-памяти. После нескольких часов работы в Интернете я, наконец, сделал это:
public static void GenerateKey(int vk, bool bExtended)
{
INPUT[] inputs = new INPUT[1];
inputs[0].type = INPUT_KEYBOARD;
KEYBDINPUT kb = new KEYBDINPUT(); //{0};
// generate down
if ( bExtended )
kb.dwFlags = KEYEVENTF_EXTENDEDKEY;
kb.wVk = (ushort)vk;
inputs[0].ki = kb;
SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
// generate up
//ZeroMemory(&kb, sizeof(KEYBDINPUT));
//ZeroMemory(&inputs,sizeof(inputs));
kb.dwFlags = KEYEVENTF_KEYUP;
if ( bExtended )
kb.dwFlags |= KEYEVENTF_EXTENDEDKEY;
kb.wVk = (ushort)vk;
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki = kb;
SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
}