tags:

views:

739

answers:

3

I've been working on injecting input into a WPF application. What makes this project hard is that I need to be able to inject the input into the application even though it's running in the background (i.e. another application has the input focus). Using the SendInput() function is therefore out of the question.

So far, I've got keyboard input working but am having trouble injecting mouse input.

I used Spy++ to observe the window messages that get sent to the WPF window when I physically click the mouse button. I then simply craft these same mouse messages (such as WM_LBUTTONDOWN and WM_LBUTTONUP) manually and send them explicitly to the WPF window to emulate mouse input.

Unfortunately, this doesn't work as expected (not even when I, for testing purposes, have set the WPF window as the foreground window).

I've added a button to my test WPF window which when clicked displays a message box. Injecting the appropriate mouse messages when I've manually positioned the cursor over the button doesn't cause the button to be clicked, however (i.e. the clicked event isn't fired by the WPF framework).

If I add a handler for mouse clicks on the actual dialog (the client area), that handler does get called if I position the cursor over the dialog itself and inject the same window messages as before:

this.MouseLeftButtonDown += WndMouseDown;

public void WndMouseDown(object sender, EventArgs e)
{
   ...
}

Strangely enough, if I change the push mode of the button to Press (i.e. it's considered clicked on mouse down rather than the default mouse up), the button clicked event is now fired when I inject the same messages as before. (It's worth mentioning that the handler from the example above correctly fires for both mouse downs and ups, so it'd seem the WPF framework does process both messages successfully.)

It seems like there are some other criteria that need to be fulfilled in order for a mouse clicked event to be fired by the WPF framework. Does anybody know how mouse input is handled internally in WPF, or why it's not interpreting my mouse up and down messages as a click on the button?

(It's worth mentioning that this approach [sending window messages] works fine on ordinary Win32 windows, such as the Start->Run dialog. The difference here is that WPF only has one physical Win32 window and the rest is WPF specific, which means all window messages go to that top-level window rather than the actual button.)

I've been searching high and low for an answer to this and would appreciate any thoughts or ideas.

+1  A: 

Use UI Automation to do this - trying to manually simulate input via window messages is a textbook mistake, like trying to start a land war against Russia.

Paul Betts
As I said in my previous comment, that won't really work for several reasons. Using e.g. the TestAPI framework to simulate mouse clicks translates into SendInput() under the hood.Going another route of identifying which control is under the cursor and then invoking that control explicitly has its own problems due to control patterns, which vary for the controls:http://social.msdn.microsoft.com/Forums/en-US/windowsaccessibilityandautomation/thread/25972b31-ac1a-401e-aab3-b7c8b1b96694#a776adcb-e6d2-43fa-b78c-da6de7628d4e
dreijer
A: 

Your strategy is basically sound but in order to send a message to a window owned by another process you must first register the message.

Here is an article explaining the whole business. The sample code is unfortunately in VB but I'm sure that won't stop you.

Peter Wone
Thanks for the input (excuse the pun :). I'm actually injecting the messages from within the same process on the same GUI thread, so there are no permission issues. As I wrote in my initial question, the messages are correctly processed by the WPF framework sometimes (like when clicking on the dialog itself).
dreijer
Yeah I just noticed the part of your question where you mention that you are getting some mouse events firing, which implies it's all inproc.
Peter Wone
+1  A: 

I'd highly suggest going the UIAutomation route. You create an AutomationElement by window handle. Crawl to the button and invoke it. I'd just like to know how you managed to get the keyboard input working. I am currently trying to resolve the converse issue. How to get a WPF window (I've managed to get a hWnd to it via Win32 calls), to respond to virtual keyboard messages. I've logged ++spy sessions on the window in question and replicated it's input without success.

Dave
The problem with this is that I don't necessarily know what (type of control) the user is clicking on. Like, it could be a button, it could be a scrollbar, it could be the Web Browser control. I'm having a hard time visualizing how to support all these controls without wrapping every single type.For keyboard input, make sure you're posting the messages that should be posted (i.e. PostMessage() vs. SendMessage()) . You could also try posting while running in the context of the GUI thread.
dreijer