Имитация клавиатуры с помощью SendInput API в приложениях DirectInput - PullRequest
16 голосов
/ 05 сентября 2010

Я пытаюсь смоделировать команды клавиатуры для пользовательского приложения игрового контроллера.Потому что мне нужно моделировать команды в среде 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 ]

22 голосов
/ 06 сентября 2010

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

Команда 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

3 голосов
/ 29 января 2013

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