Click on Informational/Warning Window

Jul 26, 2012 at 2:26 PM
Edited Jul 26, 2012 at 2:26 PM

Hi, I just began working with UIAutomation module for Powershell.

My goal is to automate configuration of MSMQ in windows server (w2k8/w2k8r2)

I almost reached it. Here is my code

Get-UIAWindow -Name 'Server Manager' | `
	Get-UIAChildWindow -AutomationId '65280' -Name 'Server Manager (SEGOTNL669)' | `
	Get-UIATree -AutomationId '12785' | `
	Get-UIATreeItem -Name 'Server Manager (SEGOTNL669)' -TimeOut 10000 | `
	Get-UIATreeItem -Name 'Roles' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Application Server' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Message Queuing' -Timeout 10000 | % {$_.setfocus()}
	Show-UIAContextMenu
	Get-UIAWindow -Name 'Menu' | Get-UIAMenuItem -AutomationId 'Item 1016' -Name 'Properties' | Invoke-UIAMenuItemClick
	
	Get-UIAWindow -Name 'Message Queuing Properties' | `
	Get-UIATab -AutomationId '12320' | `
	Get-UIATabItem -Name 'Storage' | `
	Get-UIAEdit -AutomationId '10780' -Name 'Message files folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'
	Get-UIAEdit -AutomationId '10782' -Name 'Message logger folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'
	Get-UIAEdit -AutomationId '10781' -Name 'Transaction logger folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'
	Get-UIAButton -AutomationId '1' -Name 'OK' | Invoke-UIAButtonClick

 

After execution of this code,there appears warning window asking for confirmation.

And I don't know how to click OK on it and get rid of it

Any help appreciated

Coordinator
Jul 26, 2012 at 6:00 PM

Hello blindrood ,

I can't watch your code in action and help you much right now as I'm sitting on an old Vista-32 notebook (with a couple of 
letters broken, and I hit buttons as on legacy mechanical typewriter :)) in the summer cabin, so that I'll try to give you 
several general tips.

Assuming that the warning is a dialog, not a UAC  request:

1) You can try the Get-UIAChildWindow cmdlet, with or without parameters. The cmdlet works similarly to Get-UIA[ControlType] 
cmdlets, not like the Get-UIAWindow cmdlet, giving you the oppoprtunity to use the following parameters: -Name, -AutomationID, 
-Class.

Please note that applications like MMC may contain several internal windows

2) Supposing that your code is running minimized (for example, start /wait /min powershell.exe -command { your_script;} ), you 
might try the Get-UIAActiveWindow cmdlet (it's very unstable thing as it relies on the current focus only. Sometimes, it helps).3) The right way is to use the Register-UIAWindowOpenedEvent cmdlet, for example, somewhere several seconds before the dialog 
appears:

$your_server_manager_window | Register-UIAWindowOpenedEvent -EventAction { param($src, $e) try { $src | Get-UIAButton -n OK | 
Invoke-UIAButtonClick; } catch {} }Please refer to these blog posts:

http://softwaretestingusingpowershell.com/2012/02/12/uiautomation-getting-events/

http://softwaretestingusingpowershell.com/2012/06/28/daily-automaiton-how-to-deal-with-windows/

I shed a drop of light there on handling 'surprise!' windows.

4) you can try to get all the sub-windows in your window and choose which is the dialog you are hunting down:

$your_server_manager_window | Get-UIAControlDescendants -ControlType Window | %{ ... }

I'd like also to give you several tips on coding for the UIAutomation module:

1) cut out -AutomationID and -Class wherever it possible. Nobody guarantees that another version of Windows have the same Ids 
onboard. If you have only one tree in the app, simply use Get-UIATreecontext menu items may or may not have the same AutomationId (I'm about "'Item 1016'") on next run.Please cut them out.

The more you managed to cut, the better is your code.UIAutomationSpy/Start-UIARecorder generate all that they can catch.

2) Use more wildcards, 'Server Manager (SEGOTNL669)' -> 'Server*Man*', with or without apostrophes/quotes. It's case-
insensitive, the capitals are just for better reading.Your code will be more stable and portable.

3) If you need to change the default timeout for a time, there is the setting:

[UIAutomation.Preferences]::Timeout (in milliseconds)

Jul 27, 2012 at 8:38 AM
Edited Jul 27, 2012 at 10:00 AM

Hi, thanks for Your huge response.

I found that it was my mistake.

This window didn't have 'OK' button but 'Yes' :D

I didn't notice that yesterday and today with a fresh mind I found it

UIAtomation is a great tool :)

However I seem to have one more problem

 

 

Here's how it looks

http://i.imgur.com/PJBTr.png

 

	Get-UIATabItem -Name 'Storage' | 
	Get-UIAEdit -Name 'Message files folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Message logger folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Transaction logger folder:' | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage'

 

In this part of code 2 last edittexts are set but first one, 'Message files folder:' seems to have problem

   

Coordinator
Jul 27, 2012 at 6:12 PM

Hello blindrood ,

luckily, I'm today in the city and I vpn'ed to a Windows 2008 SP2 x64 host to watch what's the problem.

I found that the Properties window throws out the value of the first text box if the drive letter is not usable. When I changed letter to C: and created the folder Logs (without inner structure), it filled all the three boxes and asked me for creating a folder.

Get-UIAWindow -Name 'Server*Man*' | `
	Get-UIAChildWindow -Name 'Server*Man*' | `
	Get-UIATree | `
	Get-UIATreeItem -Name 'Server*Mana*' -TimeOut 10000 | `
	Get-UIATreeItem -Name 'Roles' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Appl*Server' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Mess*Queu*' -Timeout 10000 | `
    Invoke-UIAControlContextMenu | `
    Get-UIAMenuItem -Name 'Properties' | Invoke-UIAMenuItemClick
	
	Get-UIAWindow -Name 'Mess*Queu*Properties' | `
	Get-UIATabItem -Name 'Storage' | `
	Get-UIAEdit -Name 'Message*files*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Message*logger*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Transaction*logger*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAButton -AutomationId '1' -Name 'OK' | Invoke-UIAButtonClick

A couple of notes:

1) The Show-UIAContextMenu cmdlet is a legacy or interim cmdlet, I even forgot about its existence. :) The right cmdlet for invoking a context menu is Invoke-UIAControlContextMenu, that accepts an AutomaitonElement as input and returns the menu window AutomationElement, so that the pipeline is uninterruptible from a tree node to a menu item.

2) There are integrated delays after every cmdlet (the default mode is Presentation). You can set, if necessary, the following setting: [UIAutomation.Preferences]::OnSuccessDelay = 0

3) These text boxes are examples of UI Automation-unfriendly programming. Why? An Edit does not have name, usually, The string that is visible as its name is no more than a LabeledBy from the nearest Text (i.e., label). Actually, the name of a text box is the text inside. And only when there's no text inside, the name is the name of the next to label.

The UIAutomation-friendly programming is setting a good automationId to each text box. Unfortunately, there is no better way to fill in text boxes except getting them by 'name'.

The first way I would recommend here is to put the values twice, i.e. fill in the first, the second, the third and again the first text boxes.

The second way is to get all three text boxes 

Get-UIAWindow -Name 'Mess*Queu*Properties' | `
    Get-UIATabItem -Name 'Storage' | `
    Invoke-UIAControlClick | `
    Get-UIAControlDescendants -ControlType Edit | %{ ... }
