2

I've been tasked with building a powershell script with a GUI which enables users to install network printers. I've succesfully managed to do so, but I cannot meet the requirement that the user be shown a 'please wait' window whilst the printers install. If I switch to the window from the main thread, the GUI hangs. If I move showing the window to a seperate job, I'm never able to close the window again. Here's my attempt:

$waitForm = New-Object 'System.Windows.Forms.Form'

$CloseButton_Click={

    # open "please wait form"
    Start-Job -Name waitJob -ScriptBlock $callWork -ArgumentList $waitForm

    #perform long-running (duration unknown) task of adding several network printers here
    $max = 5
    foreach ($i in $(1..$max)){
        sleep 1 # lock up the thread for a second at a time
    }

    # close the wait form - doesn't work. neither does remove-job
    $waitForm.Close()
    Remove-Job -Name waitJob -Force
}

$callWork ={

    param $waitForm

    [void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
    $waitForm = New-Object 'System.Windows.Forms.Form'

    $labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
    $waitForm.Controls.Add($labelInstallingPrintersPl)
    $waitForm.ClientSize = '502, 103'
    $labelInstallingPrintersPl.Location = '25, 28'
    $labelInstallingPrintersPl.Text = "Installing printers - please wait..."

    $waitForm.ShowDialog($this)
} 

Does anyone know how I can dismiss the $waitForm window when the long-running task has concluded?

Morten Nørgaard
  • 2,609
  • 2
  • 24
  • 24

3 Answers3

2

This first answer was correct, create the form on the main thread and perform the long running task on a separate thread. The reason it doesn't execute the main code until after the form is dismissed is because you're using the 'ShowDialog' method of the form, this method haults subsequent code execution until the form is closed.

Instead use the 'show' method, code execution will continue, you should probably include some event handlers to dispose of the form

Add-Type -Assembly System.Windows.Forms

$waitForm = New-Object 'System.Windows.Forms.Form'
$labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
$waitForm.Controls.Add($labelInstallingPrintersPl)
$waitForm.ClientSize = '502, 103'
$labelInstallingPrintersPl.Location = '25, 28'
$labelInstallingPrintersPl.Text = "Installing printers - please wait..."

$waitForm.Add_FormClosed({
$labelInstallingPrintersPl.Dispose()
$waitForm.Dispose()
})

$waitForm.Show($this)

Start-Job -ScriptBlock $addPrinters | Wait-Job

$waitForm.Close()

$addPrinters = {
    $max = 5
    foreach ($i in $(1..$max)) {
        sleep 1 # lock up the thread for a second at a time
    }
}
2

You could try to run the Windows Forms dialog on the main thread and do the actual work in a background job:

Add-Type -Assembly System.Windows.Forms

$waitForm = New-Object 'System.Windows.Forms.Form'
$labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
$waitForm.Controls.Add($labelInstallingPrintersPl)
$waitForm.ClientSize = '502, 103'
$labelInstallingPrintersPl.Location = '25, 28'
$labelInstallingPrintersPl.Text = "Installing printers - please wait..."
$waitForm.ShowDialog($this)

Start-Job -ScriptBlock $addPrinters | Wait-Job

$waitForm.Close()

$addPrinters = {
    $max = 5
    foreach ($i in $(1..$max)) {
        sleep 1 # lock up the thread for a second at a time
    }
}
Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
  • I did try that but unfortunately the background-job doesn't execute until after the form is dismissed! $addPrinters = { foreach ($i in $(1..$max)) { [console]::Beep() sleep 1 } } Add-Type -Assembly System.Windows.Forms $waitForm = New-Object 'System.Windows.Forms.Form' $waitForm.ClientSize = '502, 103' $waitForm.ShowDialog($this) Start-Job -ScriptBlock $addPrinters | Wait-Job $waitForm.Close() – Morten Nørgaard Feb 24 '12 at 14:16
  • 1
    This was the solution. Check out Kriss Milne's response below for important info. – Morten Nørgaard May 30 '13 at 13:15
0

How about adding a Windows.Forms.Progressbar to the main GUI window? Update its value step by step when adding printers, so users will see that the application is working.

vonPryz
  • 22,996
  • 7
  • 54
  • 65
  • I did try this, however the progressbar loses out to the long-running task and although I increment the progressbar's value throughout, the UI appears unresponsive if I try to drag the main window to a different location. A good suggestion, though, I'll probably do this if all attempts at display a seperate window fails. – Morten Nørgaard Feb 24 '12 at 09:58