"Timeout" while listing items in List

Oct 5, 2013 at 3:37 PM
There are extractly 8 items in the list. When executing the following command, after wallked through 8 items, it stuck for 15 seconds and reports timeout exception.
Why?
PS C:\Windows\system32> Get-UIAWindow -Name 'xxx' | `
    Get-UIAList | `
    Get-UIAListItem -Seconds 15

Cached                  Current                 CachedParent            CachedChildren         
------                  -------                 ------------            --------------         
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
System.Windows.Autom... System.Windows.Autom...                                                
Get-UIAListItem : Get-UIAListItem: timeout expired for control with class: + '', control type: 
'ListItem', title: '', automationId: '', value: ''
所在位置 行:3 字符: 2
+     Get-UIAListItem -Seconds 15
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationTimeout: (:) [Get-UIAListItem], Exception
    + FullyQualifiedErrorId : ControlIsNull,UIAutomation.Commands.GetUIAListItemCommand
Coordinator
Oct 5, 2013 at 10:30 PM
Edited Oct 6, 2013 at 8:56 AM
Hi VictorWoo,
could you try the same code with UIAutomation 0.8.6 Beta 5?
Oct 6, 2013 at 5:34 AM
Edited Oct 6, 2013 at 5:35 AM
Yes I tried, the problem remains.
PS C:\Windows\system32> gcm Get-UIAWindow | select DLL

DLL                                                                                                                                                      
---                                                                                                                                                      
D:\sync\script\UIAutomation.0.8.6B5.NET40\UIAutomation.dll                                                                                               



PS C:\Windows\system32> Get-UIAWindow -Name 'XXX' |
    Get-UIAList |
    Get-UIAListItem | % { $_.Current.Name }
AAA
BBB
CCC
Get-UIAListItem : Get-UIAListItem: timeout expired for control with class: + '', control type: 'ListItem', title: '', automationId: '', value: ''
所在位置 行:3 字符: 5
+     Get-UIAListItem | % { $_.Current.Name }
+     ~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationTimeout: (:) [Get-UIAListItem], Exception
    + FullyQualifiedErrorId : ControlIsNull,UIAutomation.Commands.GetUIAListItemCommand
Coordinator
Oct 6, 2013 at 6:10 AM
How many controls of type List (list view, grid, etc) might your application have?
(Get-UIAWindow -Name 'XXX' |  Get-UIAList).Count
It seems to me that UIA tries to enumerate list items from several lists.
If there are several of them, you can try the following:
  • use some identifier for the list you work with now:
Get-UIAWindow -Name 'XXX' |  Get-UIAList -automationId zzz -name yyy -class mmm
  • a number (they are often the same during the life of the app):
(Get-UIAWindow -Name 'XXX' | Get-UIAList)[0]
  • make path in the Automation tree leading only to one List:
Get-UIAWindow -Name 'XXX' | Get-UIAPane -Aitomationid 1 | Get-UIAPane -AutomationId 2 | Get-UIAList
you can also run UIAutomationSpy, point to a list item and print the full path to the control here; it sometimes might be helpful to read the full path.

The last thought that you work with a child window and UIAutomation enumerates at first the short way, and after that the long way, I.e.
Window xxx -> list
And window 'main window' -> child window xxx -> list
MS UI Automation treat windows as controls.

If you have several windows in your application (you may be unaware of it. for example, a frame inside MMC window is one more window), UIA 0.8.6 Betas 4 and 5 could help: you are allowed to use more parameters with the Get-UIAWindow cmdlet.

I'm sure for 90% that UIA just has a similar area (List) for further search. Alternatively, try a bigger timeout (maybe, 120 seconds) to obtains further results and learn what they are.
Marked as answer by VictorWoo on 10/6/2013 at 1:30 AM
Oct 6, 2013 at 6:39 AM
GOTCHA!
There are 2 UIALists in the same position. Walking through the second causes exception.
It seems empty.
Thank you very much, and my faith ++ :)
Coordinator
Oct 6, 2013 at 7:00 AM
It explains.
Initially, the UIA module returned only one control. It was good in simple situations, but never fitted for rich output.
If UIA is asked to return list items from an empty List, it returns timeout error.
In case of two lists, an empty one and one that is non-empty, the result is puzzling.
Unfortunately, any output (string) could not be used: it spoils the standard output.

We have the following options:
  • Verbose output (but it overly verbose)
  • old logging (UIAutomatin.log) that is also verbose and, the worst of all, is locked until you close PowerShell.exe (0.8.5)
  • new logging (TMX.log) that is not turned on by default (0.8.6)
