Automating applications using messages
Written by Harry Fairhead   
Thursday, 16 July 2009
Article Index
Automating applications using messages
Getting started
Finding a dialog
Opening a file
Doing the Demux

Load the App

Before we get started on the task of passing messages we need to create a running instance of the application we want to control. This can be most easily achieved using the Process class.

Start a new C# Windows application project and enter the definitions for the two API calls given earlier and three “using” statements:

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

Also add a button titled “Run VOB job” – clicking on this will start the application and run the automated task. We need a global variable to keep track of the application so add:

public Process VobEdit = new Process();

in the button’s click event handler. We can now write the code that creates the application:

private void button1_Click(
object sender,EventArgs e)
{
VobEdit.StartInfo.FileName =
@"VobEdit.exe";
VobEdit.Start();
VobEdit.WaitForInputIdle(5000);

For simplicity it is assumed that the VOBedit program is stored in the same directory as the C# project, in this case in the ..bin\debug directory, but you can use a complete path name to an existing application.

The Start method loads a new instance of the application and the WaitForInputIdle waits, with an upper limit of 5 seconds, until the application enters the “Idle” state and hence has finished loading and is ready for use.

 

The Open button

Given that the application is now running we need to get its window handle so that we can send messages to it. The Process class automatically gets the applications main window handle when it starts:

IntPtr Hwnd = VobEdit.MainWindowHandle;

All the standard Windows controls send a WM_COMMAND to let their parent window know that they have been clicked. The documentation reveals that the code for WM_COMMAND is 111 (in hexadecimal) and a suitable constant can be defined:

const uint WM_COMMAND = 0x111;

The only remaining difficulty is that the third parameter of the message-sending call has to specify the control ID of the button that has been clicked. All controls have a control Id which never changes, unlike window handles which change each time the program is run. So how do we discover what the control ID of the Open button is? The answer is that you can use a utility like Spy++ which is included with the full Visual Studio or one of its downloadable equivalents. Even if you have Spy++ you might like to try Winspector (also included in the code distribution in the CodeBin)  which is slightly easier to use and has more features and a better user interface. By running Winspector you can discover the control ID of any control hosted by a given window – see Figure 1 – as well as details of what messages are being passed between windows.

winspector

Winspector reveals the control Ids for all of the controls in the main window

Using Winspector the control ID of the Open button is revealed to be 1002 but we also need its window handle which is passed as the final parameter. Unlike the control ID, the window handle changes each time the program is run. Fortunately there is another API call that will find the current window handle of any given control ID. Add the following definition to the start of the program:

[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(
IntPtr hDlg, int nIDDlgItem);

Now we can get the window handle and send the WM_COMMAND message to the main window:

IntPtr OpenBtnHwnd = 
GetDlgItem(Hwnd, 1002);
SendNotifyMessage(
new HandleRef(VobEdit, Hwnd),
WM_COMMAND,
new IntPtr(1002),
OpenBtnHwnd);

If you now run the program you will see VOBedit start and the Open file dialog box appears just as it does when you click the Open button.

There are a few points to make about the way the message is sent.

The first is that a HandleRef is used to make sure that the VobEdit process object isn’t destroyed while the API call is in progress and results in the Hwnd parameter being passed to the API in a way that is safe. In most cases you don’t need to specify the window handle and the last parameter can be set to zero – but it’s safer to pass it correctly.

Finally SendNotifyMessage has to be used because the Open button creates a modal dialog box which pops up in front of the main window and freezes its message processing until the dialog is closed. What this means is that if we were to use SendMessage our application would wait until the dialog box was closed.

We now have to deal with the problem that the dialog box may not actually be open and ready for business when we try to move on and make use of it.

<ASIN:0735625301>

<ASIN:0321294319>



Last Updated ( Sunday, 19 July 2009 )