Tag Archives: Daily automation

Daily automation: SendKeys tricks

Today’s comment is absolutely worth posting here!

How to send a special character via SendKeys – it may be not so easy as I thought.

First of all, how to do this task in an easy way (i.e., using ValuePattern). I created a simple Windows Forms application called ‘testeditbox’ with a label and a text box (the label is not used in the test, just a typical case text box + label).

We can write in and, after, clean it the following way:

Get-UIAWindow -n testeditbox | Get-UIAEdit | Set-UIAEditText texttexttext;


Get-UIAWindow -n testeditbox | Get-UIAEdit | Set-UIAEditText "";


Now, we are going to perform this without using the pattern. There are two ways to put text in: Set-UIAControlText and Set-UIAControlKeys.
The first method can’t send special characters (it’s just my fault, I need to add the possibility), it works through the SendMessage API call. It should work pretty well as it sends to a handle.
The second is .NET SendKeys.
The problem is that SendKeys sends only to the focused control. Well, I achieved the result this way:

Get-UIAWindow -n testeditbox | Get-UIAEdit | Set-UIAFocus;
Get-UIAEdit | Set-UIAControlKeys "text2";


Get-UIAWindow -n testeditbox | Get-UIAEdit | Set-UIAFocus;
Get-UIAEdit | Set-UIAControlKeys "^(A){DELETE}";


Step by step guide:
1) get a control
2) set focus
3) get a control again (the Set-UIAControlKeys requires input), the control is foreground now
4) put keys to the control (Ctrl+A and Delete).

If your control of some other sort, you may need to do other tricks. Nonetheless, the idea behind is as I demonstrated.


Daily automation: getting selected radio button

This is truth, the phrase “only one who completely understood the topic can explain it to others” and “to put it so simply that even a five-year old guy would get it”. When I tried to write how to get an item that is selected, such as a radio button, I discovered that it’s not so easy as it should be. If a control containing items does not support SelectionPattern, there is a problem.
This fact led to creation of the Get-UIASelectedItem cmdlet.
To demonstrate how it works, let’s start calc.exe in the Programmer mode:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAMenuItem -Name View | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -Name Programmer | Invoke-UIAMenuItemClick;

The calc window in this mode looks like:


There are two sets of radio buttons. Unfortunately, containers of these controls do nothing, as they don’t support SelectionPattern. It would be excellent if the following code worked:

# this code does not work
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAImage -Name 'some name' | Get-UIAPaneSelection;

The problem is that none of two images supports SelectionPattern (and this is very atypical for an Image to support the most of the patterns).

This fact forces us to find out a solution using only those patterns that available to us. We know that radio buttons are likely to support SelectionItemPattern. Excellent! Let’s use this pattern.

We can group controls and get only those that are selected:

# this code does not work
Get-UIAImage -AutomationId 1080 | Get-UIARadioButton | Get-UIASelectedItem;

Why does this code not work? The answer is simple: images are not containers for radio buttons. Even though two groups of radio buttons work simultaneously, i.e. there can be selected two radio buttons, one for each image, we can’t rely on images. All radio buttons, by the opinion of MS UI Automation, are under the Pane that includes the most of controls.


What should we do to get the selected radio button on the upper image? We can group radio button manually and put them as an input to the Get-UIASelectedItem cmdlet:

"hex","dec","oct","bin" | %{ Get-UIARadioButton -Name $_; } | Get-UIASelectedItem | Read-UIAControlName

Daily automation: Execution plan for UI Automation?

Have you tried to debug GUI or UI Automation scripts? It’s possible, of course, however this activity is often like making a mountain out of a molehill. Why?, may you ask. Because, because you need to run part of your scripts, or the whole suite, in the debugging mode, and this disturbance is, most often, from a single error.

It might be time-consuming. It’s not so pleasant: you need to interrupt your activity and do something another. Maybe, insert more Write-Host or Write-Verbose statements. Or extend the log. Or run the code inside command-line or GUI-based debugger.

Well, something simple you can now do directly from your script! A couple of months ago, the most of cmdlets (I’m about he UIAutomation module) could return only one object. Now, cmdlets return  all objects that match. Even absolutely all. This is a typical behavior. This is also a problem: imagine the following hypothetical code: you get a window, after that you want to get one or more panes, and, finally, buttons. Say, Close and Cancel.

Your code looks like:

ipmo [path]\UIAutomation.dll
Get-UIAWindow -n "window name" | Get-UIAPane | Get-UIAButton -n C*;

