c# - SendKeys 与游戏 : with some characters it works, 但有一些它没有

标签 c# .net winapi input sendkeys

我想用 模拟游戏中的输入发送 key ,但我很难过。

如果我将它与字母 一起使用电话 ,而 Minecraft 中的光标位于文本框中(在主菜单中),它可以工作,字母 T 写在文本框中。

但与 {ESC} 它不起作用。没发生什么事。如果我手动按下它,它会返回上一个菜单。 (正如它应该)

在某些应用程序中,ESC 可以工作:

  • 它适用于 Discord、Sourcetree、Slack、Chrome、CS2D、
  • 但由于某种原因,它不适用于 Minecraft、Spelunky、Half-Life。

  • 上面提到的所有应用程序都在 中。窗口模式 .

    另一个问题:
  • 如果我发送 2 在文本字段中到 Minecraft,它可以正常工作,写入 2。
  • 但是如果我在播放时发送它,则没有效果。 (角色应该切换到元素栏#2)
  • 与 "" 相同(空白) .在文本字段中它可以工作,但角色不会在游戏中跳跃。

  • 代码:
        [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
        [DllImport("USER32.DLL")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
    
        public Form1()
        {
            InitializeComponent();
            IntPtr minecraftHandle = FindWindow("GLFW30", "Minecraft* 1.15.2");
    
            if (minecraftHandle == IntPtr.Zero)
            {
                MessageBox.Show("Minecraft is not running.");
                return;
            }
    
            SetForegroundWindow(minecraftHandle);
            SendKeys.SendWait("{ESC}");
        }
    

    我在没有焦点切换的情况下尝试了它:通过分配 SendKey 调用热键 ,因此在调用 SendKeys 时目标应用程序可以成为焦点。

    结果是一样的:\

    最佳答案

    不要使用 SendKeys.Send用于在不同运行时工作的进程之间的消息传递
    SendKeys.Send方法来自 System.Windows.Forms命名空间。

    这意味着它不是 Windows 输入模拟器,而只是 Windows 窗体应用程序的小 helper 。无法保证此方法可与不同(非 .NET)运行时系统上的另一个进程一起使用。

    尽管 SendKeys.Send方法使用 native Windows API , 它只发送 的按键消息固定时间段 ,因此游戏帧处理可能没有时间捕获此消息来管理它。因此,您可能需要单独的命令来发送有关按键按下和按键向上事件的消息。

    不要使用 SendKeys用于与其他进程进行消息传递的 API,尤其是游戏 .
    此外,游戏可以使用保护系统来摆脱自动机器人,这些机器人可以阻止来自操作系统编程输入的任何消息

    那么,你可以使用什么?

    首先可以尝试使用PostMessageuser32.dll系统库:

    const uint WM_KEYDOWN = 0x0100;
    const uint WM_KEYUP = 0x0101;
    
    [DllImport("user32.dll")]
    static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
    
    // hWnd - Window handler (can be getted by using GetForegroundWindow/FindWindow methods)
    // msg - Key up/down message (WM_KEYUP / WM_KEYDOWN)
    // wParam - Virual key code you need to pass to the window
    // lParam - Additional parameter for set up key message behaviour.
    

    所有虚拟键码都可以在 microsoft docs 网站上找到:
    https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

    不要忘记您需要等待一段时间才能释放 key 。这是必需的,因为游戏在帧之间缓存输入,并且帧有固定的时间来捕捉输入。只需在按键向下和向上消息之间写一些延迟。

    您还可以通过 lParam 设置关键消息行为。见 WM_KEYDOWNWM_KEYUP参数。 WM_KEYDOWN 消息的特殊之处在于,如果您在真实键盘上长时间按下键,操作系统会相应地重复 WM_KEYDOWN 消息。可以通过 lParam 设置重复计数。如果您的消息窗口对单个 keydown 消息没有反应,请使用它。
    PostMessage是低级系统命令,可用于进程之间的消息传递。此命令被保护系统阻止的概率很低(但不是零),而您使用的游戏/进程接收到的概率很高。它还提供了将按键向上和按键向下消息分开的机会。

    如果 PostMessage没用?

    尝试使用硬件扫码而不是 虚拟键码 . this answer 中描述的详细说明如何做到这一点.

    如果保护系统真的很好,PostMessage即使您使用硬件扫描码也会被阻止,您可以尝试的一件事是 使用另一个键盘输入驱动程序或者自己写。该驱动程序必须替换默认系统键盘驱动程序。您可以向它发送消息以与游戏互动。这是 100% 保证 通过键盘与其他进程交互的方式。但是如果您使用公共(public)自定义键盘驱动程序,则保护系统有可能会阻止它。因此,您需要编写自己的驱动程序来在进程之间发送消息。

    关于c# - SendKeys 与游戏 : with some characters it works, 但有一些它没有,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60578711/

    相关文章:

    c# - 无法引用 Microsoft.Device 命名空间

    c# - 如何使用单元测试断言 Linq 集合

    .net - 如何使用 .NET 中的当前区域性将整数转换为字符串?

    c# - 任务被取消,但不清楚原因和方式

    c++ - 如何在编辑控件上自动隐藏滚动条

    c++ - 暂停低级 Hook 的最佳方法(winapi)

    c++ - 如何防止 DirectX Sprite 在将其移出屏幕时被删除

    c# - 如何优化以下 linq to object 查询

    c# - ElasticSearch-过滤类型

    c# - VMR9 与 EVR : How to use ImageCompositor with EVR?