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

发布于 2024-09-17 21:52:38 字数 1407 浏览 9 评论 0原文

我正在尝试模拟自定义游戏控制器应用程序的键盘命令。因为我需要在 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());
    }
}

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

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

谢谢!

I'm trying to simulate keyboard commands for a custom game controller application. Because I'll need to simulate commands in a DirectInput environment most of the usual methods don't work. I know that using a hook would work 100% but I'm trying to find an easier implementation.

I've done quite a bit of searching and found that by using the SendInput API with Scancodes instead of virtual keys should work, but it seems to behave like the key's are "sticking". I've sent both the KEYDOWN and KEYUP events but when I attempt to send the message in a DirectInput environment, the game acts as if the key is being held down.

For instance, if I simulate a "W" keypress and have that key mapped in a First Person Shooter to the "move forward" action, once I'm in game, the function below will cause the character to move forward. However, just by issuing the command once, it will move the character forward indefinitely.

Here is a code snippet (in C#) for the SendInput function I'm calling:

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

I'm not sure if this method is a lost cause, or if there is just something silly I'm missing. I hate to over complicate my code if I don't have to by using hooks, but this is also new territory for me.

Any help anyone can give is much appreciated.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

被翻牌 2024-09-24 21:52:38

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

keyup 命令无法正常工作,因为仅发送扫描代码时,keyup 必须与扫描代码标志进行 OR 运算(有效启用两个标志),以告诉 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

I've found the solution to my own problem. So, I thought I'd post here to help anyone who may have similar issues in the future.

The keyup command was not working properly because when only sending the scan code, the keyup must be OR'ed with the scan code flag (effectively enabling both flags) to tell the SendInput() API that this is a both a KEYUP and a SCANCODE command.

For instance, the following code will properly issue a scan code key-up:

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

Thanks to Hans for the response. I did some investigating and sending two messages back to back like the original example does indeed simulate a "keypress" but it's very fast. It would not work well for a movement command, but would be ideal when action keys are to be "tapped" and not held down.

Also, the virtual key field is ignored when sending a scan code. MSDN had the following to say on the subject:

"Set the KEYEVENTF_SCANCODE flag to define keyboard input in terms of the scan code. This is useful to simulate a physical keystroke regardless of which keyboard is currently being used. The virtual key value of a key may alter depending on the current keyboard layout or what other keys were pressed, but the scan code will always be the same."

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

最近可好 2024-09-24 21:52:38

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

I was trying to simulate keyboards for a presentation in Flash, and was also having the same problem. It did work for apps like Notepad, but not for flash. After hours of googling I finally made it work:

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]));
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文