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.
You must be logged in to post a comment.