在DirectInput应用程序中使用SendInput API模拟键盘

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

我已经做了很多搜索,发现通过使用带有Scancodes而不是虚拟键的SendInput API应该可以工作,但它似乎表现得像键是“坚持”。 我已经发送了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()); } } 

我不确定这种方法是否是一个失败的原因,或者是否只是一些愚蠢的东西我不知道。 如果我不需要使用钩子,我讨厌使代码复杂化,但这对我来说也是一个新的领域。

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

谢谢!

我找到了解决自己问题的方法。 所以,我想我会在这里发帖,以帮助将来可能遇到类似问题的人。

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

感谢Hans的回复。 我做了一些调查并发送两条消息,就像最初的例子确实模拟了一个“按键”,但速度非常快。 它不适用于移动命令,但是当操作键被“轻敲”而不是按下时,它将是理想的。

此外,发送扫描代码时会忽略虚拟键字段。 MSDN对此主题有以下说法:

“设置KEYEVENTF_SCANCODE标志以根据扫描代码定义键盘输入。这对于模拟物理按键非常有用,无论当前使用哪个键盘。键的虚拟键值可能会根据当前键盘布局或内容而改变按下其他键,但扫描码总是一样的。“

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

我试图模拟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])); }