One of the main problems with using PowerShell for event handlers in Windows Forms is that long running script blocks cause the UI to hang. This is a poor user experience. To avoid this problem, we will look at the new ThreatJob module and how to use it with a Windows Form.

Creating a Windows Form App

First, lets use PowerShell Pro Tools Windows Form designer in VS Code. Open a PS1 file and execute the PowerShell: Show Windows Form Designer command. The Windows Form designer should open. Create a form that looks like the following.

Name the button btnStartJobs, the checkbox chkRandomWait and the listbox lstResults. Then you can double click on the button. You’ll see that in VS Code a new event handler has been created in your original PowerShell script file.

$btnStartJobs_Click = {


You’ll need to add some additional code to the file to pop open your form. Add the following code underneath the event handler.

. (Join-Path $PSScriptRoot "script.designer.ps1")


If you press F5, you should now see your form running. Sometimes the form is hidden behind VS Code so minimize if it’s missing.

Mutlithreading Your PowerShell Script

Now you’ll want to install the ThreadJob module. The ThreadJob module works the same way as standard PowerShell jobs but it does not create a separate PowerShell process for each job. This drastically increases the performance of thread job. It also means it can access variables, like Windows Forms controls, in the script block. It’s available on the PowerShell Gallery so you can use Install-Module to install it.

Install-Module ThreadJob -Scope CurrentUser -Force

To take advantage of ThreadJob we simply can use the Start-ThreadJob cmdlet to start a new job. We will have to make sure to pass our arguments in through the -ArgumentList parameter. In this example, I’m passing in the form, the current value of the for loop, and whether or not the Random Wait checkbox is checked.

This event handler will start 100 jobs. If the Random Wait checkbox is checked, it will wait between 100 and 2000 milliseconds before adding its value to the results list box. Without using jobs, this type of code would hang the UI.

$btnStartJobs_Click = {

    $RandomWait = $Form1.chkRandomWait.Checked

    1..100 | % {
        Start-ThreadJob -ScriptBlock {
            if ($args[2]) {
                Start-Sleep -Milliseconds (Get-Random -Minimum 100 -Maximum 2000)

        } -ArgumentList @($form1, $_, $RandomWait)

Without the Random Wait checkbox checked, you’ll see the the list box populates very quickly. This the benefit of using ThreadJob over standard PowerShell jobs.

With the Random Wait checkbox checked, you’ll see that it is easy to demonstrate that the UI is not hung as you can move the scrollbar up and down on the list box as items are still being added.