Category Archives: PowerShell

0.8.7 Beta 2


Briefly, this beta release brings two features and several bug fixes:

1) support for the SendInput call (thanks to the Windows Input Simulator project): Keyboard and Mouse properties (‘property hubs’ – properties with properties and methods).

The Keyboard property works, of course, with keys (for example, you could press Shift, send characters and release Shift). The Mouse property provides us with various clicks (this does not use handles, so that a window of your interest should be foreground).

Clicks that work via handles have been moved to the Control property:

$element.Click() -> $element.Control.Click()

$element.InvokeContextMenu() -> $element.Control.InvokeContextMenu()

I feel sorry to early adopters who could start using this, but a lot of properties directly on an automation element was a nightmare.

2) new logging. After moving to AOP, more and more possibilities are open. The new logger writes how cmdlets were called, with their parameters. String, int, bool (switch parameter) are represented as is. Automation elements are shown as hashtables. I hope that this logging will be much more useful in troubleshooting, for example, by analyzing what automation elements were passed to a cmdlet.

Now, new logging supports the most of cmdlets and properties and methods of the new object model. By default, it’s UIA.log in the Documents folder.

3) the Invoke-UiaWizard cmdlet became faster and much more reliable

4) the Invoke-UiaControlContextMenu cmdlet and the $element.Control.InvokeContextMenu() method have been working in not all situations, fixed

5) some more fixes I’m unable to remember what has been fixed in (getting controls in some situations that I found during writing of unit tests).

Advertisements

The new elements’ object model is out. A short intro


Many claim that it’s not so easy to decide which pattern cmdlet to use with a particular control. For long time we were tied to what Microsoft’s UI Automation outputs to us. However, there is a way to increase productivity: extension methods. Yes, they are not visible in PowerShell by default, nonetheless I’d like to offer an object model that grew from nothing else but extension methods.

To start, let’s page through the presentation, with binaries of 0.8.7 alpha 3 or higher in your hands. Enjoy!

Sample scripts online


Have I not mentioned yet that all sample scripts are constantly available online? Scripts that are honored to be public are always put in the folder that is checked in to the source control. Here is the root of the samples folder: https://github.com/apetrovskiy/STUPS/tree/master/samples

SamplesFolder

For instance, UIAutomation samples are here: https://github.com/apetrovskiy/STUPS/tree/master/samples/UIAutomation, TMX samples are here https://github.com/apetrovskiy/STUPS/tree/master/samples/TMX and so on.

Unfortunately, some scripts may become incompatible with the contemporary versions of binaries. Sometimes I refresh them, alternatively, you could initiate a pull request with a renewed version of a script.

 

A bug in ISE


As I heard, there were a million test cases in PowerShell v. 1.0. In PowerShell ISE v. 3.0 I faced the following bug (a repeatable bug), on a Windows 8 x64 box:

1. In ISE have been opened several script modules.

2. ISE was closed

3. Two modules have been renamed (like XXXTestCasesIteration1.psm1 -> XXXTestCasesIteration01.psm1) and their folders were also renamed (i.e., XXXTestCasesIteration1\XXXTestCasesIteration1.psm1 -> XXXTestCasesIteration01\XXXTestCasesIteration01.psm1)

4. ISE was opened again and one of renamed files was called via Recent with a right (and a bit verbose) message box.

5. After that, I opened the Open menu item, a second thereafter ISE crashed.

After crash, it points (on selecting the Open menu item) to the %SystemRoot%\system32\WindowsPowerShell\v1.0 folder (i.e., where the executable is situated).

How to repeat? I opened a file in a next to folder to renamed (to point the last folder to a similar place), performed steps 1-5 with the second module (i.e.,  XXXTestCasesIteration2\XXXTestCasesIteration2.psm1 -> XXXTestCasesIteration02\XXXTestCasesIteration02.psm1) and the answer from ISE was the same, crash. Arr!

Desktop automation: towards code simplicity. Part 3


Not only in the Get-UIA[ControlType] cmdlet family were introduced improvements. Another veteran, the Get-UIAWindow cmdlet has been modernized.

This is a start place for our experiments:

# there's an application with two forms open
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAMenuItem help | Invoke-UIAMenuItemExpand | Get-UIAMenuItem -n about* | Invoke-UIAMenuItemClick;

StartPlace
Earlier, we had such options to get a child window:

# using the Get-UIAChildWindow cmdlet
Get-UIAWindow -pn calc | Get-UIAChildWindow;
# using recursive search for a control of window type (in practice, the same as using the Get-UIAChildWindow cmdlet)
Get-UIAWindow -pn calc | Get-UIAControl -ControlType Window;

These samples worked well for us until there were three or more windows open (the main form, the child form and a message box, for example. Another case is the main form and two child forms) or the main form is so complex that consists of two or more forms (dockable parts or a window inside an MMC application).
In such cases, the search for a certain window led to complicated code, especially when forms had the same captions.
Time changed, let’s review new ways of getting a certain window:

# the topmost window (there are no changes with the previous behavior)
Get-UIAWindow -pn calc;

# all windows of the process(es):
Get-UIAWindow -pn calc -Recurse;

# all windows with the phrase 'calc' in caption
Get-UIAWindow -pn calc -Name *calc*;

