2010-09-05 17 views
15

Estoy tratando de 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.Teclado simulando con SendInput API en aplicaciones DirectInput

Realicé bastante búsqueda y descubrí que al usar la API SendInput con códigos de escaneo en lugar de las teclas virtuales debería funcionar, pero parece que se comporta como si las claves estuvieran "pegadas". Envié los eventos KEYDOWN y KEYUP, pero cuando intento 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 a un jugador en primera persona para 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í es un fragmento de código (en C#) para la función SendInput 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 hay algo que tonta estoy perdido 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!

+4

¿Qué es 'INPUT []'? ¿Se suponía que provenía de '" user32.dll "'? –

Respuesta

21

He encontrado 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 al enviar 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 se trata de un KEYUP y un comando SCANCODE.

Por ejemplo, el siguiente código emitirá correctamente un escaneo de código de tecla arriba:

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 para 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 tenía lo siguiente que decir al respecto:

"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 sin importar qué teclado se esté utilizando actualmente. el valor de una tecla puede variar dependiendo de la distribución del teclado actual o de las otras teclas que se presionaron, pero el código de exploración siempre será el mismo ".

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

+3

En su solución encontró que menciona un DirectInputKeyScanCode, que no puedo usarlo, ¿podría ayudarme? – Fede

+0

¿Podría publicar una solución completa para presionar una tecla y tecla arriba? No estoy seguro de lo que estoy haciendo mal y de lo que necesito reemplazar en el primer fragmento de código que ha enviado. ¡GRACIAS! –

3

yo estaba tratando de simular teclados para una presentación en Flash, y también tenía 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])); 
    } 
Cuestiones relacionadas