Monthly Archives: September, 2012

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
[UIAutomation.Preferences]::OnErrorScreenShot
[UIAutomation.Preferences]::ScreenShotFolder

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
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.

What the WordPress :)


Isn’t it a bug?

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.