SendKeys with games: with some characters it works, but with some it doesn't

Viewed 1379

I want to simulate input in games with SendKeys, but I have a hard time.

If I use it with i.e. the letter T, while the cursor in Minecraft is in a textbox (in the main menu), it works, the letter T is written in the textbox.

But with {ESC} it doesn't work. Nothing happens. If I press it manually, it backs to the previous menu. (as it should)

With some applications ESC works:

  • It works with Discord, Sourcetree, Slack, Chrome, CS2D,

  • but for some reason it doesn't work with Minecraft, Spelunky, Half-Life.

All of the applications mentioned above were in windowed mode.

Another issue:

  • If I send 2 to Minecraft while in a text field, it works correctly, 2 is written.

  • But if I send it while I'm playing, there is no effect. (The character should switch to Item Slot #2)

  • Same with " " (whitespace). In text fields it works, but the character won't jump in the game.

Code:

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

I tried it without focus switching: by assigning the SendKey calls to a hotkey, so the target application can be in focus when the SendKeys are called.

The results are the same :\

1 Answers

Do not use SendKeys.Send to messaging between processes working on different runtimes

SendKeys.Send method is from System.Windows.Forms namespace.

This means it is not a Windows Input simulator, but just a little helper for Windows Forms applications. There is no guarantee this method work with another process on different (not .NET) runtime system.

Despite the fact that SendKeys.Send method uses native Windows API, it send key pressing message only of fixed period of time, so game frame handling may not have time to catch this message to manage it. So you may need for separate commands to send message about key down and key up events.

Do not use SendKeys API for messaging with another processes, especially with games.
Also, games can use protection system to rid of automatic bots that can blocks any messages from operation system programming input

So, what you can use?

First, you can try to use PostMessage of user32.dll system library:

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.

All virtual key codes can be found on microsoft docs website:
https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

Don't forget that you need to wait some time before key releasing. This is needed because games cache inputs between frames, and frame has fixed time to catch input. Just write some delay between key down and up messages.

Also you can set up key message behaviour by lParam. See WM_KEYDOWN and WM_KEYUP parameters. Special thing about WM_KEYDOWN message is if you pressing key on real keyboard long time, operation system repeating WM_KEYDOWN message accordingly. Repeating count can be setted up through lParam. Use it if window you messaging does not react on single keydown message.

PostMessage is low-level system command that can be used for messaging between processes. This command has a low probability to be blocked by protection system (but not zero) and high probability to be received by the game/process you working with. Also it provides opportunity to separate key up and key down messages.

What if PostMessage didn't work?

Try to use hardware scan code instead of virtual key code. Detailed explanation how you can do that described in this answer.

If protection system is really good and PostMessage is blocking even if you use hardware scan code, one thing you can try is to use another keyboard input driver or write it yourself. That driver must replace default system keyboard driver. And you can message it to interact with game. This is the 100% guarantee way to interact with other process through keyboard. But if you use public custom keyboard drivers, there is some probability that protection system blocks it. So you need to write your own driver to message between processes.

Related