There are a way to watch which controls were used: the Show-UIAExecutionPlan cmdlet.
It shows several waves of output with number (mouse over controls to refresh) and might help to learn which controls were used.
Oct 6, 2013 at 7:11 AM
apetrovskiy wrote:
If UIA is asked to return list items from an empty List, it returns timeout error.
Is it logically suitable? If we can't initially know the count of list items, it could be 0..infinite.
e.g.: Reply all incoming emails in the inbox.
If the inbox is empty, the script turn in to error. Or need some judgement? how?
Oct 6, 2013 at 7:25 AM
My UIAutomationSpy generated code is:
Get-UIAWindow -Class 'TXGuiFoundation' -Name 'XXX' | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAPane | `
Get-UIAList | `
Get-UIAListItem -Name 'YYY'
And the last but one item in Hierarchy view is:
name = ''; automationId = ''; class = ''; control type = 'List'
The automationId is empty. How to use -AutomationId then?

BTW, it'll be awesome if add some copy as text feature to UIAutomationSpy :)
Coordinator
Oct 6, 2013 at 7:32 AM
The Get- cmdlets are not the same as, for instance, the Get-ChildItem cmdlet:
the Get-ChildItem cmdlet works only 'now'.
The Get-UIA cmdlets work not only now, the work (when :)) from start time to the time timeout expires.
This is very useful in software testing as the situation a windows appears after ten seconds is common. Appearance of a control (the next step of a wizard, for example) after several seconds is also common.
A Get-UIA[ControlType] cmdlet fails if no controls in question are here during the time your timeout or default timeout expires.

Of course, there are ways to avoid code interruption:
  • the Test-UIAControlState cmdlet checks ($true/$false) ad hoc
  • the Wait-UIAWindow and Wait-UIA[ControlType] cmdlets check all the time timeout is equal are return $true/$false
if ((Wait-UIAWindows bla-bla-bla)) {

}
if ((Test-UIAControlState ...)) {

}
if (Get-UIAWindow ... | Wait-UIAButton -n OK)) {

}
A mess-up in names? We tried to follow Cmdlet Development Guidelines as we could (despite that Microsoft's cmdlets were created before this guidelines appeared and these guys don't follow the guidelines :)).
Oct 6, 2013 at 7:46 AM
apetrovskiy wrote:
The Get-UIA cmdlets work not only now, the work (when :)) from start time to the time timeout expires.
Yes, I think I can follow your meaning: Get-UIA* Cmdlets are time-based, detecting for a period. So we should change our mind from traditional understanding of linear programming.

And there is one last more question, please have a look at the "automationId is empty" problem above, thanks.
Coordinator
Oct 6, 2013 at 7:47 AM
Regarding 'Copy as text', there is check box 'Write code to clipboard'. It copies to the clipboard only one line (Get-UIA[ControlType] ...).

However, we have more ways to copy it:
1) stop the spy and copy from the lower box or from the Script tab (the whole history)
2) use the Start-UIARecorder cmdlet, for exmaple:
Start-UIARecorder -WriteCurrentPattern -NoClassInformation:$false -NoEvents:$false -NoScriptHeader -NoUI -Seconds 30
It writes to two files: full (with the sample you provided) and short (less code). By default, the files are saved to your Temp folder (cd %temp%).

I'll going to re-write the spy (I'm tried to do this for the last year :)) and I'll be thinking how to do this and do this as simply for the user as possible. For example, Coded UI works in other way: collects actions, the user edits these high-level actions and after that their spy generates code. Maybe, it's a good feature to implement...

Regarding the lack of properties, yes, sometimes UI automation is no t only craftwork, but also art. :) :) There is also the -SearchCriteria parameter that sometimes helps:
Get-UIAWindow -pn calc | Get-UIAButton -SearchCriteria @{name="1";isenabled=$true}
the full list of properties supported (MS UI Automation v.1.0):
(Get-UIAWindow -pn calc | Get-UIAButton -SearchCriteria @{name="1";isenabled=$true}).Current
Coordinator
Oct 6, 2013 at 8:23 AM
VictorWoo wrote:
And there is one last more question, please have a look at the "automationId is empty" problem above, thanks.
I suggested in the previous post using the -SearchCriteria parameter:
Get-UIAWindow -pn calc | Get-UIAButton -SearchCriteria @{name="1";isenabled=$true}
MS UI Automation v.1.0 provides as with the following set of properties for an AutomaitonElement:
ControlType : System.Windows.Automation.ControlType
LocalizedControlType : button
Name : 1
AcceleratorKey :
AccessKey :
HasKeyboardFocus : False
IsKeyboardFocusable : True
IsEnabled : True
BoundingRectangle : 1453,494,34,27
HelpText :
IsControlElement : True
IsContentElement : True
LabeledBy :
AutomationId : 131
ItemType :
IsPassword : False
ClassName : Button
NativeWindowHandle : 66766
ProcessId : 4912
IsOffscreen : False
Orientation : None
FrameworkId : Win32
IsRequiredForForm : False
ItemStatus :

