[Edit]
Please note, this is brittle in the way described below. Restarting the "client" application generates a new classname based on the Hash of the AppDomain. This means the code below will work once and only once. Please see my other next answer for a less brittle Windows7-specific implementation. If doing this in c++, you will need to call the equivalent code as the second answer.
Yes. In principle, this is easy.
1. Use User32.dll (.net needs to import this)
2. Use
FindWindow
to get an address pointer to the window containing the control.
3. Use
FindWIndowEx
to get an address pointer to the button you want to click
4. Use
SendMessage
to place a button click message onto the windows message queue.
Now it depends which language you are using, I will assume c# here (and give links to a c++ version). The C++ version is simpler as it can import windows assemblies directly.
First import the relevant methods from user32 (I tested on 64 bit win7, so don't be put off by the name):
private const int BM_CLICK = 0x00F5;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
Now for the business end I created a dummy app. The form had a caption "FormToBeCaptured" (this is important). On it there was a text box and a button with the text "Add" in it. The event handler was hooked up so that if the button is pressed it appends "a" to the existing text in the text box.
IntPtr hwndChild = IntPtr.Zero;
IntPtr hwnd=IntPtr.Zero;
hwnd = FindWindow(null, "FormToBeCaptured");
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1", "Add");
SendMessage(hwndChild, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
The first two lines initalize empty pointers. The 3rd line finds the window via its caption, you may need to find another way to do this, but this is the simplest.
This line was problematic (it gets the Pointer to the button to be pressed via its caption:
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1", "Add");
SendMessage(hwndChild, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
The class name was "Button" in windows XP. In Windows 7 (possibly Vista too) this seems no longer to be the case. I used SPY++ (it is installed with Visual Studio) to examine the window. It seems the class name is now "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1".
This seems a bit brittle to me - you may need to check the class name on your machine, it may break on update too. If the class name is different in spy++, change it obviously.
The last line sends a message to the Windows Message Queue with the pointer to the button, the message as BM_CLICK and the lParam and wParam as
IntPtr.Zero
, in effect null.
As I say this is brittle, it will probably change between windows versions and possibly between updates. I've suggested spy++, but also you might find the following useful:
pinvoke.net[
^] - a wiki of interop calls for .net
C++ example here with VB6[
^] Note this is targeted at earlier windows versions.