Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Using WPF UI Automation with PowerShell

5.00/5 (2 votes)
20 Sep 2010CPOL1 min read 25.9K  
I encountered a problem while trying to use WPF UI Automation with PowerShell - PowerShell reported incorrect type for automation elements

Consider the following PowerShell script:



VB
[void] [Reflection.Assembly]::Load('UIAutomationClient, ' +
    'Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
[void] [Reflection.Assembly]::Load('UIAutomationTypes, ' +
    'Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')

$root = [Windows.Automation.AutomationElement]::RootElement

$condition = New-Object Windows.Automation.PropertyCondition( `
    [Windows.Automation.AutomationElement]::NameProperty, `
    'start')

$startButton = $root.FindFirst( `
    [Windows.Automation.TreeScope]::Descendants, $condition)

$startButton.Current.Name
$startButton.Current.ControlType.ProgrammaticName


It uses WPF UI automation to get the reference to the Windows XP Start button. But the output is unexpected:



start
ControlType.Pane


PowerShell reports the Start button control type as Pane. Here is the equivalent (at least I think so) C# console application:



using System;
using System.Windows.Automation;
namespace UIAutTest
{
    class Program
    {
        static void Main(string[] args)
        {
            AutomationElement root = AutomationElement.RootElement;
            AutomationElement startButton = root.FindFirst(TreeScope.Descendants,
                new PropertyCondition(AutomationElement.NameProperty, "start"));
            Console.WriteLine(startButton.Current.Name);
            Console.WriteLine(startButton.Current.ControlType.ProgrammaticName);
        }
    }
}


This time the output is:



start
ControlType.Button


So, what is the difference? When I first encountered this, I posted a question here[^] and here[^], but never got a response. On further research, I found this[^] thread on MSDN forums. The problem seemed similar to mine and in his reply Bruce Payette states two problems with the starting script. The first is that UI Automation API works only from STA threads and PowerShell always runs scripts MTA. The workaround for this is to run PowerShell with the -sta switch or to run scripts from within the PowerShell 'Integrated Scripting Environment' (ISE) which defaults (I believe) to STA.



The other issue was a problem 'with how PowerShell handles value types' and the demonstrated solution was to insert some inline C# code in the script to get the UIAutomation root element. For the script in question the root element was an instance of Notepad created in the script so the example didn't work for me because I needed a reference to AutomationElement.RootElement (the current desktop) and I had to create my own. here is the final script:



[void] [Reflection.Assembly]::Load('UIAutomationClient, ' + 
    'Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
[void] [Reflection.Assembly]::Load('UIAutomationTypes, ' + 
    'Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
$source = @"
using System;
using System.Windows.Automation;
namespace UIAutTools
{
    public class Element
    {
        public static AutomationElement RootElement
        {
            get
            {
                return AutomationElement.RootElement;
            }
        }
    }
}
"@
Add-Type -TypeDefinition $source -ReferencedAssemblies( `
    "UIAutomationClient", "UIAutomationTypes")
$root = [UIAutTools.Element]::RootElement
$condition = New-Object Windows.Automation.PropertyCondition( `
    [Windows.Automation.AutomationElement]::NameProperty, `
    'start')
$startButton = $root.FindFirst( `
    [Windows.Automation.TreeScope]::Descendants, $condition)
$startButton.Current.Name
$startButton.Current.ControlType.ProgrammaticName


I created a small C# class with one static property to return AutomationElement.RootElement and used Add-Type to add it to the script. This time the output was:



start
ControlType.Button

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)