# all windows with the word 'about' in caption (in fact, it's only the child window)
Get-UIAWindow -pn calc -Name *about*;

# all windows with ClassName like 'frame' (the main window has Classname == 'CalcFrame')
Get-UIAWindow -pn calc -Class *frame*;

# all windows with ClassName == '#32770' (the child window has such a standard class name)
Get-UIAWindow -pn calc -Class '#32770';

As can be easily seen, one call of the Get-UIAWindow cmdlet could substitute an old-days function of getting the right window.

The only problem in recursive search for windows is the time it could take: if forms are full of controls, the search will relatively slow.

Desktop automation: towards code simplicity. Part 2


Just to drop a couple of words about parameterless search. How many words could be used describing parameterless search?

Let’s dive into samples:

# the classic parameterless search for a control
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name [123] | Invoke-UIAButtonClick;
Get-UIAText 123;

 

# here we search for a control that has handle
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name [123] | Invoke-UIAButtonClick;
Get-UIAText 123 -Win32;

 

# using wildcards in parameterless search (-Win32 only)
Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name [123] | Invoke-UIAButtonClick;
Get-UIAText 12* -Win32;

ParameterlessSearch001

All three samples return the same: two labels (texts). However, you should understand that

  • the first sample (i.e., Get-UIAText 123) works via MS UI Automation. This means that there is no use of wildcards. The query is (ControlType.Text AND (Name == ‘123’ OR AutomationId == ‘123’ OR ClassName == ‘123’ OR ValuePattern.Value == ‘123’)).
  • the second and the third samples work via Win32 API (SendMessage) and check for the value given every AutomationElement with handle of given type

The only disadvantage of this syntax is a habit to supply all names with asterisks:

# MS UI Automation does not like asterisks:
Get-UIAText 12*;
# use the -Win32 parameter if applicable
Get-UIAText 12* -Win32;
# or the exact name, automation id, class name or value of a control
Get-UIAText 123;

In simple situation this syntax helps make your code even simpler:

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAMenuItem view | Invoke-UIAMenuItemExpand | Get-UIAMenuItem worksheets | Move-UIACursor -X 10 -Y 10 | Get-UIAMenuItem mortgage | Invoke-UIAMenuItemClick;

Desktop automation: towards code simplicity. Part 1


As you could notice, versions 0.8.3, 0.8.4 and 0.8.5 of the UIAutomation module took a few steps to be simpler and more friendly to the user. We carried out the following improvements to the module:

  • added a Win32 click to the InvokePattern cmdlets (i.e., Invoke-UIAButtonClick, Invoke-UIAMenuItemClick, etc) in the case when pattern failed
  • added the -Value parameter to Get-UIA[ControlType] cmdlets (i.e., Get-UIAEdit, etc)
  • added the parameterless search like Get-UIAButton Next
  • and we are working on an improvement to Set-UIAControlText and Set-UIAControlKeys cmdlets

Today’s post sheds light on the brand-new -Value parameter. Many controls support ValuePattern. The preceding link and this one give us the following definitions: “Represents a control that has an intrinsic value that does not span a range and can be represented as a string. This string may or may not be editable depending on the control and its settings.” “Allows clients to get or set a value on controls that do not support a range of values. For example, a date time picker.”

In practice, ValuePattern’s value is a string in a text field. Typically, it’s not wise to search for a text box by the value that is inside. It may be useful in certain situations (i.e., text boxes that are hard to recognize, for example, those that have auto-generated ids in AutomationId. And, in such situations, search for a control by a value in the field could help).

Start-Process calc -PassThru | Get-UIAWindow | Get-UIAButton -Name [123] | Invoke-UIAButtonClick;
Get-UIAText -Value 123;

TheValueParameter001

A better example is Computer Management -> Disk Management: while names of drives’ squares say the number of device and partition, values contain descriptive information:

Get-UIAWindow -Name *comp*manag* | Get-UIACustom -Value *unallocated*;

TheValueParameter002W

Where else the -Value parameter can be used? Accordingly to the suggested controls to patterns mapping, ValuePattern is useful when automating links.

For example, a link in an event’s help page in Event viewer:

Get-UIAWindow -n *event*event* | Get-UIAHyperlink -Value *event*log*online*;

TheValueParameter003

What’s more, link’s href is available even it’s invisible:

Get-UIAWindow -n *control*pattern*mapping* | Get-UIAHyperlink -Value '*#control_mapping_clients*' | Read-UIAControlName;

TheValueParameter004

Finally, the best area of applicability of the -Value parameter is grids and other complex controls.

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

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

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

SendKeys002

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

SendKeys003

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";

SendKeys004

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

SendKeys005

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:

CalcProgrammerMode001

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.

Get-UIAPane;

CalcProgrammerMode002
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

Trying to improve the documentation…


The long public holidays in Russia is a gift. When the employer pays salary as if we work, these ten days are the gift for personal projects. Have you heard that a French actor has recently changed citizenship? Just in time, in the time we are working on our projects while the whole world is working for the boss.

Instead of writing code, I started writing texts. The first project is UIAutomation. Several topics are already rewritten or reorganized, several are in progress.

Pipeline001

 

Pipeline002

%d bloggers like this: