2010-09-05 36 views
15

J'essaie de simuler des commandes clavier pour une application de contrôleur de jeu personnalisée. Étant donné que j'ai besoin de simuler des commandes dans un environnement DirectInput, la plupart des méthodes habituelles ne fonctionnent pas. Je sais que l'utilisation d'un crochet fonctionnerait à 100% mais j'essaie de trouver une implémentation plus simple. J'ai fait pas mal de recherches et j'ai trouvé que l'utilisation de l'API SendInput avec Scancodes au lieu de touches virtuelles devrait fonctionner, mais il semble que les clés soient "collées". J'ai envoyé les deux événements KEYDOWN et KEYUP, mais lorsque je tente d'envoyer le message dans un environnement DirectInput, le jeu se comporte comme si la touche était maintenue enfoncée. Par exemple, si je simule une touche "W" et que cette touche est mappée dans un jeu de tir à la première personne à l'action "avancer", une fois que je suis en jeu, la fonction ci-dessous fera avancer le personnage . Cependant, en émettant la commande une seule fois, le personnage avancera indéfiniment.Simulation du clavier avec API SendInput dans les applications DirectInput

Voici un extrait de code (en C#) pour la fonction SendInput J'appelle:

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

Je ne sais pas si cette méthode est une cause perdue, ou s'il y a juste quelque chose de stupide je Je suis manquant. Je déteste trop compliquer mon code si je n'ai pas à utiliser des crochets, mais c'est aussi un nouveau territoire pour moi.

Toute aide que quelqu'un peut donner est très appréciée.

Merci!

+4

Qu'est-ce que 'INPUT []'? Était-ce censé provenir de "" user32.dll "'? –

Répondre

21

J'ai trouvé la solution à mon propre problème. Donc, je pensais que je posterais ici pour aider toute personne qui pourrait avoir des problèmes similaires dans le futur. La commande keyup ne fonctionnait pas correctement car lorsque vous envoyez uniquement le code d'analyse, vous devez utiliser le code de scrutation avec l'indicateur de code d'analyse (activant ainsi les deux indicateurs) pour indiquer à l'API SendInput() qu'il s'agit à la fois d'un KEYUP et une commande SCANCODE.

Par exemple, le code suivant correctement délivre un code d'analyse des clés:

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

Merci à Hans pour la réponse. J'ai fait quelques recherches et envoyé deux messages dos à dos comme l'exemple original simule en effet une "touche" mais c'est très rapide. Cela ne fonctionnerait pas bien pour une commande de mouvement, mais serait idéal lorsque les touches d'action doivent être «exploitées» et non maintenues enfoncées.

En outre, le champ de clé virtuelle est ignoré lors de l'envoi d'un code de balayage. MSDN a déclaré à ce sujet:

"Définir l'indicateur KEYEVENTF_SCANCODE pour définir l'entrée au clavier en termes de code de balayage, ce qui est utile pour simuler une frappe physique, quel que soit le clavier utilisé. la valeur d'une clé peut changer en fonction de la disposition actuelle du clavier ou des autres touches pressées, mais le code de balayage sera toujours le même. "

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

+3

Dans votre solution trouvée vous mentionnez un DirectInputKeyScanCode, que je ne peux pas l'utiliser, pourriez-vous m'aider? – Fede

+0

Pourriez-vous poster une solution complète pour appuyer sur une touche enfoncée et enfoncée? Je ne suis pas sûr de ce que je fais mal et ce que j'ai besoin de remplacer dans le premier extrait de code que vous avez envoyé. THX! –

3

je tente de simuler des claviers pour une présentation en Flash, et a également été le même problème. Cela a fonctionné pour des applications comme le Bloc-notes, mais pas pour Flash. Après des heures de googling, je l'ai finalement fait fonctionner:

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