Powershell UI Automation within a webbrowser control - powershell

Ok so I recently learned how to execute UI automation using the $ie = new-object -com "internetexplorer.application" command. This works pretty good for a task I am attempting to automate on an ASPX web app...until I ran into a modal popup which freezes my script. From what I have read online, I may want to bypass this by running this same process on a form, using a forms.webbrowser control.Sample Follows
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | out-null
[Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$form = New-Object Windows.Forms.Form
$form.text = "My Form"
$form.size = New-Object Drawing.size #(700,600)
$web = New-object System.Windows.Forms.webbrowser
$web.location = New-object System.Drawing.Point(3,3)
$web.minimumsize = new-object System.Drawing.Size(20,20)
$web.size = New-object System.Drawing.size(780,530)
$web.navigate("http://myASPXsite.com")
$form.Controls.Add($web)
$form.showdialog()
$doc = $web.document
$doc.getElementbyID('txtUserName').value = "myUser"
The point is that the ability to reference and invoke html elements doesn't work through this method. I can navigate to the webpage but nothing more. Could it be that the webbrowser control doesn't have the same functionality as the internetexplorer object. This is the code I would use with the other method.
$ie = new-object -com "internetexplorer.application"
$ie.navigate("http://www.MyASPXsite.com")
While ($ie.readystate -ne 4) {start-sleep -m 100}
$doc = $ie.document
$doc.getelementbyid('txtUserName').value = "myUser"
Any ideas on my situation? Am I even on the right track to handle the modal dialogs?

Your problem is $form.showdialog(). The script will hold up in this point until you close the form. You have to add an event handler to the form's Shown event something like this:
$form.Add_Shown({
$doc = $web.document;
$doc.getElementbyID('txtUserName').value = "myUser";
});
Then it should work

Just to get the Selenium suggestion out of the way before discussing PowerShell/UIA: I'd investigate using Selenium for this sort of web automation work, since it's awesome! I believe it has built-in support for handling modal dialogs.
Otherwise, if you must stay with manipulating the IE automation interface via PowerShell, I'd explore using the MS UI Automation API to deal with your modal dialog.
You can use UIA directly without any wrapper libraries, but I'd suggest using one of the existing PowerShell wrappers like WASP or the newer UIA PS extensions:
http://wasp.codeplex.com/
http://uiautomation.codeplex.com/
One approach is to write a few lines of UIA code to dismiss the modal dialog, and make sure it works in isolation (i.e., manually bring up the modal dialog, and execute just the code that should dismiss it).
Then, run the UIA code from your existing PowerShell script that drives IE. You could put the dialog handling code in a separate file and launch it via your main program.
Putting the UIA code in a separate file is a handy hack to work around a big browser automation gotcha you may be running into - it's likely whatever you're doing in code that brings up the modal dialog blocks execution of your script until the dialog is dismissed. So you must make sure your dialog-handling code is running in a separate process or thread that's launched before you cause the modal dialog to appear.

Related

Multiple buttons in a GUI / Add Key down Event Handler

I have already received valuable advice from another post and have already been able to expand "my little GUI project". I have built a GUI with different tabs and buttons based on a code (see screenshot 1)
If I trigger the underlying functions (search in a CSV, or ping from a target) via button, the whole thing works fine.
But I want to execute the actions when the user presses the enter key and here I have a problem. I'm trying to target the buttons with the following code:
*
Search by Site ID:
$form.KeyPreview = $true
$form.Add_Keydown({if ($_.Keycode -eq "Enter")
{$button_Search.PerformClick()}})
Ping command (under the Network tab):
$form.KeyPreview = $true
$form.Add_Keydown({if ($_.Keycode -eq "Enter")
{$button_Search_NW.PerformClick()}})
I thought I can thus uniquely identify the buttons, but still both are addressed. If I search via Enter key for a site ID, I get the site ID, but at the same time I get the message in Powershell that the "SiteID" can't be reached via ping.
I hope I have expressed myself understandably. My question: How can I make it so that I can address the buttons clearly and with the Enter key also only the action is executed, in whose tab I am also currently moving.

Download file from web page button click using PowerShell

I have a web page where there is a button called "Download Excel" - once we click on this button, this will download the excel. This is working fine from the web page UI, the same functionality we want to automate, so we are trying to achieve through powershell using the below code :
$ie = New-Object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.silent = $true
$ie.Navigate($loginUrl)
while($ie.busy){Start-Sleep 1}
$copyBtn = $ie.Document.getElementsByClassName('btn btn-primary') | Where-Object {$_.innerText -eq 'Download Excel'}
$copyBtn.click()
This is working fine - and opening the save as dialog box but I want to skip the save as dialog box or we should be able to pass the file download path and click OK button through the code (the file should be saved in pre-defined location)...Is it possible to achieve thru the PowerShell? If yes, how can I do this? PowerShell Expert opinions are appreciated :-)