Suddenly, you drove into an error. What? Error? I see the buttons, you think. The answer is simple: you’ve gotten two or three panes, but only one pane contains these buttons. You are given an error: the second and the third panes admitted that they lack buttons.

What would you do to resolve this? You need somehow learn which pane is yours. Here comes the Execution plan. What is it? I sure, many of us already know what an execution plan in MS SQL is. I was very excited about it, when, more than a decade ago, I’ve been achieving MCDBA. It’s how your queries will work and how long.

If you have never heard about it, several google results are worth being visited now: the search result.

Returning to our panes and buttons, we can now discuss what our plan consists of. UIAutomation 0.8.2. offers highlighter squares of ten colors for ‘generations’ of controls and numbers that allow you learn the order controls were output.

What is the thing a ‘generation’ of controls? I called so a wave of output. In our example, generation one is the window itself (it will be bordered and numbered 1). The second generation is panes (another color and numbers 2 in their upper-down corners). Finally, the third generation is buttons, also in another color.

Let’s run this code:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton; Get-UIAMenuItem; Get-UIAComboBox; Get-UIAEdit;

Here is how it looks like:

If we drag the window from under the ‘mesh’ of squares, what will be seen:

Number at the lower-right corners of squares follow the steps they have appeared:

  1. Get-UIAWindow
  2. Get-UIAButton
  3. Get-UIAMenuItem
  4. Get-UIAComboBox
  5. Get-UIAEdit

This may help in a complicated situation when you don’t know what path in the Automation tree your code walks.

Finally, there is a problem with PowerShell 2.0, or even powershell.exe -version 2. Thus, this feature is for PowerShell 3.0 running in the 3 mode.

Daily automation: dumping control’s genealogy

This is a new edition of the old post.

Note: this post is deprecated, please refer to my comment below.

One more way to find out where in the Automation tree a control resides is to use the Get-UIAControlAncestors cmdlet. We are told that TreeScope.Parent and TreeScope.Ancestors used as filter for search will call the functionality that is not yet implemented? However, the cmdlet does perform the search from the very bottom of the hierarchy to the topmost window. Let’s take Calculator and dump the control hierarchy:

Task: demonstrate using the search to the top cmdlet.

Requirements: perform search that consumes a part of control’s name and retrieve all the ancestors of the search result.

Solution: the following code prints out all the hierarchy in reverse order:

Set-StrictMode -Version Latest;

ipmo [path]\UIAutomation.dll;

# here we are searching for all the element whose names start with 'a'

Start-Process calc -PassThru | Get-UIAWindow | Search-UIAControl -Name A* | `

%{Write-Host "===========================================================";

Write-Host "@{Name='$($_.Current.Name)';
$_ | Get-UIAControlAncestors | `