Practically, we can use IsEnabled (enabled), IsOffscreen (! visible), sometimes we can play with AcceleratorKey, AccessKey, FrameworkId (if an author of the app provided us with these values).
Alternatively, the order can help. Using the order is not a good idea, however, it may help:
(Get-UIAWindow -Name 'XXX' | Get-UIAList)[0] | Get-UIAListItem
Probably, the second List could be avoided by specifying the path to the first control (there should be difference between full paths).

Sometimes, if control never change its relative position to the parent of ancestor when the user changes the size of form, we can use coordinated click:
Get-UIAWindow -Name 'XXX' | Get-UIAPane | Get-UIAPane | Get-UIAPane | Move-UIACursor -X 20 -Y 30 | Get-UIAControlFromPoint # List
If the situation when one List is not used or never contains list items, you can simplify your work:
Get-UIAWindow -Name 'XXX'  | Get-UIAListItem
Oct 6, 2013 at 8:33 AM
Yes I saw your answer. They are good practice and valuable for new user.
They can be placed in wiki or documentation to help people.
Coordinator
Oct 6, 2013 at 8:50 AM
I'll extend the documentation working with controls, some 0.8.x features are not yet documented.
There was a slight decrease in use of the framework during the summer and I was overwhelmed with automation at work, so that I stopped re-writing old documentation and extending it. I'll continue, of course.

There's also a tool from MIcrosoft UIA V that might help learning the Automation tree. It works exactly as UI Automation v.1.0 (the last month they removed the tool based on MS UI Automation v.2.0).
Oct 6, 2013 at 9:17 AM
I've an idea about UIAutomationSpy:
In multiple UIAList situation, if UIAutomationSpy detects multiple instance in the same tree level, it automatically compares their ".Current" properties or automationId, etc., and calculate the minimal filter set to make sure the user selection wins. Or generate some suggestions for creating filters. It can save life, isn't it?
Coordinator
Oct 6, 2013 at 10:39 AM
UIAutomationSpy works "from the end": it uses the ability of Microsoft UI Automation to get a control from the point the mouse cursor is now hovering over.
After that the spy builds the full/short path up to the window.

I added the Test generated code check box, which was meant to test the PowerShell code, however I didn't add the C# code that checks the PowerShell code the spy writes out...
Oct 6, 2013 at 12:28 PM
So it's not possible to find a unique path automatically?
Coordinator
Oct 7, 2013 at 7:33 AM
It's hardly possible to find the best path automatically.
Cmdlets support several ways of search
  • direct UI Automation search
  • UI Automation search with wild cards
  • Win32 search (SendMessage) with wild cards
    The two last types of search collect 1) all control of the type, below parent 2) all controls with handles below the parent
    and apply wild cards to the collection.
    These types of search much more useful, however, if we work with a grid, table, etc control with hundreds and thousands of elements (controls), it takes time.
    Finally, the best way is what the user prefers.
    If the spy tests all ways, if will eat a lot of time.
The similar problem with Selenium. It supports, besides direct searches and search via JavaScript, two types of search: CSS and XPath.
People prefer the latter, though relatively slow but readable.
A tool, firebug for example, output something like
//div/div/table/tr/td/div/[0] (a very simple example)
The user makes from it something like:
//../tr/td/div/[0]
I used Selenium a year ago and my sample is not so clear.
The most interesting that all tools that out of the box generate code (HP QTP, TestComplete, Visual Studio, etc) are left behind in popularity by Selenium that even does not have code generation.
Oct 7, 2013 at 7:52 AM
Edited Oct 7, 2013 at 7:53 AM
Yes, I've thought about css, xpath, etc, before. A tool call XmlSpy gives similar answers like //div/div/table/tr/td/div/[0] , not friendly to people.
So, is there any tip in PowerShell to compare two objects, find different value to help making a filter?
I tried
Compare-Object xxx.Current yyy.Current
Though they have different property value, but Compare-Object doesn't give me the expected answer. My stupid solution is:
Compare-Object (xxx.Current.ToString().Split("`n")) (yyy.Current.ToString().Split("`n"))
I think there must be a better solution..
Coordinator
Oct 7, 2013 at 2:28 PM
$wnd1 = Get-UIAWindow -n *command*pro*
$wnd2 = Get-UIAWindow -n *calc*
$wnd1.Current | Get-Member | %{ Compare-Object ($wnd1.Current)."$($_.Name)" $wnd2.Current."$($_.Name)"; }
Oct 8, 2013 at 3:09 AM
Thank you for your script.