4

Is it possible to get UI text from an external application in C#.

In particular, is there a way to read Unicode text from a label (I assume it's a normal Windows label control) from an external Win32 app that was written by a 3rd party? The text is visible, but not selectable by mouse in the UI.

I assume there is some accessibility API (e.g. meant for screen readers) that allows this.

Edit: Currently looking into using something like the Managed Spy App but would still appreciate any other leads.

OJ.
  • 28,944
  • 5
  • 56
  • 71
dbkk
  • 12,643
  • 13
  • 53
  • 60

3 Answers3

6

If you just care about the standard Win32 label, then WM_GETTEXT will work fine, as outlined in the other answers.

--

There is an accessibility API - UIAutomation - for standard labels, it too uses WM_GETTEXT behind the scenes. One advantage to it, however, is that it can get text from several other types of controls, including most system controls, and often UI using non-system controls - including WPF, text in IE and Firefox, and others.

// compile as:
// csc file.cs /r:UIAutomationClient.dll /r:UIAutomationTypes.dll /r:WindowsBase.dll
using System.Windows.Automation;
using System.Windows.Forms;
using System;

class Test
{
    public static void Main()
    {
        // Get element under pointer. You can also get an AutomationElement from a
            // HWND handle, or by navigating the UI tree.
        System.Drawing.Point pt = Cursor.Position;
        AutomationElement el = AutomationElement.FromPoint(new System.Windows.Point(pt.X, pt.Y));
        // Prints its name - often the context, but would be corresponding label text for editable controls. Can also get the type of control, location, and other properties.
        Console.WriteLine( el.Current.Name );
    }
}
BrendanMcK
  • 14,252
  • 45
  • 54
5

You could do it if that unicode text is actually a window with a caption by sending a WM_GETTEXT message.

[DllImport("user32.dll")]
public static extern int SendMessage (IntPtr hWnd, int msg, int Param, System.Text.StringBuilder text);

System.Text.StringBuilder text = new System.Text.StringBuilder(255) ;  // or length from call with GETTEXTLENGTH
int RetVal = Win32.SendMessage( hWnd , WM_GETTEXT, text.Capacity, text);

If it is just painted on the canvas you might have some luck if you know what framework the application uses. If it uses WinForms or Borland's VCL you could use that knowledge to get to the text.

Beygi
  • 1,918
  • 1
  • 16
  • 22
Lars Truijens
  • 42,837
  • 6
  • 126
  • 143
  • This also works for standard win32 labels and buttons. Interop nit: SendMessage should return IntPtr, and take IntPtr for wParam. May not matter in the case of WM_TEXT (though incorrect wParam could be an issue if run as 64bit code?), but it's good practice to use the right types in case the code gets cut&paste repurposed. – BrendanMcK Jun 14 '12 at 22:42
2

didn't see the values for wm_gettext or wm_gettextlength in that article, so just in case..

const int WM_GETTEXT = 0x0D;
const int WM_GETTEXTLENGTH = 0x0E;
sieben
  • 2,161
  • 4
  • 23
  • 31