%{Write-Host "@{Name='$($_.Current.Name)';

Daily automation: taking screenshots unleashed

By default, UIAutomation saves a screenshot if a cmdlet fails. This behavior is set by the default settings:

ipmo [path]\UIAutomation.dll

The default folder for storing screenshots is user’s TEMP. Taking a screenshot can be initiated manually, below is how to do it:

ipmo [path]\UIAutomation.dll

# saves the desktop
Save-UIAScreenshot -Description 123

The former line of code creates a file in the TEMP folder, for example: %USERPROFILE%\AppData\Local\Temp\20129261311418.bmp. The latter creates a file with more recognizable name: %USERPROFILE%\AppData\Local\Temp\20129261315161_123.bmp. Wanted better file names? Consider the following piece of code:

ipmo [path]\UIAutomation.dll

# saves the desktop to a specified file
Save-UIAScreenshot -Path c:\1 -Name 20120921_1.bmp

After we have discussed where we can save a screenshot, it’s time to discuss what can be saved. All the samples above saved the desktop – a reasonable choice if you don’t exactly know what the situation is when test fails. We can save a window, to the TEMP folder or to a folder of your choice:

ipmo [path]\UIAutomation.dll

# saves a window
Start-Process calc -PassThru | Get-UIAWindow | Save-UIAScreenshot

# saves a window to the specified file
Start-Process calc -PassThru | Get-UIAWindow | Save-UIAScreenshot -Path c:\1 -Name 20120921_2.bmp

# throws an exception 'Save-UIAScreenshot : File 'c:\1\20120921_2.bmp' already exists'
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -n 1 | Save-UIAScreenshot -Path c:\1 -Name 20120921_2.bmp

It’s not cmdlet’s concern if the file you specified already exists, so that it’s up to you to resolve such situations as shown in the code snipped above.

Finally, we can save any control, with or without a handle. The legend says that a control with handle is a window even if it’s a mere control. Times changes, controls without handles are now seen by UIAutomation as windows too. Here we get a button’s screenshot:

ipmo [path]\UIAutomation.dll

# saves a button to the specified file
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -n 1 | Save-UIAScreenshot -Path c:\1 -Name 20120921_3.bmp

The advantage of automation (as well as of UIAutomation) is that you can automate even already automated things. Ten screenshots at a time? It’s possible, below we produce screenshots for each of numeric buttons:

ipmo [path]\UIAutomation.dll

# generates a screenshot for each numeric button (ten screenshots)
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -SearchCriteria @{automationid='13*'} | Save-UIAScreenshot

This slideshow requires JavaScript.

Daily automation: more ways to perform a search

Initially, UIAutomation worked only with exact names, so that getting a control with several search conditions was an almost impossible thing:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -n 1;

When support of wildcards was introduced, search became useful as it was never before:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -n a*;
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -n [0-9];

Nonetheless, even with wildcards, search capabilities were not so friendly as they could be. Thus, the search criteria were added to Get- cmdlets as they have already been in the Test-UIAControlState and Wait-UIAcontrolState cmdlets.

The code samples below return the same – numeric buttons in Calculator:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name [0-9];
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -AutomationId '13*';
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -SearchCriteria @{Name="[0-9]"};
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -SearchCriteria @{automationid="13*"};
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControl -SearchCriteria @{Name="[0-9]"};
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControl -SearchCriteria @{automationid="13*"};

Why do we need more ways for search? Sometimes, we need some kind of grouping of controls:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -SearchCriteria @{Name="[0-9]"},@{name="c*"};
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -SearchCriteria @{automationid="13*"},@{name="a*"};

In the sample, the first search returns numeric buttons plus the Clear, Clear entry and Close (a cross) buttons. The latter returns numeric buttons and the Add button.

There might also be searches like:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControl -SearchCriteria @{isoffscreen="true"};
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAControl -SearchCriteria @{isenabled="false"};

What’s more, ‘search criteria’ strings (that are natural PowerShell hashtables) are way to store queries in a text file or a database instead of storing lines of code.

Daily automation: a few windows in parallel

At work, an interesting question was raised: is it possible to work with several windows in parallel if their processes are run under various users? Yes, it surely is.

Task: use UIAutomation to get windows of AUTs that are run under various users.

Requirements: demonstrate how to use the technique and how to determine which window might cause a problem.

Solution: the first example is simple and demonstrates how to handle several windows:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll
Start-Process calc;
Start-Process calc;
Start-Process calc;
Start-Process calc;
Get-UIAWindow -ProcessName calc | Get-UIAButton -Name 1 | Invoke-UIAButtonClick | Read-UIAControlName
Get-UIAWindow -ProcessName calc | Get-UIAMenuItem -n vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -n scien* | Invoke-UIAMenuItemClick

The sample starts the foursome of calculators, gets them all, clicks the 1 button and changes application mode to the Scientific one.

There is no difficulty with windows run under several user accounts while your test process (i.e., powershell) is running with at least equal or higher privileges.

Now, we’ll start calc under two different user accounts (credentials are not provided to the public domain just for you not locking your accounts ;):

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
(Start-Process calc -Credential (Get-Credential) -PassThru).Id
(Start-Process calc -Credential (Get-Credential) -PassThru).Id
# Here, we are writing up numbers to a paper
Get-UIAWindow -ProcessId 5924,6076 | Get-UIAMenuItem -Name vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -Name scien* | Invoke-UIAMenuItemClick

This works, but the sample is not friendly to use: you need to store process ids. Okay, below is a better one-liner:

Set-StrictMode -Version Latest;
ipmo [path]\UIAutomation.dll;
@((Start-Process calc -Credential (Get-Credential) -PassThru).Id; (Start-Process calc -Credential (Get-Credential) -PassThru).Id) | %{ Get-UIAWindow -ProcessId $_ | Get-UIAMenuItem -Name vi* | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -Name stat* | Invoke-UIAMenuItemClick; }

Finally, how to learn which window failed? For example, by defining a counting variable that will increment every window and point to an element of the windows array.

Daily automation: eliminating the fragility of tests. Part 3

Many of us faced the situation when a window is gone and a testing tool hits the air for long time. The testing tool tries to get a control, a next control, and so on. Every attempt takes time.

UIAutomation had the remedy for the problem for relatively long time. This feature has never been widely known, so that it’s time to tell you what it is and how it works.

Task: minimize the time that UIAutomation spends on controls of a window that failed to appear.

Requirements: demonstrate how to use the technique with default settings.

Solution: you don’t need to do anything to benefit from this technique in case if a window of your interest is gone:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow;
Get-UIAWindow -Name calc1 # non-existing window
Get-UIAWindow -Name calc* # this call get a window

After we ran this sample, we have the following output:
5000 – the default timeout
2000 – the AfterFailTurboTimeout
2000 – the timeout equals to AfterFailTurboTimeout. This is because calc1 has not been caught and the default timeout was considered as too long to wait for nothing
2000 – the AfterFailTurboTimeout
5000 – the default timeout was restored after the test have got a window
2000 – the AfterFailTurboTimeout
Note: this funcitonality has been broken in 0.8.0 Preview 4 (more accurately, 0.8.0P4 was released despite the fact that several unit tests have not been fixed. Just because this functionality is not widely known and never used explicitly).

Internally, UIAutomation checks that [UIAutomation.CurrentData]::CurrentWindow is $null and [UIAutomation.CurrentData]::LastResult also is $null.

Well, what about controls? Imagine the case you have a window and a child window or a control failed to appear.

This problem is not so obvious as the ‘no such window’ problem. ‘One control is gone’ is a typical case during the development cycle. Nonetheless, some controls are vital for the whole test. For this, a new parameter is here: -IsCritical. Let’s see how it works:

ipmo [path]\UIAutomation.dll
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 10
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 10 -IsCritical
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name 1 -IsCritical

Below is the output:
5000 – the default timeout
5000 – the default timeout as the -IsCritical parameter has not been used
2000 – the turbo timeout (thanks to the -IsCritical parameter)
5000 – the default timeout (after the first control that has been caught successfully)
5000 – IsCritical does nothing if there wasn’t a problem

The general rule is to use the -IsCritical parameter for child windows, default buttons and controls like combo boxes and radio buttons if their state blocks you from going forward.

Daily automation: peeling the onion

Sometimes, hit happens. It’s a shame to confess, but rarely even I recommend simple Win32 clicks. Today was the story about such clicks. A member of the test team, not a newcomer to UIAutomation and with thousands of test cases in the suite behind, the guy complained that Invoke-UIAControlClick (i.e., PostMessage) did not work!

I approached, and it didn’t work! The panic was spreading out among the testers, suggestions like ‘use mouse_event!’ or ‘revise the code!’ was hovering around the room.

The answer was simple. The Invoke-UIAControlClick cmdlet as well as the PostMessage/SendMessage Win32 call hits by control’s handle. We attempted to do the following:

Get-UIAWindow -n window_name | Set-UIAFocus | Get-UIATab -n tab_name | Invoke-UIAControlClick -X 500 -Y 125;

What the world was awaiting was:

Get-UIAWindow -n window_name | Set-UIAFocus | Get-UIATab -n tab_name | Get-UIAPane | Get-UIAPane | Get-UIAPane | Invoke-UIAControlClick -X 500 -Y 125;

Each control in this pipeline had a handle and there was no point to hit the tab if the link was on the lowest pane.
Layers are of almost the same size and put one over one. Even spy++ couldn’t help as nobody knew which layers was pointed to by the spy++ target cursor in order to collect messages.

Task: help to find the right level.

Requirements: write out the script that hit-tests every layer of controls and prints their handles.

Solution: below is the script that walks down the hierarchy and prints out handles of controls:

function hittest
 if ($element | Invoke-UIAControlClick -X $X -Y $Y) {
 Write-Host "The control has handle $($element | Read-UIAControlNativeWindowHandle)";

Get-UIAWindow -Name *window*name* | Set-UIAFocus | `
 Get-UIATab -Name *tab*name* | `
 Get-UIAControlDescendants | `
 %{hittest $_ 500 125;}

The script tries to click on each control and outputs the handle value. The Invoke-UIAControlClick cmdlet couldn’t decide was or wasn’t a click successful and returns $true if no crash were during the script execution.

Anyhow, you can use handles to get controls and click by handle in a cycle to find out the control you need.

Daily automation: how to test help files. Part 2. Better navigation

Today’s post is a next version of the recently posted HelpWalker.ps1. This version (HelpWalker2.ps1) brought to us the following improvements:

  • scroll down, in order to make nodes visible. The Invoke-UIAControlClick cmdlet uses Win32 API functions to perform clicks, and these functions are bound to the visible area to be working
  • scroll horizontally, so that the middle of some long tree node names are put geometrically in the tree area. Otherwise, it was seen as if click was send not to a node but to a form splitter or even to a right-side page.

Task: improve the script that all the clicks, especially done in the lower area of the tree, get working.

Requirements: the script should walk through any number of tree nodes, independently of how long their names are.

Solution: below is the script that walks through all nodes with no exception:

# Script name: HelpWalker2.ps1
# Description: demonstrates how to work with the Invoke-UIATreeItemScrollItem cmdlet
# Copyright: http://SoftwareTestingUsingPowerShell.com, 2012

Set-StrictMode -Version Latest

#ipmo $global:uiautomationModule;

if ($FileName.Length -eq 0 -or $AppTitle.Length -eq 0 -or $HelpTitle.Length -eq 0) {
 Write-Host "Please specify parameters as .\HelpWakler2.ps1 app_file_name application_title help_title";

# set a small delay between nodes
# because the refresh of each page should take time
[UIAutomation.Preferences]::OnSuccessDelay = 100;

# create our enum object
[PSObject]$testNames = New-Object PSObject;
$testNames | `
 Add-Member -MemberType NoteProperty -Name AppTitle -Value $AppTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name HelpTitle -Value $HelpTitle -PassThru | `
 Add-Member -MemberType NoteProperty -Name AppName -Value $FileName;

# start the AUT, its help file and get the window
Start-Process $testNames.AppName -PassThru | Get-UIAWindow -Seconds 120 | `
 Get-UIAMenuItem -Name Help | Invoke-UIAMenuItemClick | `
 Get-UIAMenuItem -Name 'Help*Topic*' | INvoke-UIAMenuItemClick;
Get-UIAWindow -Name $testNames.HelpTitle -Seconds 120;

function Invoke-TreeNodeExpand
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);

function Invoke-TreeNodeCollapse
 [void]($element | Invoke-UIAControlClick -DoubleClick -PassThru:$false);
function Invoke-TreeNodeShowRightPane
 [void]($element | Invoke-UIAControlClick -PassThru:$false);

function Invoke-TreeNodeScrollTo
 [void]($element | Invoke-UIATreeItemScrollItem);

function Invoke-TreeNodeChildrenProcess

 # children of the node or top-level nodes
 $children = $element | Get-UIAControlChildren -ControlType TreeItem;

if ($children -ne $null -and $children -is [Object[]] -and $children.Count -gt 0) {

# we store the previous node in a variable, so that
 # we can return to it and collapse its chilren
 # after we walked through all of them
 [System.Windows.Automation.AutomationElement]$previousChild = $null;

foreach ($childNode in $children) {
 try {
 if ($previousChild -ne $null) {
 # collapse the previous node
 Invoke-TreeNodeShowRightPane $previousChild;
 Invoke-TreeNodeCollapse $previousChild;
 $previousChild = $childNode;

# expand the current node
 Invoke-TreeNodeScrollTo $childNode;
 Invoke-TreeNodeExpand $childNode;
 Invoke-TreeNodeShowRightPane $childNode;

# print out the hierarchy
 [string]$fullHierarchy = "";
 if ($NodeHierarchy.Length -gt 0) {
 $fullHierarchy = $NodeHierarchy + " -> " + $childNode.Current.Name;
 } else {
 $fullHierarchy = $childNode.Current.Name;
 Write-Host $fullHierarchy;

# go down the hierarchy
 Invoke-TreeNodeChildrenProcess $childNode $fullHierarchy;
 catch {
 Write-Host $Error[0];

# first time, the tree is given as the root node
$rootNode = Get-UIATree -Seconds 120;
Invoke-TreeNodeChildrenProcess $rootNode;

The script accepts three parameters, as required, and a typical script run looks like:

.\HelpWalker2.ps1 services.msc Services '*Microsoft*Management*'

You should administrative rights or run PowerShell as administrator, if you are trying this sample. Usually, for regular applications, end user’s rights are enough.

Here are more samples how to run the script:

.\HelpWalker2.ps1 compmgmt.msc 'Computer*Management' '*Disk*Management*'
.\HelpWalker2.ps1 dsa.msc '*Active*Directory*Users*' '*Microsoft*Management*'
.\HelpWalker2.ps1 certmgr.msc *Cert* *Micr*Manag*

%d bloggers like this: