locked
In Powershell, displaying a server restart check script in a textbox, freezes the WPF GUI RRS feed

  • 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&#xD;&#xA;" 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
  • Tuesday, July 31, 2018 1:21 PM