Asked by:
In Powershell, displaying a server restart check script in a textbox, freezes the WPF GUI

Question
-
I created a PowerShell script that uses a WPF GUI. It has the option to choose a server from a combobox (more servers will be added later). The chosen server can be rebooted by clicking on the reboot button. Then a script, that will check the reboot procedure, will run in Button.Add_Click and the results will be shown in a textbox.
The problem is that the GUI freezes until Button.Add_Click is finished and will then show the info in the textbox.
I tried to solve this by implementing a runspace. But now I run into new problems. In the ComboBox.add_SelectionChanged section I cannot get the selected combobox content. I want to store the content in a variable $computer.
In the Button.Add_Click section I can’t write to the textbox. When I use $Global:uiHash.Window.Dispatcher.Invoke([action]{ $Global:uiHash.TextBox.AppendText("Check that reboot is initiated properlyn")},"Normal")` then the GUI freezes.
Here is the full code:
$Global:uiHash = [hashtable]::Synchronized(@{}) $newRunspace =[runspacefactory]::CreateRunspace() $newRunspace.ApartmentState = "STA" $newRunspace.ThreadOptions = "ReuseThread" $newRunspace.Open() $newRunspace.SessionStateProxy.SetVariable("uiHash",$Global:uiHash) $psCmd = [PowerShell]::Create().AddScript({ $Global:uiHash.Error = $Error Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase $xaml = @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="CSS Server Reboot" Height="450" Width="800"> <Grid> <ComboBox Name="Server_Combobox" HorizontalAlignment="Left" Margin="240,107,0,0" VerticalAlignment="Top" Width="120"> <ComboBoxItem Name="Server1">10.15.12.148</ComboBoxItem> </ComboBox> <Label Name="Title_Label" Content="CSS – Console Quentris, Server Reboot
" HorizontalAlignment="Left" Margin="240,41,0,0" VerticalAlignment="Top" Height="34" Width="284" FontSize="16"/> <Button Name="Reboot_Button" Content="Reboot" HorizontalAlignment="Left" Margin="449,107,0,0" VerticalAlignment="Top" Width="75"/> <TextBox Name="Reboot_Textbox" Grid.Column="1" HorizontalAlignment="Left" Height="173" Margin="81,180,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="294"/> </Grid> </Window> "@ # $reader=(New-Object System.Xml.XmlNodeReader $xaml) $Global:uiHash.Window=[Windows.Markup.XamlReader]::Parse($xaml ) $Global:uiHash.TextBox = $Global:uiHash.window.FindName("Reboot_Textbox") $Global:uiHash.Button = $Global:uiHash.window.FindName("Reboot_Button") $Global:uiHash.ComboBox = $Global:uiHash.window.FindName("Server_Combobox") $Global:uiHash.Window.ShowDialog() | out-null }) $psCmd.Runspace = $newRunspace $handle = $psCmd.BeginInvoke() Start-Sleep -Milliseconds 100 $computer = "" $Global:uiHash.ComboBox.add_SelectionChanged({ $Script:computer = $Global:uiHash.Combobox.SelectedItem.Content }) $Global:uiHash.Button.Add_Click({ $Username = 'xxxx' $Password = 'xxxx' $pass = ConvertTo-SecureString -AsPlainText $Password -Force $SecureString = $pass $MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString Restart-Computer -ComputerName $computer -Force -Credential $MySecureCreds $timeout=5 $MAX_PINGTIME = $timeout * 60 $max_iterations = $MAX_PINGTIME/5 $Notification_timeout = 10 # in seconds function ping-host { param($pc) $status = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$pc'" if( $status.statuscode -eq 0) { return 1 } else { return 0 } } if(ping-host -pc $computer) { $status = "online`n" for ($i=0; $i -le $max_iterations; $i++) { if (!(ping-host -pc $computer )) { break } Start-Sleep -Seconds 5 if($i -eq $max_iterations) { $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("$computer never went down in last $timeout minutes`n")},"Normal") $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("Check that reboot is initiated properly`n")},"Normal") $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("$computer is still ONLINE; Check that reboot is initiated properly`n")},"Normal") exit } } $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("$computer is offline now; monitoring for online status`n")},"Normal") } else { $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("$computer is offline; Monitoring for online status`n")},"Normal") $status = "offline`n" } for ($i=0; $i -le $max_iterations; $i++) { if ((ping-host -pc $computer )) { break } Start-Sleep -Seconds 5 if($i -eq $max_iterations) { $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("Your computer never came back online in last $MAX_PINGTIME seconds`n")},"Normal") $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("Check that nothing is preventing starup`n")},"Normal") $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("$Computer is NOT coming online; Something is preventing its startup`n")},"Normal") exit } } $Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("Your computer is Online Now; Task done; exiting")},"Normal") })
Monday, July 30, 2018 8:42 AM
All replies
-
try using jobs so that the GUI and the tasks aren't using the same thread.
https://blogs.technet.microsoft.com/heyscriptingguy/2012/12/31/using-windows-powershell-jobs/
Monday, July 30, 2018 9:36 AM -
There is no need to ping for completion. Use "Wait" and the command will wait on the return.
Restart-Computer -ComputerName $computer -Force -Credential $MySecureCreds -Wait
To run the command as a job use the following command:
$job = Restart-Computer -ComputerName $computer -Force -Credential $MySecureCreds -AsJob
See help for details about how to use this command. With "AsJob" the command also waits and the job completes or fails when the remote computer is up or the wait has timed out.
\_(ツ)_/
- Edited by jrv Monday, July 30, 2018 1:38 PM
Monday, July 30, 2018 1:34 PM -
Using the Start-Job cmdlet has no result. The GUI freezes as long as the Job is running.
For example:
$Global:uiHash.Button.Add_Click({ $computer = "10.15.12.148" $sb = [scriptblock]::Create("Test-Connection -ComputerName $computer") Start-Job -Name test3 -ScriptBlock $sb | Wait-Job Get-Job test3 $Results = Receive-Job -Name test3 Write-Host $Results })
Tuesday, July 31, 2018 1:09 PM -
Using the Start-Job cmdlet has no result. The GUI freezes as long as the Job is running.
For example:$Global:uiHash.Button.Add_Click({ $computer = "10.15.12.148" $sb = [scriptblock]::Create("Test-Connection -ComputerName $computer") Start-Job -Name test3 -ScriptBlock $sb | Wait-Job Get-Job test3 $Results = Receive-Job -Name test3 Write-Host $Results })
Tuesday, July 31, 2018 1:10 PM -
Remove the "Wait-Job" As long as that is there the form will freeze. Use a timer to poll the job on each timer tick until the job completes.
$timer.add_Tick({ if($job.Status -ne 'Running){ Write-Host 'Job completed' }else{ Write-Host 'job still running' } })
In WPF you will have to use a System.Timer as WPF has no timer control.
\_(ツ)_/
- Edited by jrv Tuesday, July 31, 2018 1:16 PM
Tuesday, July 31, 2018 1:15 PM -
Here is an article on how to use a timer in WPF with PowerShell.
\_(ツ)_/
Tuesday, July 31, 2018 1:21 PM