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

Finding a dialog

Our next problem is to find the window handle of the dialog box we have just opened. It’s easy to find a child windows of a given window, there’s an API call for it, but a dialog box is harder because it isn’t a child window – it’s an “owned top level” window. The only way that I know of finding a dialog box is to write a function that examines each top level window in turn until we find one with the right characteristics.

To scan through all of the top level windows we can use EnumWindows and its associated callback function, the definitions of which need to be added to the start of the program:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(
EnumWindowsProc lpEnumFunc,
ref IntPtr lParam);
public delegate bool EnumWindowsProc(
IntPtr hWnd, ref IntPtr lParam);

EnumWindows will call the callback function with the window handle in hWnd for each top level window. The final parameter lParam is passed to the callback function and can be used to get a result back. The enumeration can be stopped when you have found the window you are looking for by getting the callback to return false. The best way to use this API call is to create a new method that searches for a dialog box with a specified title and with the specified window as its Owner:

public IntPtr getDialog(
IntPtr Owner,
String Caption,
int timeout)
{

A timeout in milliseconds is included because we have to deal with the possibility that the dialog window we are looking for hasn’t been created yet and we might need to try to find it more than once.

The first thing we have to do is create the callback function and this is most easily done using the “anonymous method” facility introduced in .NET 2.0. This allows us to define a delegate without having to first create a separate named function. You can think of this as a direct or “in-line” definition of the delegate object:

EnumWindowsProc enumProc = delegate(
IntPtr handle,
ref IntPtr pointer)
{

We need to test that the windows title matches the one we are looking for:

int length = GetWindowTextLength(handle);
StringBuilder wTitle =
new StringBuilder(length + 1);
GetWindowText(handle,
wTitle,
wTitle.Capacity);
if (wTitle.ToString() == Caption)
{

If it does we next check that it is a dialog box by retrieving its class name, which for most dialogs is #32770. You can find the class name of any window using Spy++ or Winspector.

int max = 100;
StringBuilder classname =
new StringBuilder(max);
GetClassName(
handle,
classname,
max);
if (classname.ToString() == "#32770")
{

If the class name proves that we have a dialog box the final test is to check that its owner is the specified window:

  IntPtr Parent = GetParent(handle);
if (Parent == Owner)
{
pointer = handle;
return false;
}
}
}
return true;
};

Notice that the GetParent API call returns either the owner or parent window depending on the type of window.

This completes the callback function and all that now remains is to use EnumWindows to make use of it:

 DateTime end =
DateTime.Now.AddMilliseconds(
(double)timeout);
IntPtr DlgHwnd = IntPtr.Zero;
do
{
Thread.Sleep(100);
EnumWindows(enumProc, ref DlgHwnd);
}while(DlgHwnd==IntPtr.Zero &&
end > DateTime.Now );
return DlgHwnd;
}

Notice the use of Sleep to give other threads time to open dialogs and complete other processing.

To make all of this work we need the following API function definitions:

[DllImport("user32.dll", 
SetLastError = true)]
static extern int GetClassName(
IntPtr hWnd,
StringBuilder lpClassName,
int nMaxCount);
[DllImport("user32.dll",
ExactSpelling = true,
CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(
IntPtr hWnd);
[DllImport("user32.dll",
SetLastError = true,
CharSet = CharSet.Auto)]
public static extern int GetWindowText(
IntPtr hWnd,
[Out] StringBuilder lpString,
int nMaxCount);
[DllImport("user32.dll",
SetLastError = true,
CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(
IntPtr hWnd);

<ASIN:078214134X>

<ASIN:1572316950>



Last Updated ( Sunday, 19 July 2009 )