after that get the text box with the least $_.Current.BoundingRectangle.Y, this will be the first, and so on.

Finally, my version is:

cls;
[UIAutomation.Preferences]::OnSuccessDelay = 0
Get-UIAWindow -Name 'Server*Man*' | `
	Get-UIAChildWindow -Name 'Server*Man*' | `
	Get-UIATree | `
	Get-UIATreeItem -Name 'Server*Mana*' -TimeOut 10000 | `
	Get-UIATreeItem -Name 'Roles' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Appl*Server' -TimeOut 10000 | Invoke-UIATreeItemExpand | `
	Get-UIATreeItem -Name 'Mess*Queu*' -Timeout 10000 | `
    Invoke-UIAControlContextMenu | `
    Get-UIAMenuItem -Name 'Properties' | Invoke-UIAMenuItemClick
	
	Get-UIAWindow -Name 'Mess*Queu*Properties' | `
    Get-UIATabItem -Name 'Storage' | `
    Invoke-UIAControlClick | `
	Get-UIAEdit -Name 'Message*files*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Message*logger*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAEdit -Name 'Transaction*logger*' | Set-UIAEditText -Text 'C:\Logs\MSMQ\Storage'
	Get-UIAButton -AutomationId '1' -Name 'OK' | Invoke-UIAButtonClick

Jul 30, 2012 at 7:31 AM
Edited Jul 30, 2012 at 11:37 AM

Hi again,

I did some more modifications and it looks now that

filter Write-Log {$_ | Out-Null}
New-Item -Path D:\Logs -ItemType directory -ErrorAction SilentlyContinue | Write-Log
New-Item -Path D:\Logs\msmq -ItemType directory -ErrorAction SilentlyContinue | Write-Log
Invoke-Item $env:SystemRoot\system32\servermanager.msc
Start-Sleep 8
[UIAutomation.Preferences]::Timeout = 15000
[UIAutomation.Preferences]::OnSuccessDelay = 0
Get-UIAWindow -Name 'Server*Manager*' |  Get-UIATree | 
	Get-UIATreeItem -Name 'Roles' | Invoke-UIATreeItemExpand | 
	Get-UIATreeItem -Name 'Application*' -TimeOut 20000 | Invoke-UIATreeItemExpand | 
	Get-UIATreeItem -Name 'Message*Q*' |
	Invoke-UIAControlContextMenu | Get-UIAMenuItem -Name 'Properties' | Invoke-UIAMenuItemClick

	Get-UIAWindow -Name 'Message Queuing Properties' | Get-UIATab | Get-UIATabItem -Name 'Storage' | Invoke-UIATabItemSelectItem -ItemName 'Storage' | Write-Log
	Get-UIAControlDescendants -ControlType Edit | ForEach-Object {
		$_ | Set-UIAEditText -Text 'D:\Logs\MSMQ\Storage' | Write-Log
	}

	Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick
	do{
		Get-UIAWindow -Name 'Message*' | Get-UIAControlDescendants -ControlType Window | ForEach-Object {
				try{$_ | Get-UIAButton -Name 'Yes' | Invoke-UIAButtonClick | Write-Log}catch{}
				try{$_ | Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick | Write-Log}catch{}
		}
		$list = Get-UIAWindow -Name 'Message*' | Get-UIAControlDescendants -ControlType Window
	}
	while ($list)
Stop-Process -Name 'mmc'

It works fine, but do-while loop takes a lot of time (comparing to previous lines). I do this because there appear about 4 confirm windows (3 with OK, one with YES ;) ) one after another.

If You know a way how to speed up this process I'll be gratefull

I also created a way to add private que:

#region create que
	Get-UIAWindow -Name 'Server*Manager*' |  Get-UIATree | 
		Get-UIATreeItem -Name 'Roles' | Invoke-UIATreeItemExpand | 
		Get-UIATreeItem -Name 'Application*' -TimeOut 20000 | Invoke-UIATreeItemExpand | 
		Get-UIATreeItem -Name 'Message*Q*' | Invoke-UIATreeItemExpand |
	    Get-UIATreeItem -Name 'Private*' | 
	    Invoke-UIAControlContextMenu | Get-UIAMenuItem -Name 'New' | Invoke-UIAMenuBarExpand | 
	    Get-UIAMenuItem -Name 'Private*' | Invoke-UIAMenuItemClick
	Start-Sleep 1
	Get-UIAEdit -Name 'Que*' | Set-UIAEditText -Text $Name
	Get-UIACheckBox -Name 'Transactional' | Invoke-UIACheckBoxToggle
	Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick
#endregion

#region set que properties
	Get-UIAWindow -Name 'Server*Manager*' |  Get-UIATree | 
		Get-UIATreeItem -Name 'Roles' | Invoke-UIATreeItemExpand | 
		Get-UIATreeItem -Name 'Application*' -TimeOut 20000 | Invoke-UIATreeItemExpand | 
		Get-UIATreeItem -Name 'Message*Q*' | Invoke-UIATreeItemExpand |
	    Get-UIATreeItem -Name 'Private*' | Invoke-UIATreeItemExpand | 
	    Get-UIATreeItem -Name $Name | Invoke-UIAControlContextMenu | 
	    Get-UIAMenuItem -Name 'Properties' | Invoke-UIAMenuItemClick
	    
	Get-UIAWindow -Name "$($Name)*" | 
	Get-UIAPane -Name 'Journal' | 
	Get-UIACheckBox -Name 'Limit*journal*' | Invoke-UIACheckBoxToggle
	start-sleep 1
	Get-UIAPane -Name 'Journal' |  Get-UIAEdit -Name 'Journal' | Set-UIAEditText -Text '5000'
	Get-UIAButton -Name 'Apply' | Invoke-UIAButtonClick
#endregion

#region set que permissions
	Get-UIAWindow -Name "$($Name)*" | Get-UIATab | Get-UIATabItem -Name 'Security' | 
	Invoke-UIATabItemSelectItem -ItemName 'Security'
	Get-UIAButton -Name 'Add*' | Invoke-UIAButtonClick
		$elem = Get-UIAWindow -Name "$($Name)*" | Get-UIAControlDescendants -ControlType Window 
		$elem | Get-UIAControlDescendants -controltype document | Set-UIAControlText -Text $user | Write-Log
		$elem | Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick
		try{$elem | Get-UIAControlDescendants -ControlType Window | Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick | Write-Log}catch{}
		Get-UIAWindow -Name "$($Name)*"| Get-UIACheckBox -Name 'Allow*Full*' | Invoke-UIACheckBoxToggle | Write-Log
	Get-UIAButton -Name 'Apply' | Invoke-UIAButtonClick
	Get-UIAButton -Name 'OK' | Invoke-UIAButtonClick
#endregion

I would like to know if I'm correctly using UIAutomation cmdlets.

Thanks in advance

Coordinator
Jul 30, 2012 at 1:05 PM

Hello, I'm on an iPad now and can't test what I'm writing here.
Have you ever heard that Python'ians say about Python? It is with batteries included.
Similarly, the UIAutomation cmdlets (Get-UIAWindow, Get-UIA[ControlType], Wait-) are shipped with timeout 'inside'.
How does the timeout works? It is a do/while loop that works until a window of interest or a control is caught. If nothing was caught, the timeout fires a null exception and the pipeline stops.

In your do/while loop, the first timeout is used partially (a Message* window), after that either the Yes-button or OK-button uses the full fifteen second-long timeout.

How to improve the code: if you surely know what the order of dialogs is, you might simply set timeouts for dialogs (if time is not known):
Instead of the loop:
Get-UIAWindow -n Message* -seconds 120 | Get-UIAButton -n OK -seconds 2 | ...
Get-UIAWindow -n Message* -seconds 120 | Get-UIAButton -n Yes -seconds 2 | ...
(there is definitely a use case for supporting regular expressions! I've been pondering , the time I added wildcards, about the reason and failed to find out at least one. There would be great the instruction: -Name 'OK|Yes' )
The code that is above waits for a dialog and spends no more than 2-3 seconds (time for search plus the timeout) per button.

The code is right, I have only two notes:
1. If the start-sleep function just sleeps, you may simply rely on the timeout of the next cmdlet. Or increase the timeout of the next cmdlet.
2. In the part of code with get descendants window and get a button, set a small timeout for Get-UIAButton -n ... as I did in the sample above.