I’ve been working with the C# P/Invoke mechanism recently to do some UI automation. In my last post I described how to get the handles (IDs) of all the child control windows of a specified window. Another key task is getting the caption text of a control window. The term caption is slightly ambiguous. For a Button control the caption is the label. For a TextBox control the caption is the text in the TextBox. I ran into a problem with the RichTextBox control that had a simple solution.
To get control caption text I generally use a wrapper around the GetWindowText function:
[DllImport("user32.dll", EntryPoint = "GetWindowText",
CharSet = CharSet.Auto)]
static extern IntPtr GetWindowCaption(IntPtr hwnd,
StringBuilder lpString, int maxCount);
and
static string GetWindowCaption(IntPtr hwnd)
{
StringBuilder sb = new StringBuilder(256);
GetWindowCaption(hwnd, sb, 256);
return sb.ToString();
}
Notice that for simplicity I hard-coded a max length of 256 characters. This works fine in most cases but for the project I was working on, the technique failed when dealing with RichTextBox controls. So after some experimentation I came up with a technique that works with most controls. The Win32 APIs are:
[DllImport("user32.dll", EntryPoint = "SendMessage",
CharSet = CharSet.Auto)]
static extern int SendMessage3(IntPtr hwndControl, uint Msg,
int wParam, StringBuilder strBuffer); // get text
[DllImport("user32.dll", EntryPoint = "SendMessage",
CharSet = CharSet.Auto)]
static extern int SendMessage4(IntPtr hwndControl, uint Msg,
int wParam, int lParam); // text length
And the C# wrappers are:
static int GetTextBoxTextLength(IntPtr hTextBox)
{
// helper for GetTextBoxText
uint WM_GETTEXTLENGTH = 0x000E;
int result = SendMessage4(hTextBox, WM_GETTEXTLENGTH,
0, 0);
return result;
}
static string GetTextBoxText(IntPtr hTextBox)
{
uint WM_GETTEXT = 0x000D;
int len = GetTextBoxTextLength(hTextBox);
if (len <= 0) return null; // no text
StringBuilder sb = new StringBuilder(len + 1);
SendMessage3(hTextBox, WM_GETTEXT, len + 1, sb);
return sb.ToString();
}
Function GetTextBoxText is really misnamed because is seems to work with most controls, including the troublesome RichTextBox. In later versions I will rename the function and its helper as GetControlText and GetControlTextLength. Calling the function might look like:
// get handle to app
// get and store handles to children
Console.WriteLine("The text/caption of each child" +
"control window is:");
for (int i = 0; i < children.Count; ++i)
{
IntPtr hControl = children[i];
string caption = GetTextBoxText(hControl);
Console.WriteLine(caption);
}
In the image below, the first control is a TextBox, the second is a RichTextBox, and the third is a Button. You can see GetTextBoxText works for all three types of controls. All in all, another interesting P/Invoke adventure.

.NET Test Automation Recipes
Software Testing
2012 IEEE-IRI Conference
2012 DevConnections
2013 Visual Studio Live Redmond
2013 Microsoft MMS Conference
2013 DevIntersection Conference
2013 Build Conference