Consider the following PowerShell script:
[void] [Reflection.Assembly]::Load(
[void] [Reflection.Assembly]::Load(
$root = [Windows.Automation.AutomationElement]::RootElement
$condition = New-Object Windows.Automation.PropertyCondition( `
[Windows.Automation.AutomationElement]::NameProperty, `
$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