Getting the Caption Text for a Control using C# P/Invoke

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);


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);

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.


This entry was posted in Software Test Automation. Bookmark the permalink.