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);
}
}
}
}
Options for sending key presses
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.
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.
Related
I'm attempting to simulate keyboard input to an window programmatically. I realize SendInput is the optimal method here, but I need to be able to send this keystroke to a window that is not currently in focus.
As such, I'm trying to use PostMessage instead.
The following works for simulating the input of the F5 key in notepad, but not for the spacebar.
using System.Diagnostics;
using System.Runtime.InteropServices;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool PostMessage(IntPtr hWnd, UInt32 msg, uint wParam, int lParam);
const uint WM_KEYDOWN = 0x0100;
const uint WM_KEYUP = 0x0101;
const int VK_SPACE = 0x20;
const int VK_F5 = 0x74;
while(true)
{
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process process in processes)
{
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_F5, 0);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_F5, 0);
Thread.Sleep(5000);
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_SPACE, 0);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_SPACE, 0);
}
}
Any idea why? Is there a better why I should go about this?
I already got mouseclick to work with one issue. If I set delay between WM_MBUTTONDOWN and WM_MBUTTONUP to more than 5 ms it won't work. Why is that?
[DllImport("user32.dll")]
public static extern IntPtr
PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public void MouseClick(short x, short y)
{
IntPtr lParam = (IntPtr)((x & 0xFFFF) | ((y & 0xFFFF) << 16));
const uint WM_MBUTTONDOWN = 0x0201;
const uint WM_MBUTTONUP = 0x0202;
PostMessage(hwnd, WM_MBUTTONDOWN, IntPtr.Zero, lParam);
//System.Threading.Thread.Sleep(100);
PostMessage(hwnd, WM_MBUTTONUP, IntPtr.Zero, lParam);
}
Now, my goal is to send keystroke to minimized window (directx game). I tried using similar approach but nothing works. I bet that wParam is the problem.
const uint WM_KEYDOWN = 0x100;
const uint WM_KEYUP = 0x101;
const uint WM_CHAR = 0x102;
const int VK_W = 0x57;
const int VK_S = 0x53;
PostMessage(hwnd, WM_KEYDOWN, (IntPtr)VK_W, IntPtr.Zero);
PostMessage(hwnd, WM_KEYUP, (IntPtr)VK_W, IntPtr.Zero);
How do I get this to work?
Remember that the window will be minimized so I can't use SendKeys or SendInput.
UPDATE:
This code works, but only for typing in in-game chat.
const int VK_G = 0x47;
const uint WM_KEYDOWN = 0x100;
PostMessage(hwnd, WM_KEYDOWN, VK_G, 0);
I can't use it to walk forward etc.
I am using the following code in C# to simulate the keyboard pressing:
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("user32")]
private static extern bool SetForegroundWindow(IntPtr hwnd);
void sim_key(string text, string proc)
{
var process = Process.GetProcessesByName(proc).FirstOrDefault();
if (process != null && process.MainWindowHandle != IntPtr.Zero)
{
SetForegroundWindow(process.MainWindowHandle);
SendKeys.Send(kat_id);
}
}
I tested it on Windows Xp, 7, 8.1, 10, and Server 2012. On windows 7 and Xp, the external app window is handled properly, however the keys are not being sent. On systems above Win 7 everything is correct. How should I fix it?
Edit:
I checked it on .NET 4.0 Client Profile and .NET 4.6.1 on 32 and 64 bit machines, but results are the same as described above.
I think you can go with this rough code
const int WM_CHAR = 0x0102;
const int WM_KEYDOWN = 0x0100;
if (process != null && process.MainWindowHandle != IntPtr.Zero)
{
PostMessage(process.MainWindowHandle, WM_CHAR, (int) <a character you want to send> , 1);
}
Use
PostMessage(process.MainWindowHandle, WM_KEYDOWN, 13, 1);
to send the "Enter".
Note that you can use SendMessage instead of PostMessage. The difference is the last one does not wait the processing of the key. They have the same signature:
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
You don't need calling SetForegroundWindow(process.MainWindowHandle); if you just want to send keys.
Thank you for your answer. I tried using PostMessage to send Ctrl+F10 to the other window. Here is my code:
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int VK_CONTROL = 0x11;
public const int VK_F10 = 0x79;
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_CONTROL, null);
PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_F10, null);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_F10, null);
PostMessage(process.MainWindowHandle, WM_KEYUP, VK_CONTROL, null);
However, it doesn't pass the keys to the app.
I am currently developing a program that will send a "key press" (the letter A or 0x41 in virtual key codes) to another program (notepad) every X seconds.
The problem is that for it to work I need the other program (notepad) to be in the FOREGROUND, example :
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process proc in processes) {
SetForegroundWindow(proc.MainWindowHandle);
// Do Whatever
}
Thread.Sleep(1000);
Is there a way to do that WITHOUT notepad having to be in the foreground ?
Like something that could run in the background ?
You could do it via winApi. Try to use SendMessage
According to this link you can do following:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(ushort k)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
I have a function in WinForms C# app that sends a string (from a textbox) to an active CMD window, using a button.
Unfortunately, if the textbox contains multiple zeros (0000x000F22000), it returns just one zero: 0x0F220
How can I fix this?
private void but_run_Click(object sender, EventArgs e)
{
uint wparam = 0 << 29 | 0;
int i = 0;
for (i = 0; i < textBox1.Text.Length; i++)
{
//PostMessage(child, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
PostMessage(cmdHwnd, WM_CHAR, (int)textBox1.Text[i], 0);
}
PostMessage(cmdHwnd, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
}
You could try using the lParam to specify repeat key presses. Also, pay attention - PostMessage has lParam as the fourth parameter (wParam is before lParam), you're mixing it up in your code.
Next, don't use (int)someChar. You should use the Encoding classes to get byte values from chars.
Use SendMessage instead of PostMessage. PostMessage is asynchronous and can complicate a lot of stuff for you. You don't need the asynchronicity, so don't use it.
Next, why use WM_CHAR? I'd say WM_SETTEXT would be way more appropriate - you can send the whole text at once. Just be careful about using the native resources (eg. the string). To make this as easy as possible, you can make yourself an overload of the SendMessage method:
const uint WM_SETTEXT = 0x000C;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, unit Msg,
IntPtr wParam, string lParam);
You can then simply call:
SendMessage(cmdHwnd, WM_SETTEXT, IntPtr.Zero, textBox1.Text);
I've managed to do it like this:
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindow(IntPtr ZeroOnly, string lpWindowName);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_CHAR = 0x0102;
public void sendText(string pText, string pCaption)
{
IntPtr wndHndl = FindWindow(IntPtr.Zero, pCaption);
char[] tmpText = pText.ToCharArray();
foreach (char c in tmpText)
{
System.Threading.Thread.Sleep(50);
PostMessage(wndHndl, WM_CHAR, (IntPtr)c, IntPtr.Zero);
}
}
Where pText is the input string and pCaption is title of the window.