2010-09-05 208 views
15

我试图模拟自定义游戏控制器应用程序的键盘命令。因为我需要在DirectInput环境中模拟命令,所以大多数通常的方法都不起作用。我知道使用钩子可以工作100%,但我试图找到一个更容易的实现。在DirectInput应用程序中使用SendInput API模拟键盘

我已经做了相当多的搜索,发现通过使用SendInput API与Scancodes而不是虚拟键应该可以工作,但它似乎表现得像键是“粘住”。我已经发送了KEYDOWN和KEYUP事件,但是当我尝试在DirectInput环境中发送消息时,游戏的行为就好像按住了键。例如,如果我模拟一个“W”按键并将该键映射到第一人称射击游戏中的“向前移动”动作,则一旦我在游戏中,下面的功能将导致角色向前移动。然而,只要发布一次命令,它就会无限期地向前移动角色。

下面是SendInput功能我打电话代码段(在C#):

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

我不知道这方法是一个失败的事业,或者如果有只是一些无聊的我我错过了。如果我不需要使用钩子,我不想使代码复杂化,但这对我来说也是一个新的领域。

任何人都可以给的帮助是非常感谢。

谢谢!

+4

什么是'INPUT []'?是否应该来自''user32.dll“? – 2015-10-23 18:00:04

回答

21

我发现了解决我自己的问题。所以,我认为我会在这里发帖以帮助任何未来可能有类似问题的人。

keyup命令无法正常工作,因为只有在发送扫描码时,keyup必须与扫描码标志进行或运算(有效启用两个标志)才能告诉SendInput()API,它既是一个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

在你的解决方案中发现你提到了一个DirectInputKeyScanCode,我无法使用它,你能帮我吗? – Fede 2013-09-10 01:43:38

+0

你可以发布一个完整的解决方案,按下键和键?我不确定我做错了什么,以及在发送的第一个代码段中需要替换的内容。谢谢! – 2014-04-15 03:43:29

3

我试图模拟键盘在Flash演示,并且也有同样的问题。它确实适用于记事本等应用程序,但不适用于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])); 
    }