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

标签 c# directinput sendinput

我正在尝试为自定义游戏 Controller 应用程序模拟键盘命令。因为我需要在 DirectInput 环境中模拟命令,所以大多数常用方法都不起作用。我知道使用 Hook 100% 有效,但我正在努力寻找更简单的实现方式。

我进行了大量搜索,发现通过将 SendInput API 与扫描码一起使用而不是虚拟键应该可以工作,但它似乎表现得像键的“粘着”。我已经发送了 KEYDOWN 和 KEYUP 事件,但是当我尝试在 DirectInput 环境中发送消息时,游戏的行为就像按键被按住一样。

例如,如果我模拟一个“W”按键并在第一人称射击游戏中将该键映射到“向前移动” Action ,一旦我进入游戏,下面的函数将导致角色向前移动。但是,只需发出一次命令,它就会使角色无限向前移动。

这是我正在调用的 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());
    }
}

我不确定这种方法是失败的原因,还是只是我遗漏了一些愚蠢的东西。如果我不必使用钩子(Hook),我不想让我的代码过于复杂,但这对我来说也是一个新领域。

非常感谢任何人可以提供的帮助。

谢谢!

最佳答案

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

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

关于c# - 在 DirectInput 应用程序中使用 SendInput API 模拟键盘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3644881/

相关文章:

c# - 是否可以使用条件运算符为可空值赋值?

c# - 用PagedList分页,效率高吗?

c# - SendInput Ctrl + C 然后通过 Clipboard.GetText 检索复制的内容不起作用

c++ - SendInput() 不等于在 C++ 中手动在键盘上按键?

java - 在 Java 中使用 directinput 发送 key ?

java - 使用 Java JNA 和 SendInput() 发送键盘输入

c# - LINQ to Entity Framework 多对多预加载问题

c# - 使用 EF 在 MVC 4 局部 View 上显示相关实体

c++ - 在窗口模式下设置/获取我的绝对鼠标位置

.net - 低级鼠标钩子(Hook)和 DirectX