6

EDIT : I was able to get it working, see below for my solution. The commenters below are correct that Powershell isn't really ideal for GUI's and threading, but it can be done.


I've got a form in Powershell that uses Start-Job to run functions in the background without freezing the GUI. My goal is to continuously check the status of those jobs for their output. I managed to use the Windows Forms Timer to check the results of the job and update the GUI accordingly.

It's all working fine, but it seems sloppy. Is this the best way to accomplish a GUI refresh? I'm relatively new to Powershell and I want to improve my coding.

Example of what I'm doing:

$jobScript = {
    Start-Sleep 5
    Write-Output "The job has finished running"
}

$timerScript = {
    $timer.Stop()
    $jobResult = Get-Job | Receive-Job -Keep
    if ($jobResult) {
        $btn.text = $jobResult
    } else {
        $timer.Start()
    }
}

Add-Type -AssemblyName System.Windows.Forms
$form             = New-Object System.Windows.Forms.Form
$form.ClientSize  = '300,300'
$form.topmost     = $true

$btn              = New-Object System.Windows.Forms.Button
$btn.Text         = "The job is still running"
$btn.Width        = 300
$btn.Height       = 300
$form.Controls.Add($btn)

$timer            = New-Object System.Windows.Forms.Timer
$timer.Interval   = 100
$timer.add_Tick($timerScript)
$timer.Start()

Start-Job -ScriptBlock $jobScript

$form.ShowDialog()

Update: My solution

Using Register-ObjectEvent did not work, it seemed like it was fighting with the GUI for the thread. Instead I was able to use [System.Windows.Forms.Application]::DoEvents(). This allows the GUI to be moved around, and once it's done being moved, the thread will resume. The big caveat here is that execution is paused as long as the GUI is being moved, so if your code needs to react to the background job on a time limit, this could cause errors.

Example code block:

$jobScript =
{
    Start-Sleep 5
    Write-Output "The job is completed"
}

Add-Type -AssemblyName System.Windows.Forms
$form             = New-Object System.Windows.Forms.Form
$form.ClientSize  = '300,300'
$form.topmost     = $true

$btn              = New-Object System.Windows.Forms.Button
$btn.Text         = "..."
$btn.Width        = 300
$btn.Height       = 300
$form.Controls.Add($btn)

$btn.add_click({
    $btn.Text = "Starting job"
    $jobby = Start-Job -ScriptBlock $jobScript
    Do {[System.Windows.Forms.Application]::DoEvents()} Until ($jobby.State -eq "Completed")
    $btn.Text = Get-Job | Receive-Job
})

$form.ShowDialog()
Luke DeWitt
  • 63
  • 1
  • 5
  • The PowerShell way to show progress is with `Write-Progress`. See `Get-Help Write-Progress -examples`. – Kory Gill Dec 10 '18 at 22:31
  • 2
    @KoryGill With winform applications it's not. – Ansgar Wiechers Dec 10 '18 at 22:32
  • @KoryGill thanks for the reply. My real question is how to have the form wait and update once the background job has returned its result, be it with `Write-Progress` or with `Write-Output` – Luke DeWitt Dec 10 '18 at 22:40
  • Making a responsive winforms GUI in Powershell is a real pain. I'd consider some other way of interaction with a user. – montonero Dec 11 '18 at 07:20

2 Answers2

1

You can use events:

 $job = Start-Job {sleep 3; Write-Output "Dummy job completed"}

$callback = {

Write-Host "Event Fired"
Unregister-Event -SourceIdentifier "DummyJob"
Write-Host ($job | Receive-Job)

}

$evt = Register-ObjectEvent -InputObject $job -EventName StateChanged -SourceIdentifier "DummyJob" -Action $callback 


# to remove all 
Get-Job | Remove-Job -Force # jobs and events
Get-EventSubscriber | Unregister-Event  # events
Mike Twc
  • 2,230
  • 2
  • 14
  • 19
  • Thank you! I'll look into this as a workaround until I get around to doing it in C#, as I keep hearing I ought to. – Luke DeWitt Dec 11 '18 at 14:10
  • Doing some testing with this, `Register-ObjectEvent` yields weird results when using winforms. I did some googling and it seems like it fights with the form for the thread. I'll update my OP with the solution I ended up finding. – Luke DeWitt Dec 11 '18 at 21:35
-1

You might want to post this on CodeReview.StackExchange.com.

I sort of hate it when people build UI's in Powershell. If you want a proper Windows forms app, just write it in C#. So I disagree with the design at its premise.

I like your impulse to move away from the polling design; you initiate the job then poll to see if it's completed. I think an event handler might be a better choice. Check out the section "Monitor a Background Job" in the article PowerShell and Events: Object Events. It's an oldie but a goodie.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Adam
  • 3,891
  • 3
  • 19
  • 42
  • 1
    Thanks so much for the response and the links. – Luke DeWitt Dec 11 '18 at 14:09
  • Would you please expand on why you don't prefer Powershell GUIs? I assume it's just more of a workaround than an intended usage? – Luke DeWitt Dec 11 '18 at 14:14
  • 1
    That's a great question. I can use my bicycle to generate electricity. The approach can keep me fit, and it's more environmentally friendly. UI's with Powershell is sort of like that. I can do it. It makes UI development more accessible, especially for Powershell developers. However, Powershell wasn't intended for that purpose and doesn't get a ton of playing time in the GUI arena. For that reason, you'll find little gotchas the deeper you go down that rabbit hole. If you stick to a language with designed for UI dev, you'll hit less bumps - IMHO. Sorry I had to keep it short; char limit. – Adam Dec 11 '18 at 19:44
  • Excellent analogy, again I appreciate your time. I ended up finding a better workaround for my purposes (I'll edit my OP), but I'll definitely start spending time in other languages to prepare for the future. – Luke DeWitt Dec 11 '18 at 21:31
  • 1
    PowerShell is .NET. You can do everything you can do in C#. And you can build very nice UIs in PoSh (example: https://bytecookie.wordpress.com/powershell-code-manager/ ). It is less complicated and less work than using c#. – Rob Oct 08 '19 at 10:11
  • you can not use c# in professional environments where an enterprise visual studio license would be required $_$ – user2455808 Oct 02 '20 at 04:27
  • @user2455808 you don't need Visual Studio to write software in C#. – Adam Oct 14 '20 at 15:29
  • do you have something that uses the .net framework to it's full extent with intellisense, multithreading handling, ect and is allowed to be used for commercial use? Mono is horrible and it's the only thing i've seen come close – user2455808 Oct 15 '20 at 05:10
  • @user2455808 why don't you post this as a question. It sounds like this is a point of concern for you. – Adam Oct 15 '20 at 09:08