0

I have a WPF application that is being started from a command-line application.

I am trying to do some simple automation (get/set text, click some buttons, etc). I cannot seem to find any of the child windows in WPF.

I have working models with WPF and UIA Framework, WinForms and WinAPI, but cannot seem to get WinAPI and WPF to play nicely.

I have used UISpy, WinSpy++, Winspector, the UIA Verify app to look at the controls, etc, but they do not seem to carry the same information for WPF as WinForms.

For example, in the WinForms app, I see a textbox with a ClassName of "WindowsForms10.EDIT.app.0.33c0d9d" when I look via the spy tools. The UIA Automation Verify app is the only one to acknowledge the element exists and reports "TextBox".

So, my question is how do I find the correct class name to pass or is there an easier route to find the child elements?

// does not work in wpf
IntPtr child = NativeMethods.FindWindowEx(parent, prevElement, "TextBox", null);

// works in winforms
IntPtr child = NativeMethods.FindWindowEx(parent, prevElement, "WindowsForms10.EDIT.app.0.33c0d9d", null);

and here is the user32.dll imports I am using:

public class NativeMethods
{
    public const int WM_SETTEXT = 0x000C;
    public const int WM_GETTEXT = 0x000D; 
    public const uint CB_SHOWDROPDOWN = 0x014F;
    public const uint CB_SETCURSEL = 0x014E;
    public const int BN_CLICKED = 245;
    public const uint WM_SYSCOMMAND = 0x0112;
    public const int SC_CLOSE = 0xF060;

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

    [DllImport("user32.dll", SetLastError = false)]
    public static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);

    [DllImport("user32.dll")]
    public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
    public static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam); 


}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
ajberry
  • 1,027
  • 2
  • 9
  • 8

1 Answers1

3

If you want to automate WPF, you must use the UI Automation, not the "old thing of the past" windows API :-).

There is a good introduction on UI automation here: Bugslayer: GUI Control to Major Tom

There is also an interesting open source project named "White" that leverages UI automation: White on codeplex. There are some samples in there if you want to dig in.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • I much prefer the UI Automation route. I have this working in WPF and UI Automation, as well as WinForms and WinAPI. Part of this exercise was determining whether or not this (WPF with WinAPI) could be done. From comments below, it looks like for WinForms and previous apps, WinAPI is an option, WPF and beyond use UI Automation. Thanks! – ajberry Dec 15 '10 at 19:17
  • Well, UI Automation also works for regular Windows and Winforms, because it can rely on the legacy MSAA (Accessibility) SDK, however, my experience shows that the support is not fully consistent across all platforms. But yes UI automation is really the way to go for WPF plus, if you own the server-side code, you can equip your own controls and UIElements with automation peers that facilitate external client handling. – Simon Mourier Dec 15 '10 at 20:23