ARTICLE

Clicking a Button in Another Application

Posted by Sam Hobbs Articles | COM Interop December 12, 2012
This article provides a simple sample of an application that clicks a button in another application.
Reader Level:
Download Files:
 

This article provides a simple sample of an application that clicks a button in another application. The technique could be used for other controls too. For the purpose of a simple sample the program is a console program. The sample will click the "=" button in the Windows Calculator Acesssory.

Note that I am using Windows 7; if the Windows Calculator Acesssory in Windows 8 is different and/or cannot be used in the manner it is used here then I hope you can find another program to use.

The first thing to do is to determine what button to click and the circumstances of when to click it. Execute the Windows Calculator Acesssory. Type "2+2=" into it. The result should be 4 of course. Then click the "=" button. Each time "=" is clicked, the value will be increased by 2. So that is something very simple to automate. We can "manually" execute Calculator and type "2+2=" into it. Then when we execute our sample application it can just click the "=" button and we can see the result.

The next thing to do is to determine the identity of the button to be clicked so our program can click it. With the Windows Calculator Acesssory executing, go to Visual Studio. In the "Tools" menu is "Spy++". If your system is a 64-bit system then you might need to use the 64-bit version of Spy++. The Spy++ toolbar will look like:

Spytoolbar.jpg

Click on the "Find Window" icon; it is a binoculars with a window in the top-left; it looks like:

FindWindowIcon.jpg

That will open a "Find Window" dialog. Now first, ensure that Calculator is visible; at least the "=" button part of it. Then click on the "Finder Tool" icon (the square with a circle in it that looks like a target). The "Find Window" dialog looks like:

FinderToolIcon.jpg

With the mouse button down, drag from the Finder Tool icon to the "=" button and release the mouse. The Find Window dialog will be filled in with the "=" button's handle and other data. Click on "OK". You will then get a "Window Properties" window with a tab control with five tabs. The five tabs are General, Styles, Windows, Class and Process. The wndow will look like:

WindowProperties.jpg

Look for "Contol ID" near the botom of the first (General) tab. It will probably have the value "00000079". It is a hexadecimal value. Whatever the value is, it will probably be that value every time you execute that program. At the Windows API level, controls are often identified by a control id. We can use the Contol ID shown in the Window Properties window in our program.

We are ready to write the application. I assume you can create a console application. If your system is 64-bit then you will need to change the "Platform Target" to "x64". The .Net Process class cannot do everything we need it to do when our application is not the same as the other application in terms of bits (32-bits and 64-bits). The "Platform Target" property is in the Build tab of the project's properties. If you need to automate a 32-bit application from a 64-bit application or 64-bit from 32-bit then you can do that but you will need to use the Windows API directly to do the equivalent of what we are using the .Net Process class to do.

You need to add a "using" for the "System.Diagnostics" and "System.Runtime.InteropServices" namespaces. You will also need to add the following to the "Program" class:

const int WM_COMMAND = 0x0111;
const int BN_CLICKED = 0;
const int ButtonId = 0x79;
const string fn = @"C:\Windows\system32\calc.exe";
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hWnd, int nIDDlgItem);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);

The sample program will first look at the processes to find Calculator. We will assume the exe is at:

C:\Windows\system32\calc.exe

When we find a process that is executed from there, we know for sure it is the one we are looking for. Note that there are very many processes in a system; many more than most people are aware of. Each Windows Service is a process. Windows Services and other processes that we need to ignore do not have a window, so we can ignore all processes without a window. The following code will do all that:

IntPtr handle = IntPtr.Zero;
Process[] localAll = Process.GetProcesses();
foreach (Process p in localAll)
{
if (p.MainWindowHandle != IntPtr.Zero)
{
ProcessModule pm = GetModule(p);
if (pm != null && p.MainModule.FileName == fn)
handle = p.MainWindowHandle;
}
}
if (handle == IntPtr.Zero)
{
Console.WriteLine("Not found");
return;
}

The GetModule function is shown later in this article.

We are ready to click the button from within our application. To click the button we will send a BN_CLICKED notification message to the button's parent, the Calculator main window. To do that we will need both the button id (as in the preceding) and the handle of the button. The handle varies for each execution but we can determine the handle based on the handle of its parent and the control id. The Windows API function GetDlgItem does that, as in:

IntPtr hWndButton = GetDlgItem(handle, ButtonId);

Windows messages are sent to a window as determined by the window handle. Messages also have a message id that specifies what the message is for. We will send a WM_COMMAND message. The other two parameters we need to send the message is a wParam and a lParam. The value of those depend on what the message is (the message id). For a WM_COMMAND message doing what we are doing, the control id is passed as the high word of wParam and the low word of wParam is the notification code (BN_CLICKED). The lParam is the handle of the control. It seems unnecessary to pass both the control id and the control window's handle but that is the way it works. The following sends the message:

int wParam = (BN_CLICKED << 16) | (ButtonId & 0xffff);
SendMessage(handle, WM_COMMAND, wParam, hWndButton);

When the processes are being searched, since some modules might be restricted from access for security purposes, we catch errors using the GetModule function. It is very simple, as in the following:

private static ProcessModule GetModule(Process p)
{
ProcessModule pm = null;
try { pm = p.MainModule; }
catch
{
return null;
}
return pm;
}

To test the program, execute Calculator and type in "2+2=". Then execute the sample program. Every time it executes, 2 will be added to the Calculator value.

Login to add your contents and source code to this article
post comment
     

Hi Sam, Thanks for the reply I have posted my question here http://www.c-sharpcorner.com/Forums/Thread/210468/extracting-data-from-a-listbox-in-another-application.aspxI need send message command to extract data and store it in C#.Any sample code would be greatly appreciated.Many Thanks for your feedback. Awaiting for your replySid

Posted by SIDDARTHA KAVILIPATI May 18, 2013

Sorry Sid that I did not see your message sooner. Yes, it is possible for some controls. That kind of thing can be done for Listboxes because they are older controls that existed in 16-bit Windows. You just need to get the window handle of the Listbox then use SendMessage to send messages using the unmanaged Windows API to interface with the windows. Create a question in the forums or send me a message for a little more help.

Posted by Sam Hobbs May 11, 2013

Sam, this solution has worked great for me. I am wondering if I can do the same thing to extract data from a windows Froms Listbox. Do you have any solutions for that? Many Thanks Sid

Posted by SIDDARTHA KAVILIPATI May 06, 2013

Andrew, I assume you need help with C#. The forums are a great place to get help with the language. Note that I set this article to be an "Intermediate" article. As a C# beginner and as a beginner to Windows programming, I am not surprised that you are having difficulty. You will likely get help in the forums but please understand that you need to also spend some time learning.

Posted by Sam Hobbs Jan 06, 2013

This works but I'm not familiar with c#. I'm trying to convert it using using several code converters but it is not working. Any advice?I would also like to make it so on button1.click it simulates the "=" click

Posted by andrew mace Jan 06, 2013
COMMENT USING
PREMIUM SPONSORS
Over-C is a holistic consortium of communications and technology specialists. We build, deploy and market both business as well as consumer products and solutions.
SPONSORED BY
  • PDF reports have never been easier to create. With our included WYSIWYG Designer, you can layout your reports, set up your data source and let DynamicPDF ReportWriter do the rest.
Join a Chapter