Simulación de teclado con SendInput API en aplicaciones DirectInput

Intento simular comandos de teclado para una aplicación de control de juego personalizada. Como tendré que simular comandos en un entorno DirectInput, la mayoría de los métodos habituales no funcionan. Sé que usar un gancho funcionaría al 100%, pero estoy tratando de encontrar una implementación más fácil.

He investigado un poco y descubrí que al usar SendInput API con Scancodes en lugar de las teclas virtuales debería funcionar, pero parece comportarse como si las claves estuvieran “pegadas”. Envié los eventos KEYDOWN y KEYUP, pero cuando bash enviar el mensaje en un entorno DirectInput, el juego actúa como si la tecla se mantuviera presionada.

Por ejemplo, si simulo una pulsación de tecla “W” y hago que la tecla se asigne en un Disparador en primera persona a la acción “avanzar”, una vez que estoy en el juego, la función siguiente hará que el personaje avance. Sin embargo, solo emitiendo el comando una vez, moverá al personaje hacia adelante indefinidamente.

Aquí hay un fragmento de código (en C #) para la función SendInput que estoy llamando:

[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()); } } 

No estoy seguro de si este método es una causa perdida, o si simplemente hay algo tonto que me estoy perdiendo. Odio complicar demasiado mi código si no tengo que usar ganchos, pero este es también un territorio nuevo para mí.

Cualquier ayuda que alguien pueda dar es muy apreciada.

¡Gracias!

Encontré la solución a mi problema. Entonces, pensé que publicaría aquí para ayudar a cualquier persona que pueda tener problemas similares en el futuro.

El comando de teclado no funcionaba correctamente porque cuando solo se envía el código de escaneo, la tecla debe estar OR con el indicador de código de escaneo (habilitando efectivamente ambos indicadores) para decirle a la API SendInput () que esto es tanto un KEYUP como un SCANCODE mando.

Por ejemplo, el siguiente código emitirá correctamente una clave de código de exploración:

 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))) 

Gracias a Hans por la respuesta. Investigué un poco y envié dos mensajes seguidos, como el ejemplo original simula una “pulsación de tecla” pero es muy rápido. No funcionaría bien para un comando de movimiento, pero sería ideal cuando las teclas de acción deben “tocarse” y no mantenerse presionadas.

Además, el campo de la clave virtual se ignora al enviar un código de escaneo. MSDN dijo lo siguiente sobre el tema:

“Establezca el indicador KEYEVENTF_SCANCODE para definir la entrada del teclado en términos del código de exploración. Esto es útil para simular una pulsación de tecla física independientemente del teclado que se esté utilizando en ese momento. El valor de la clave virtual de una tecla puede variar según el diseño del teclado actual o se presionaron otras teclas, pero el código de exploración siempre será el mismo “.

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

Estaba tratando de simular teclados para una presentación en Flash, y también estaba teniendo el mismo problema. Funcionó para aplicaciones como el Bloc de notas, pero no para el flash. Después de horas de búsqueda en Google finalmente lo hice funcionar:

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