Naura
Naura

Reputation: 27

Infinity pressed keys 'PostMessage' to hidden window

Hello I'm trying to send keys to hidden game and I have 1 problem, when I send the keys to game it press only the first one and it's not ending e.g like it's pressed W for infinity time and cant send the next key. I think the key it's not going up.

My code:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Naura_2._0
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

        public Form1()
        {
            InitializeComponent();
        }

        private const UInt32 WM_KEYDOWN = 0x0100;
        private const UInt32 WM_KEYUP = 0x0101;
        private const int VK_KEY_W = 0x57;

        private void button1_Click(object sender, EventArgs e)
        {
            IntPtr hWnd = FindWindow(null, "Game Title");

            if (!hWnd.Equals(IntPtr.Zero))

            {
                PostMessage(hWnd, WM_KEYDOWN, VK_KEY_W, 0);
                PostMessage(hWnd, WM_KEYUP, VK_KEY_W, 5);
            }
        }
    }
}

Upvotes: 1

Views: 725

Answers (1)

Kwiksilver
Kwiksilver

Reputation: 855

Options for sending key presses

  1. If the Window must be in focus to send keys presses use SendInput instead of PostMessage as it is designed for this very process. This scenario is quite common with games where they automatically pause if the game window is not focus so sending any keys will likely do nothing.
  2. If you would like the window focus to be optional while sending key presses then PostMessage seems like the only way.

Solution for sending key presses using PostMessage

To "Emulate" a keypress a Virtual Key Codes (the key 'A') and Scan Codes (the location on the keyboard of 'A') and some additional flags, Keystroke Message Flags, are sent.

PostMessage(hWndChild, WM_KEYDOWN, virtualKey, scanCode << 16 | 0x0000001);
PostMessage(hWndChild, WM_KEYUP, virtualKey, scanCode << 16 | 0xC0000001);

You can map between virtualKeys and scanCodes using MapVirtualKey

Full program example

class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern uint MapVirtualKey(uint uCode, uint uMapType);

    [DllImport("user32.dll")]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);

    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);

    const uint WM_KEYDOWN = 0x0100;
    const uint WM_KEYUP = 0x0101;

    const uint MAPVK_VK_TO_VSC = 0x00;

    // virtual key codes
    const uint VK_ESCAPE = 0x1B;
    const uint VK_LSHIFT = 0xA0;
    const uint VK_LCONTROL = 0xA2;
    const uint VK_OEM_2 = 0xBF; // For the US standard keyboard, the '/?' key

    const uint VK_W = 'W';
    const uint VK_A = 'A';
    const uint VK_S = 'S';
    const uint VK_D = 'D';

    // Scan Code for US keyboard layout
    const uint VSC_W = 0x11; // MapVirtualKey('W', MAPVK_VK_TO_VSC);

    const uint VSC_A = 0x1E; // On QUERY MapVirtualKey('A', MAPVK_VK_TO_VSC);
                             // On AZERY MapVirtualKey('Q', MAPVK_VK_TO_VSC);

    const uint VSC_S = 0x1F;
    const uint VSC_D = 0x20;

    const uint VSC_LSHIFT = 0x2A;

    // Write 'w' (yes lower case) in Notepad.exe (*Untitled - Notepad)
    static void Example1()
    {
        uint keyPress = VK_W;
        uint scanCode = MapVirtualKey(keyPress, MAPVK_VK_TO_VSC);
        IntPtr hWnd = FindWindow(null, "*Untitled - Notepad");
        IntPtr hWndChild = FindWindowEx(hWnd, IntPtr.Zero, "Edit", string.Empty);

        PostMessage(hWndChild, WM_KEYDOWN, keyPress, scanCode << 16 | 0x0000001);
        Thread.Sleep(42);
        PostMessage(hWndChild, WM_KEYUP, keyPress, scanCode << 16 | 0xC0000001);
    }

    // Write 'help' (yes lower case) in Notepad.exe (*Untitled - Notepad)
    static void Example2()
    {
        string text = "help";

        IntPtr hWnd = FindWindow(null, "*Untitled - Notepad");
        IntPtr hWndChild = FindWindowEx(hWnd, IntPtr.Zero, "Edit", string.Empty);

        foreach (var c in text)
        {
            uint keyPress = char.ToUpper(c);
            uint scanCode = MapVirtualKey(keyPress, MAPVK_VK_TO_VSC);

            PostMessage(hWndChild, WM_KEYDOWN, keyPress, scanCode << 16 | 0x0000001);
            Thread.Sleep(42);
            PostMessage(hWndChild, WM_KEYUP, keyPress, scanCode << 16 | 0xC0000001);
            Thread.Sleep(142);
        }
    }
}

Some additional background reading

https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names

Arriving at the solution

Visual Studio comes with a program called Spy++ which can be used to view the message queue of any window.

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\spyxx.exe
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\spyxx_amd64.exe

where spyxx.exe is used to inspect 32bit processes and spyxx_amd64.exe is used to inspect 64bit process. Using Spy++ and the documentation, Keystroke Message Flags, I was able to view the WM_KEYDOWN and WM_KEYUP and imitate the desired behavior using PostMessage.

Potential short falls

  • You can send a shift key and most games should receive the shift key but operations like [Shift]+[A] in a textbox will result in 'a'. Windows uses some other mechanism to generate the capital letters.
  • Some programs may used GetKeyboardState it which case posting messages won't do anything since it queries the keyboard and ignores the messages. Note the there is a SetKeyboardState which can be used to set which keys are pressed and hence communicate with these programs.

Upvotes: 1

Related Questions