I'm trying to simulate a keystroke ("z") on another window, a game in particular.
I've implemented in my program a simple timer that sends the key with PostMessage every 1000ms, but the "action" related to the pressing of that key doesn't start.
I've analysed the Messages sent to the window of the game with Spy++, but the strange thing is that i can see the exact sequence of messages (KEYDOWN, CHAR and KEYUP), whether i press it manually or send it through my application. Obviously if i press "z" manually the game's function gets called correctly.
Here i report the messages that i get from Spy++, the first 3 are from me hitting z manually, the last 3 are from my software.
Messages from Spy++
And here i include the code that i'm using
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, uint lParam);
private void SendKeys(IntPtr proc_hwnd, IntPtr key)
{
PostMessage(proc_hwnd, 0x100, key, 0x002C0001);
Thread.Sleep(100);
PostMessage(proc_hwnd, 0x101, key, 0xC02C0001);
}
Process[] proc;
private void Button1_Click(object sender, EventArgs e)
{
proc = Process.GetProcessesByName("Proc_name");
if (proc.Length == 0)
{
MessageBox.Show("No process found");
return;
}
tmr_raccogli.Interval = (int)(num_raccogli.Value * 1000);
tmr_raccogli.Start();
}
private void tmr_raccogli_Tick(object sender, EventArgs e)
{
SendKeys(proc[0].MainWindowHandle, (IntPtr)Keys.Z);
}
I don't get why it's not working since from that point of view the two actions are identical.
i don't have an exact answer but i might able to guide you in the right direction.
The focus and foreground state of your window maybe important as well as the input thread.
SetFocus
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus
SetForegroundWindow
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
AttachThreadInput
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
Your windows receives the messages but i decides to ignore them, this depends on how the implementation of windows msgs is programmed in the target. I can remember creating something similar and AttachThreadInput fixed most of my code interoperability problems.
Related
I have a button on which I press it sends F5 to the application "Tibia". Tibia is not focused, and it sends the F5 in the background without a problem.
I wonder how can I send a string of text like "hello world".
Or send a CTRL+V. That way I could set my clipboard text to "hello world" first, and then send it.
It seems like I can only press 1 key at a time no matter what I do.
Please help me. Here's my code:
const UInt32 WM_KEYDOWN = 0x0100;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
public Form1()
{
InitializeComponent();
}
// Sends F5 to Tibia
private void button1_Click(object sender, EventArgs e)
{
Process[] processes = Process.GetProcessesByName("Tibia");
foreach (Process proc in processes)
{
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, (int)System.Windows.Forms.Keys.F5, 0);
}
}
Note: I do NOT want to focus on the Tibia window when I do this. It should be done "in the background" like now with the F5 press.
I send mouse events to another application in the following way. The problem is, this works for some applications but not for others.
Why?
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int downclick = 0x201;
private const int upclick = 0x202;
IntPtr handle = IntPtr.Zero;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
foreach (Process p in Process.GetProcessesByName("mspaint"))
{
IntPtr handle = p.MainWindowHandle;
int X = 50;
int Y = 380;
IntPtr lParam = (IntPtr)((Y << 16) | X);
IntPtr wParam = IntPtr.Zero;
SendMessage(handle, downclick, wParam, lParam);
SendMessage(handle, upclick, wParam, lParam);
}
}
}
}
Using Spy++ I see that the application recieves the following data:
<00062> 0004052C S WM_LBUTTONDOWN fwKeys:0000 xPos:50 yPos:380
<00063> 0004052C R WM_LBUTTONDOWN
<00064> 0004052C S WM_LBUTTONUP fwKeys:0000 xPos:50 yPos:380
<00065> 0004052C R WM_LBUTTONUP
I assume that the events themselves are correct. But I don't know why it works for some software but not for others. How can I send mouse messages from one window to another?
The software where I want to send the messages is not always visible.
Is it possible at all?
No it's not possible in any reliable way - as you've found out in your testing. The mouse messages are only one part of the input. Windows keeps an input state and just sending messages will not update that input state. And you're also ignoring mouse move messages, etc.
For example in your WinForms application you can use the MousePosition property to get the current mouse positon. Sending messages can't simulate that.
Also you can't send the mouse message to the main window handle, you would have to find the exact button you want to click on and send the message directly to the correct button.
So maybe it will work if the application is only listening for mouse messages this will work, but if not they it won't.
They supported way to simulate mouse clicks, is the SendInput function. But that won't work with minimized applications. It literally goes through the entire Windows input process and will move the mouse cursor - which means that the application has to be visible on the screen.
Here's some information, it talks about keyboard events, but similar logic applies:
http://blogs.msdn.com/b/oldnewthing/archive/2005/05/30/423202.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2010/12/21/10107494.aspx
I've got a C# winforms application that runs in the background, listening for hotkeys to be pressed. When a hotkey is pressed, my form makes a brief appearance. The form is always running, but set to hidden until I receive a hotkey event, at which time I set the visible property to true. The code looks like this:
void hook_volumeDown(object sender, KeyPressedEventArgs e)
{
this.Visible = true;
}
It should be noted that the topmost property of this form is set to true.
The really odd part is, after my C# app has stolen focus from another application, it will never do it again. For example: I launch my app, then launch some fullscreep app like Team Fortress 2. Then I press my hotkey. Team Fortress 2 minimizes, and I see my form. Then, however, I can restore TF2, and press my hotkey again all I want (with the desired effect), and TF2 will remain focused.
At any rate, I'm looking for a way to fix this. I've found a lot of questions here covering similar problems, but all of them are related to creating/launching a new form, not making an existing one visible (unless I missed something). I could rework the application to create a new form every time I need one, but that would entail creating yet another form to be invisible all the time just to wait for hotkey events, so I'd rather leave it as it is.
Any ideas?
I think you problem is related to the fact that Visible = true behaves differently between the first and subsequent calls. The first time visible is called and the window handle has not been created, the Window is created by calling CreateWindowEx which has some style parameters which controls how the window should behave. I think you need to make sure that the window is created with the style WS_EX_NOACTIVATE, which you can do by overriding CreateParams.
Other things to try out:
1) The ShowWindow function (used by Visible = true) ignores the focus parameter the first time it is called (http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx) if the program provides a STARTUPINFO structure. Dig into reflector and find out if the Form class provides a STARTUPINFO structure and if so, how to manipulate it.
2) The Form has a ShowWithoutActivation property than can be overriden and set to true, have you overriden this?
Sorry for the "no exact answer", but I hope this at least gives you some starting points for further investigation. Good luck.
Seeing KeyPressedEventArgs being used in your function looks really strange. Hot keys can be implemented by P/Invoking the RegisterHotKey() API function. It sends a message to your window when the hot key is pressed. Here's an example of a form that's invisible at start up, springs alive when you press the hot key. Ctrl+Alt+U in this case:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
private const int MYKEYID = 0; // In case you want to register more than one...
public Form1() {
InitializeComponent();
this.FormClosing += (s, args) => UnregisterHotKey(this.Handle, MYKEYID);
}
protected override void SetVisibleCore(bool value) {
if (value && !this.IsHandleCreated) {
this.CreateHandle();
RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);
value = false;
}
base.SetVisibleCore(value);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) {
this.Visible = true;
if (this.WindowState == FormWindowState.Minimized)
this.WindowState = FormWindowState.Normal;
SetForegroundWindow(this.Handle);
}
base.WndProc(ref m);
}
// P/Invoke declarations
private const int WM_HOTKEY = 0x312;
private const int MOD_ALT = 1;
private const int MOD_CONTROL = 2;
private const int MOD_SHIFT = 4;
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hWnd, int id, int modifier, Keys vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
}
}
Note that the SetForegroundWindow() function is the rub, possibly also the source of the problem you describe in your question. Windows doesn't permit an app to shove a window in the user's face when the user is actively using another window. At least several seconds of inactivity must expire before it will allow the window to steal the focus. With the given code, that is easy enough to see, the taskbar button of your form will be blinking. Avoid setting the ShowInTaskbar property to false. It isn't necessary to do so with this code, the taskbar button won't show up until the hot key is pressed.
System.Diagnostics.Process exposes a StreamWriter named StandardInput, which accepts only characters as far as I know.
But I need to send keystrokes as well, and some keystrokes don't map well to characters.
What should I do?
You are mixing input streams with control signals. A console process has a default input stream which you can control with the StandardInput, as you already know. But Ctrl-C and Ctrl-Break are not characters sent to the process through this stream, but instead they are instead control signals that the process receives using the registered signal handlers, see CTRL+C and CTRL+BREAK Signals:
By default, when a console window has
the keyboard focus, CTRL+C or
CTRL+BREAK is treated as a signal
(SIGINT or SIGBREAK) and not as
keyboard input.
To send fake signals to a process you can use GenerateConsoleCtrlEvent and send either CTRL_C_EVENT or CTRL_BREAK_EVENT. This API has no .Net equivalent, so you have to PInvoke it.
To use it from .NET you simply need to include the function definition:
const int CTRL_C_EVENT = 0;
const int CTRL_BREAK_EVENT = 1;
[DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(
uint dwCtrlEvent,
uint dwProcessGroupId);
There's an input Simulator found here on Codeplex which may do just the job for you.
I am working on a sample code and will post back here shortly, bear in mind the Input Simulator is similar to what was found in the link supplied by Remus...
Edit: I have found that there is a limitation with this, you can definitely get away with the typical System.Windows.Forms.SendKeys.Send method, it does work effectively! , but, the process must have
No redirections of the streams
Cannot be hidden window (this is where it will fail, since the window's handle is nowhere to be seen, no way of bringing it to the foreground to make it active!)
A window showing the process for this to be effective!
In your case, it's a matter of finding the window, set it active via pinvoke 'SetForegroundWindow', and send the sequences ^{BREAK} which sends the Ctrl+Break signal to the process which does work very well (especially if the process is a command line program/batch file). Here's an article on CodeProject that does this exactly and mirrors the SendKeys...I have yet to paste some code into this to demonstrate ....
Edit#2: Actually I am quite surprised...as this code will show (proof of concept)...it is using:
InputSimulator (as mentioned previously)
A windows form that consists of a button, when the form is loaded it automatically runs the class. Upon clicking the button, it posts a ctrl-break to the hidden process
The output stream is indeed redirected and is a hidden window.
The weird thing, is the output is being captured but does not show the results in the debug window, in real-time that is, it is buffered (I guess) until the process terminates, the whole output is shown...
I cheated a bit on the FindWindow API call, because I knew the window's title was and was somehow, able to bring it to the foreground, and using the InputSimulator to send the keystrokes to it...or use the traditional plain old SendKeys function...the reason I had the Thread.Sleep is to ensure that the keystrokes are sent in order to be 'pushed into the keyboard queue of the "active foreground window", which despite that, is hidden'
I used the 'netstat -e 5' command to loop forever, refreshing the results every 5 seconds until it receives a 'Ctrl+C' to break the infinite loop.
public partial class Form1 : Form
{
private TestNetStat netStat = new TestNetStat();
public Form1()
{
InitializeComponent();
using (BackgroundWorker bgWorker = new BackgroundWorker())
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.RunWorkerAsync();
}
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("BGWORKER ENDED!");
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
netStat.Run();
}
void btnPost_Click(object sender, EventArgs e)
{
netStat.PostCtrlC();
System.Diagnostics.Debug.WriteLine(string.Format("[{0}] - {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), this.netStat.OutputData.Replace(Environment.NewLine, "")));
}
}
public class TestNetStat
{
private StringBuilder sbRedirectedOutput = new StringBuilder();
//
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
public string OutputData
{
get { return this.sbRedirectedOutput.ToString(); }
}
public void PostCtrlC()
{
IntPtr ptr = FindWindow(null, #"C:\Windows\System32\netstat.exe");
if (ptr != null)
{
SetForegroundWindow(ptr);
Thread.Sleep(1000);
WindowsInput.InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.CANCEL);
// SendKeys.Send("^{BREAK}");
Thread.Sleep(1000);
}
}
public void Run()
{
System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
ps.FileName = "netstat";
ps.ErrorDialog = false;
ps.Arguments = "-e 5";
ps.CreateNoWindow = true;
ps.UseShellExecute = false;
ps.RedirectStandardOutput = true;
ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
{
proc.StartInfo = ps;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
}
void proc_Exited(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
}
void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data);
}
}
}
Nitpicky aside, I know that the netStat is running off the 'BackgroundWorker' thread, and I directly invoked the 'PostCtrlC' method from the main GUI thread...this is pedantic as a proof-of-concept code, but it does show that it needs to implement 'ISynchronizeInvoke' to make it thread-safe, that aside...it does indeed work.
Have you seen this great tool - AutoIt. This is a scripting tool. To send a backspace you would use Send("{BACKSPACE}")
This is a great tool and it can help in automating many manual clicks/double-clicks/etc.
Is this relevant to your question ?
If you have a Windows Forms window that you can send the keys to, then SendKeys might be an appropriate solution.
For pressing backspace and Ctrl+C, that should be
SendKeys.Send("{BACKSPACE}^C");
I have a windows mobile app that look like this:
class Program
{
static void Main(string[] args)
{
RunHook runHook = new RunHook();
}
}
class RunHook
{
private HookKeys hook;
public RunHook()
{
hook = new HookKeys();
hook.HookEvent += EventForHook;
}
private void EventForHook(HookEventArgs e, KeyBoardInfo keyBoardInfo,
ref Boolean handled)
{
if ((keyBoardInfo.scanCode == 4) && (keyBoardInfo.vkCode == 114))
handled = true;
}
}
It will create a hook into the keyboard (I know that is frowned on by some). My issue is that I need the Main method to never return. This is going to run on devices owned by my company and we are using this to disable the phone hardware keys.
This seems like it should be simple, but I am stuck on it.
On normal .NET I would just call Console.Readline(), but that does not work on Windows Mobile Compact Framework. I have also tried Thread.Sleep(0), but it does not work either.
Thanks for any feedback.
Thread.Sleep(0) sleeps for zero milliseconds.
You probably want Thread.Sleep(Timeout.Infinite).
You might also consider creating an EventWaitHandle:
class Program
{
static public ManualResetEvent StopMain;
static void Main(string[] args)
{
StopMain = new ManualResetEvent(false);
RunHook runHook = new RunHook();
StopMain.WaitOne(); // waits until signalled
}
}
Then, if you were ever ready to exit Main(), you could call (from another thread):
Program.StopMain.Set();
If it going to run on devices that are owned by your company then why not run a small windows program in background. I mean just hide the window. Let it sit in your task bar.
Click on this link for more information on use of notification icon in CF.
not sure this will help but with native code youd call
LRESULT CallNextHookEx(
HHOOK hhk,
int nCode,
WPARAM wParam,
LPARAM lParam
);
in your handler to execute the default handling behaviour, havent tested this but i think if you dont call the next handler in the chain, nothing will happen
more info:
http://msdn.microsoft.com/en-us/library/ms644974%28VS.85%29.aspx
.
the link contains some managed code samples which may help
hth