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

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

Я сделал довольно много поиска и обнаружил, что с помощью API SendInput с Scancodes вместо виртуальных ключей должен работать, но он, похоже, ведет себя так, как ключ "прилипает". Я отправлены как события 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 не работала должным образом, потому что при отправке только кода сканирования keyup должен быть or'ED с флагом кода сканирования (эффективно позволяя обоим флагам), чтобы сообщить API SendInput (), что это и команда KEYUP, и команда SCANCODE.

например, следующий код правильно выдает скан-код key-up:

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, а также имел ту же проблему. Он работал для приложений, таких как блокнот, но не для flash. После нескольких часов googling я, наконец, сделал это:

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]));
    }