How to make an apply button in powershell

I'm making a GUI and I need a button, that doesn't close the window on click.
Just copying from some of my other code for button creation, which works fine, but closes the window, on click. I need something that doesn't close the window, when clicked.
$apply = New-Object System.Windows.Forms.Button
$apply.Location = New-Object System.Drawing.Point(150, 120)
$apply.Size = New-Object System.Drawing.Size(50, 22)
$apply.Text = "Apply"
# $apply.DialogResult = [System.Windows.Forms.DialogResult]::
I'm making a GUI and I need a button, that doesn't close the window on click, like an apply button, does.
A button in a GUI with text Apply usually means some setting is performed without closing the window.
You just need to add an Click event handler to the button like:
$apply.Add_Click(
{
# here you do whatever the Apply is meant for
}
)
Another button called OK is usually also present which also applies whatever is was, but does close the form.

Can i change button names for Out-GridView?

I have a script which filters an excel sheet (schedule) and displays the result through Out-Gridview. When the user clicks "Cancel" it opens the excel sheet after closing the Out-Gridview. When the user clicks "Ok" it only closes the Out-Gridview. But these actions are not visible for the user. So can i rename the "Ok" and "Cancel" button of Out-Gridview to lets say "Close Schedule" and "Open Excelsheet"?
And also, is it possible to run the rest of the script without waiting for the response, but keep the buttons working?
I know that if i don't use an output mode the rest of the script runs without waiting, but with outputmode (which gives me buttons) it waits for the response.
I use this code to filter a schedule and it would be nice if i can use the rest of the program without shutting down the rest of the program, maybe by starting another instance of powershell or something?
$Schedule = $ResultsTable | Out-Gridview -OutputMode Single -Title "Schedule"
if ($Schedule -eq $Null) {
Start-Process "$Fileserver\Logistics_share\Planning_Alg\TS Delivery schedule rev2.0.xlsx"
}
If it can be done with only a visual overlay, it is ok by me (Buttons)

Multi-Runspace PowerShell/XAML script crashing on button click

I have a script which uses asynchronous runspaces in order to display a XAML/WPF GUI while processing stuff in the background. When I click the button, it crashes, but only when in a specific state. Let me elaborate.
The script begins with pinging a long list of servers to ensure that they're up. After that, it runs through a few SQL queries, WMI calls, other logic, processes all of the data, and then updates my DataGrid in the other window one row at a time with the information.
The button simply brings up a WinForm confirmation:
#$Global:uihash is my synchronized hashtable.
$Global:uihash.Button.add_click({if([System.Windows.Forms.MessageBox]::Show `
("Yes or No?", "Question",[System.Windows.Forms.MessageBoxButtons]::OKCancel) -eq "OK")
{Do stuff back in main PS Window}
If I press the button while the script is pinging all of the devices, it works perfectly. If I click the button at any other time during the script, the PS Windows immediately flip to not responding and I have to kill them.
From the application event log:
The program PowerShell_ISE.exe version 10.0.10586.117 stopped
interacting with Windows and was closed.
I'm like 95% sure it's because pinging devices is the only part of the script which has absolutely zero interaction with the other window, I just don't know how to fix this, or even work around it.
I'm wondering if anybody has seen or experienced this or something similar before, and how you got around it. Any help would be greatly appreciated. Thanks!
I was never able to completely figure this out, but I did find a good workaround for me. I put the main chunk of my script in a Do-Until loop, and made the button click change a global variable I created. That broke it out of the loop, closed the window, and let all the finishing tasks run without crashing.
#$Global:uihash is my synchronized hashtable.
#Create synchronized hash table variable
$Global:uiHash.Add(“End”,$false)
#Create Click Event
$Global:uihash.Button.add_click({if([System.Windows.Forms.MessageBox]::Show
("Yes or No?", "Question",[System.Windows.Forms.MessageBoxButtons]::OKCancel) -eq "OK")
{$Global:uiHash.End = $true}
Do {A whole bunch of stuff}
Until ($global:uiHash.end -eq $true)
#Close the window
$Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.Window.close() },"Normal")
#Then process the rest of the script