Question

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?

Was it helpful?

Solution

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
    }
}

OTHER TIPS

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
    }
}

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top