Имитация клавиатуры с помощью SendInput API в приложениях DirectInput

Я пытаюсь смоделировать команды клавиатуры для пользовательского приложения игрового контроллера. Потому что мне нужно имитировать команды в среде DirectInput, большинство обычных методов не работают. Я знаю, что использование хука будет работать на 100%, но я пытаюсь найти более простую реализацию.

Я провел немало поисков и обнаружил, что использование API SendInput со скан-кодами вместо виртуальных ключей должно работать, но, похоже, он ведет себя так, будто ключи "залипают". Я отправил события KEYDOWN и KEYUP, но когда я пытаюсь отправить сообщение в среде DirectInput, игра ведет себя так, как будто клавиша удерживается нажатой.

Например, если я имитирую нажатие клавиши "W" и сопоставляю эту клавишу в шутере от первого лица с действием "двигаться вперед", как только я в игре, приведенная ниже функция заставит персонажа двигаться вперед. Однако, просто выполнив команду один раз, он продвинет персонажа вперед на неопределенное время.

Вот фрагмент кода (в C#) для функции 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());
    }
}

Я не уверен, что этот метод - безнадежное дело, или я просто скучаю по глупости. Я ненавижу чрезмерно усложнять мой код, если мне не нужно использовать хуки, но это также новая территория для меня.

Любая помощь, которую кто-либо может оказать, высоко ценится.

Спасибо!

2 ответа

Решение

Я нашел решение своей проблемы. Итак, я решил опубликовать здесь, чтобы помочь любому, кто может иметь подобные проблемы в будущем.

Команда keyup не работала должным образом, потому что при отправке только кода сканирования ключ должен быть ИЛИ с флагом кода сканирования (эффективно включающим оба флага), чтобы сообщить 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)))

Спасибо Гансу за ответ. Я провел некоторое исследование и отправил два сообщения подряд, как в оригинальном примере действительно имитирует "нажатие клавиш", но это очень быстро. Это не будет хорошо работать для команды движения, но будет идеальным, когда клавиши действия должны быть "нажаты" и не удерживаются.

Кроме того, поле виртуального ключа игнорируется при отправке кода сканирования. MSDN сказал по этому поводу следующее:

"Установите флаг KEYEVENTF_SCANCODE, чтобы определить ввод с клавиатуры в терминах кода сканирования. Это полезно для имитации физического нажатия клавиши независимо от того, какая клавиатура используется в данный момент. Значение виртуальной клавиши может изменяться в зависимости от текущей раскладки клавиатуры или того, что другие клавиши были нажаты, но код сканирования всегда будет таким же. "

http://msdn.microsoft.com/en-us/library/ms646271%28v=VS.85%29.aspx

Я пытался смоделировать клавиатуру для презентации во 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]));
    }
Другие вопросы по тегам