4

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 :\

Tudvari
  • 2,715
  • 2
  • 14
  • 33
  • Have you tried adding ` ` to `App.config` (or calling [SendInput()](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput) directly)? – Jimi Mar 07 '20 at 14:59
  • I added this app settings under configuration, but didn't help. How could I call SendInput() directly? – Tudvari Mar 07 '20 at 15:05
  • If you added `` correctly to `App.config`, you already did. With that setting, `SendKeys.SendWait()` calls `SendInput()`. I have no idea what `Esc` is supposed to do here (close a Window, maybe?), so you have to test other ways. -- You said that `SetForegroundWindow` works, so (since it might not), maybe the app is expecting something different or the currently focuses object doesn't bubble up the key press, or... – Jimi Mar 07 '20 at 15:12
  • Oh, I see. Then it doesn't work either :\ (Letter T is still working) – Tudvari Mar 07 '20 at 15:14
  • 1
    If you're trying to close a Window, maybe send `WM_CLOSE` (with `SendMessage`, of course), see if that works. – Jimi Mar 07 '20 at 15:22
  • That works: the windows is instantly closed. – Tudvari Mar 07 '20 at 15:40
  • @Jimi Turns out it works with other applications, and with Minecraft it doesn't. – Tudvari Mar 09 '20 at 09:49
  • @Jimi I edited the post again, I got even more clues. – Tudvari Mar 09 '20 at 12:20
  • I have tried https://archive.codeplex.com/?p=inputsimulator which let's you simulate KeyDown/KeyUp as well. While in console normal chars are written. ESC and ENTER don't have an effect. Don't know why. DirectX/OpenGL problem? Idk. – Sven Bardos Mar 10 '20 at 18:29

1 Answers1

3

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://learn.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.

picolino
  • 4,856
  • 1
  • 18
  • 31
  • The **only** guaranteed way? *There is no guarantee this method work with another process on different (not .NET) runtime system.* The WinForm SendKeys method uses an unmanaged native method: `eventsSent += UnsafeNativeMethods.SendInput(1, currentInput, INPUTSize);`, its line 655 here: https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/SendKeys.cs,2c0710ad7203a696 – Jeremy Thompson Mar 13 '20 at 03:26
  • @JeremyThompson native `SendInput` method calling directly on foreground window **only**, while code in question operate with window handler. `PostMessage` method can be used even if target window not in foreground, so it's just more flexible and low-level solution. – picolino Mar 13 '20 at 08:01
  • @JeremyThompson and yes, self-developed keyboard driver is the **only, 100% guaranteed** way to pass key pressing to other windows. If you have any other thinks about it, it will be nice if you share it with me and community. – picolino Mar 13 '20 at 08:08
  • 1
    This is the correct answer, but there are also options between `PostMessage()` and writing your own keyboard driver such as [keybd_event](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event) – tenfour Mar 13 '20 at 09:13
  • Sadly it still doesn't work with ESC with Spelunky and Minecraft. (But i.e. with the letter "t" it works this way as well. Could it be possible that all these games have these kind of protections? But with Diablo, ESC, and any input works with SendKeys and PostMessage as well. – Tudvari Mar 13 '20 at 22:09
  • By the way, I tried only with the KEYDOWN, because ESC functions in these example games as soon as I press down the button, it won't wait for a release So I think it wouldn't make any difference. – Tudvari Mar 13 '20 at 22:11
  • Guys, is Minecraft DirectX or OpenGL? The former doesn't receive Ring 3 commands. – Jeremy Thompson Mar 14 '20 at 01:33
  • Now I correctly calculated an lParam, instead of just passing 0 as you suggested, and now it seems to work. Hopefully will pass the further tests as well. – Tudvari Mar 14 '20 at 07:07
  • @Tudvari I'll perform some other research and will update my answer according your information. Thank you. – picolino Mar 14 '20 at 07:18
  • I got the solution here, maybe it could be helpful: https://stackoverflow.com/questions/10280000/how-to-create-lparam-of-sendmessage-wm-keydown – Tudvari Mar 14 '20 at 07:41
  • @Tudvari i've updated my answer accordingly. You can check it above. – picolino Mar 14 '20 at 07:43
  • @JeremyThompson as I know Minecraft working on OpenGL tech. – picolino Mar 14 '20 at 08:03