Part V – Building Responsive PowerShell Apps with Progress bars


This post is part of the Learning GUI Toolmaking Series, here on FoxDeploy. Click the banner to return to the series jump page!

Where we left off

If you’ve followed this series, you should know how to make some really cool applications, using WPF for the front-end and PowerShell for the code-behind.

What will now probably happen is you’ll make a cool app and go to show it off to someone with a three letter title and they’ll do something you never imagined, like drag a CSV file into the text box….(true story).  Then this happens.


We don’t want this to happen.  We want our apps to stay responsive!

In this post we’ll be covering how to implement progress bars in your own application, and how to multi-thread your PowerShell applications so they don’t hang when background operations take place. The goal here is to ensure that our applications DON’T do this.



Do you even thread, bro?

Here’s why this is happening to us…

if we are running all operations in the same thread, from rendering the UI to code behind tasks like waiting for something slow to finish, eventually our app will get stuck in the coding tasks, and the UI freezes while we’re waiting.  This is bad.

Windows will notice that we are not responding to the user’s needs and that we’re staying late at the office too often and put a nasty ‘Not Responding’ in the title. This is not to mention the passive-aggressive texts she will leave us!

If thing’s don’t improve, Windows will then gray out our whole application window to show the world what a bad boyfriend we are.

Should we still blunder ahead, ignoring the end user, Windows will publicly dump us, by displaying a ‘kill process’ dialog to the user.  Uh, I may have been transferring my emotions there a bit…

All of this makes our cool code look WAY less cool.

To keep this from happening and to make it easy, I’ve got a template available here which is pretty much plug-and-play for keeping your app responsive. And it has a progress bar too!

The full code is here PowerShell_GUI_template.ps1.  If you’d like the Visual Studio Solution to merge into your own project, that’s here.  Let’s work through what had to happen to support this.

 A little different, a lot the same

Starting at the top of the code, you’ll see something neat in these first few lines: we’re setting up a variable called $syncHash which allow us to interact with the separate threads of our app.

$Global:syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"

After defining a synchronized variable, we then proceed to create a runspace for the first thread of our app.

  What’s a runspace?

This is a really good question.  A runspace is a stripped down instance of the PowerShell environment.  It basically tacks an additional thread onto your current PowerShell process, and away it goes.

Similar to a PowerShell Job, but they’re much, much quicker to spawn and execute.

However, where PSJobs are built-in and have tools like get-job and the like, nothing like that exists for runspaces. We have to do a bit of work to manage and control Runspaces, as you’ll see below.

Short version: a runspace is a super streamlined PowerShell tangent process with very quick spin up and spin down.  Great for scaling a wide task.

So, back to the code, we begin by defining a variable, $syncHash which will by synchonized from our local session to the runspace thread we’re about to make.  We then describe $newRunSpace, which will compartmentalize and pop-out the code for our app, letting it run on it’s own away from our session.  This will let us keep using the PowerShell or ISE window while our UI is running.  This is a big change from the way we were doing things before, which would lockup the PowerShell window while a UI was being displayed.

If we collapse the rest of the code, we’ll see this.

The entire remainder of our code is going into this variable called $pscmd.  This big boy holds the whole script, and is the first thread which gets “popped out”.

The code ends on line 171, triggering this runspace to launch off into its own world with beginInvoke().  This allows our PowerShell window to be reused for other things, and puts the App in its own memory land, more or less.

Within the Runspace

Let’s look inside $pscmd to see what’s happening there.

unnamed (1)


Finally, something familiar!  Within $pscmd on lines 10-47, we begin with our XAML, laying out the UI.  Using this great tip from Boe, we have a new and nicer approach to scraping the XAML and search for everything with a name and mount it as a variable.

This time, instead of exposing the UI elements as $WPFControlName, we instead add them as members within $syncHash.  This means our Console can get to the values, and the UI can also reference them.  For example:

Even though the UI is running in it’s own thread, I can still interact with it using this $syncHash variable from the console

Thread Count: Two and climbing

Now we’ve got the UI in it’s own memory land and thread…and we’re going to make another thread as well for our code to execute within.  In this next block of code, we use a coding structure Boe laid out to help us work across the many runspaces that can get created here.  Note that this time, our synchronized variable is called $jobs.

This code structure sets up an additional runspace to do memory management for us.
This code structure sets up an additional runspace to do memory management for us.

For the most part, we can leave this as a ‘blackbox’.  It is efficient and practical code which quietly runs for as long as our app is running.  This coding structure becomes invoked and then watchs for new runspaces being created.  When they are, it organizes them and tracks them to make sure that we are memory efficient and not sprawling threads all over the system.  I did not create this logic, by the way.  The heavy lifting has already been done for us, thanks to some excellent work by Joel Bennett and Boe Prox.

So we’re up to thread two.  Thread 1 contains all of our code, Thread 2 is within that and manages the other runspaces and jobs we’ll be doing.

Now, things should start to look a little more familiar as we finally see an event listener:

unnamed (2)


We’re finally interacting with the UI again.  on line 85, we register an event handler using the Add_Click() method and embed a scriptblock.  Within the button, we’ve got another runspace!

This multi threading is key though to making our app stay responsive like a good boyfriend and keep the app from hanging.

Updating the Progress Bar

When the button is clicked, we’re going to run the code in its own thread.  This is important, because the UI will still be rendered in its own thread, so if there is slowness off in ‘buttonland’, we don’t care, the UI will still stay fresh and responsive.

Now, this introduces a bit of a complication here.  Since we’ve got the UI components in their own thread, we can’t just reach over to them like we did in the previous example.  Imagine if we had a variable called $WPFTextBox.  Previously, we’d change the $WPFTextBox.Text member to change the text of the box.

However, if we try that now, we can see that we get an error because of a different owner.

Exception setting Text The calling thread cannot access this object because a different thread owns it.

We actually created this problem for ourselves by pushing the UI into its own memory space. Have no fear, Boe is once again to the rescue here.  He created a function Called-Update window, which makes it easy to reach across threads.  (link)

The key to this structure is its usage of the Systems.Windows.Threading.Dispatcher class.  This nifty little guy appears when a threaded UI is created, and then sits, waiting  for update requests via its Invoke() method.  Simply provide the name of a control you’d like to change, and the updated value.

Function Update-Window {
        Param (

        # This is kind of a hack, there may be a better way to do this
        If ($Property -eq "Close") {

        # This updates the control based on the parameters passed to the function
            # This bit is only really meaningful for the TextBox control, which might be useful for logging progress steps
            If ($PSBoundParameters['AppendContent']) {
            } Else {
                $syncHash.$Control.$Property = $Value
        }, "Normal")

We’re defining this function within the button click’s runspace, since that is where we’ll be reaching back to the form to update values. When I load this function from within the console, look what I can do!

Enter a caption


With all of these tools in place, it is now very easy to update the progress bar as we progress through our logic.  In my case, I read a big file, sleep for a bit to indicate a slow operation, then update a text box, and away it goes.

If you’re looking to drag and drop some logic into your code, this is where you should put all of your slow operations.

Update-Window -Control StarttextBlock -Property ForeGround -Value White
start-sleep -Milliseconds 850
$x += 1..15000000 #intentionally slow operation
update-window -Control ProgressBar -Property Value -Value 25

update-window -Control TextBox -property text -value "Loaded File..." -AppendContent
Update-Window -Control ProcesstextBlock -Property ForeGround -Value White
start-sleep -Milliseconds 850
update-window -Control ProgressBar -Property Value -Value 50

Update-Window -Control FiltertextBlock -Property ForeGround -Value White
start-sleep -Milliseconds 500
update-window -Control ProgressBar -Property Value -Value 75

Update-Window -Control DonetextBlock -Property ForeGround -Value White
start-sleep -Milliseconds 200
update-window -Control ProgressBar -Property Value -Value 100


That’s all there is to it! The hard part here was containing our app into separate threads, but hopefully with the template involved you can easily see where to drop you XAML and how to make your application hum along swimmingly!

I could not have done this post without the many examples provided by Boe Prox on his blog:

Writing WPF Across Runspaces
PowerShell WPF Radio Buttons
Multi-runspace Event Handling
Asynchronous event handling in PowerShell

Additionally, I had help from Joel Bennett (JayKul) of

I learned a lot from reading over Micah Rairdon’s New-ProgressBar cmdlet from his blog, so check that out too.  Finally, Rhys W Edwards has a great cmdlet also on TechNet, with some more good demos if you’re looking for help or inspiration.


54 thoughts on “Part V – Building Responsive PowerShell Apps with Progress bars

  1. mattsky May 17, 2016 / 6:01 pm

    Very nice Stephen! if you have to do this in C# with VS2015 is it simpler?


    • FoxDeploy May 17, 2016 / 8:57 pm

      That’s a great question… BRB studying c#


  2. anmagnussen May 18, 2016 / 1:25 am

    Would be really cool to see you implement C# and PowerShell together, keep up with the great articles.


  3. EM May 18, 2016 / 7:54 pm

    Absolutely awesome Stephen. I’ve read countless articles of Boe, and his knowledge on the topic is great.
    I’ll be using these techniques soon for one of my long running scripts. However, it’s going to be runspace inception.


    • FoxDeploy May 18, 2016 / 8:23 pm

      Aw, thanks for the kind words! I used Boe’s writings to help me understand this and then tried to share what I learned from him.


  4. Andrew Z June 16, 2016 / 10:21 am

    This is a great post (and series)! This has been a huge help on a project I have been working on. I do have a quick question which is how to access input from a user when using this multi-thread setup. I have removed a lot of the code to make it shorter but here is what I’m trying but it’s not working.
    Thank you in advance!


  5. sodawillow June 26, 2016 / 2:10 am

    Hi, the article is great but I noticed the quotes in the code examples get replaced with HTML entities, which makes the code a bit less readable. Don’t know if you can do much about it though?


    • FoxDeploy June 26, 2016 / 8:19 am

      Sometimes WordPress messes with my code because of its syntax highlighting. There’s a link to a github post in this post for just that reason! Thanks for the comment, I’ll fix the code when I can get on a pc !


  6. Jarko June 30, 2016 / 8:52 am

    Great info Fox!

    Am I the only one getting “Main Window(Not Responding)” when clicking the “Start” button from the ISE Snippet exampelcode on github?

    I thought this was what we should get rid of with runspaces?


    • sodawillow July 1, 2016 / 12:05 pm

      I get the same behavior. You can reduce the value for the “intentionally slow operation” to avoid this, or make it quicker.


  7. FoxDeploy July 1, 2016 / 5:02 pm

    Hey guys I’ll check this tonight. I don’t see any delay but sometimes I might be using a slightly different version


    • Jesse Paxson December 16, 2016 / 3:20 pm

      The line where you are updating the textblock with the values of $x is what is causing the hang. It’s listing 1-15,000,000, so it causes update-window to hang until $x can be completely parsed. I tried a foreach loop on that line and it eliminated the “Not responding…” message, but the window is still somewhat unresponsive.


  8. Eirikr July 30, 2016 / 10:57 pm

    This is really great.

    If you have multiple buttons, would you need to include the “function Update-Window { .. }” in each of them to be able to call the function?


    • FoxDeploy July 31, 2016 / 8:29 am

      You only need to include the function in a button or script block if you plan on running the script block within its own runspace. If not (like if the operation is really fast) then feel free to just call the function instead.


  9. Mahendran August 31, 2016 / 2:58 pm

    Hi Stephen,

    want to get current value of the gui text box from my second runscape (the one that is in the $synchash.button.add_click)is there any possibility.


  10. Mahendran September 1, 2016 / 4:52 am

    Hi Stephen,

    Great share, i have tried this in my tool but am not able to get the value of text box in another thread its empty.

    is there any possibility pass the value of the text box to other threads, pasted my script in pastebin please take a look


    • FoxDeploy September 1, 2016 / 1:22 pm

      For one, this tool looks VERY good. Quite sophisticated, well done!

      Give me some help, what text box are you trying to access, and from which part of the UI?


      • Mahendran September 1, 2016 / 1:39 pm

        i am trying get the value of text box filename_txtbox inside the Function Get-UPTime


        • FoxDeploy September 1, 2016 / 2:08 pm

          Store the value that you want to get inside of the sinkhash as a property.


        • Mahendran September 8, 2016 / 1:37 pm

          Thanks Stephen.


        • Jordan September 9, 2016 / 9:28 am

          Found something cool in one of the comments from Scriptabit here:

          Function Read-Textbox(){
          write-host “`nBegin Read-Textbox [$(get-date)]”
          $SyncHash.TXT_Output.Dispatcher.Invoke($Normal,[action]{$Global:Return = $SyncHash.TXT_Output.Text})
          remove-variable -Name Return -scope Global
          write-host “End Read-Textbox [$(get-date)]”

          I tweaked it slightly:

          function Read-Textbox {
          $Normal = [System.Windows.Threading.DispatcherPriority]::Normal
          $SyncHash.txtVMName.Dispatcher.Invoke($Normal,[action]{$Global:Return = $SyncHash.$arg.Text})
          remove-variable -Name Return -scope Global

          $value = Read-Textbox -Arg txtBoxName


        • Jordan September 9, 2016 / 1:04 pm

          I responded a little bit earlier with a Read-Textbox function I found, not sure if my comment went through, Stephen might have to approve it for it to show up. However, in the meantime, I have discovered that adding the value to $synchash is definitely way easier. I’ve had luck so far just doing it like this:



        • FoxDeploy September 9, 2016 / 1:12 pm

          Approved! And yes, the syncHash is the way to go to communicate with the threads of your app


  11. Daniel Petcher October 21, 2016 / 5:45 pm

    I’m just starting to study Runspaces. I’ve read the stuff you cited from Boe Prox that compares them so favorably to Start-Job. Boe’s article doesn’t mention Windows Workflow, though. (I think it was not yet available when he wrote his article.)

    How would using a Workflow compare to using a Runspace for handling a bunch of parallel tasks?


    • FoxDeploy October 21, 2016 / 9:14 pm

      I like where you’re going with this! Workflows are great when you want to code a structure to run forever, or to be able to survive through a reboot.

      If you were building a serious project, you might break it into a few workflows.

      But they are slow and hard to write on top of it.

      You definitely could break your code into multiple threaded workflows but to do so, you will end up working on a proverbial island with little assistance,as few people have great love for workflows.


  12. Samuel Owens January 11, 2017 / 9:37 am

    Hi Stephen,

    This blog series has been really useful. I have used it to build a batch printing tool . At the moment, the tool looks great and technically does what it needs to. The area I am trying to improve is around threads.

    At the moment, the script will use a third party product to render the PDF documents and print them, but this process can take some time. Currently I have popped up a “please wait” box to the user, which, as you say will become (not responding) if the user tries to interact with it. Obviously this is not ideal. Also, I would like to provide the stdout information into the GUI as the print job is taking place, but at the moment it seems like an all or nothing approach.

    This blogpost looked like it could answer my prayers, but I have hit a snag with trying to copy my code into XAML portion. When I try to hit run, and then return $synchhash I get a “cannot call a method on a null valued expression”.

    Name : Error
    Value : {You cannot call a method on a null-valued expression., You cannot call a method on a null-valued expression., You cannot call a method on a null-valued expression., Multiple ambiguous overloads found for
    “Load” and the argument count: “1”….}

    Am I doing something stupid here?



    • Samuel Owens January 11, 2017 / 9:49 am

      Scratch this, I seem to have got over that hurdle. I will keep working through this.



  13. TheMadTechnician January 19, 2017 / 12:30 pm

    Since you are talking about breaking out runspaces into their own threads, and you reference the ISE, you may want to make mention that simply launching the ISE via double click runs it in STA (Single Thread Apartment) mode.

    To avoid potential issues you might want to make a shortcut that launches the ISE with the -mta switch, which makes it run in Multi Thread Apartment mode. As a bonus, this also makes the ISE run in the same apartment model as the console, so you won’t see any different behavior if you, for example, run a script as a scheduled task. Not that it causes issues very often, but it can happen.


    • FoxDeploy January 25, 2017 / 10:29 am

      Thanks for taking the time to leave this comment, that actually resolved a big question I’ve had about difficulties getting multi-threading to work within the ISE.


    • mavericksevmont December 12, 2017 / 12:32 am

      Whoa I wasn’t aware of that!!! Many thanks THEMADTECHNICIAN


  14. rnhamre March 6, 2017 / 8:22 am

    I am adding an output textbox to my GUI, I am able to get everything in to the textbox, but I would want the textbox to autoscroll down to the last line each time I add something to the textbox. Are you able to see what I am doing wrong with the following example code? It is in the second line I am trying to force it to scroll down after each update to the window:

    Update-Window -Control out_textBox -Property text -AppendContent (Get-Service | Sort Status -Desc| out-string)


    • rnhamre March 6, 2017 / 8:31 am

      I can’t edit my post, but shortly after posting I saw my typo: Normanl instead of Normal
      Fun times 🙂


  15. Allen March 10, 2017 / 2:54 pm

    Great series. Many thanks. Adapting now to meet my needs.

    In this lesson, you removed the try/catch when loading the WPF XML because everything is loaded in $pscmd and run in the default runspace. I would like to add the try/catch, but post the error messages back to the command-prompt if it could not load the WPF. Any idea how the initial runspace can talk back to the main process, i.e., command-line ?

    $Reader = (New-Object System.Xml.XmlNodeReader $XAML)
    try{ $SyncHash.Window = [Windows.Markup.XamlReader]::Load( $Reader ) }
    catch [System.Management.Automation.MethodInvocationException] {
    Write-Host ‘We ran into a problem with the XAML code. Check the syntax for this control…’ -ForegroundColor Red
    Write-Host $error[0].Exception.Message -ForegroundColor Red
    if( $error[0].Exception.Message -like ‘*Button*’ ) { Write-Warning ‘Ensure your Button does NOT have a Click=ButtonClick property. PS cannot handle this.’ }
    if( $error[0].Exception.Message -like ‘*TextBox*’ ) { Write-Warning ‘Ensure your TextBox does NOT have a TextChanged=”…_TextChanged” property.’ }
    if( $error[0].Exception.Message -like ‘*MenuItem*’ ) { Write-Warning ‘Ensure your MenuItem does NOT have a Click=”MenuItem_Help_About_Click” property.’ }
    Exit 1
    Write-Host ‘Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .NET is installed.’ -ForegroundColor Red
    Exit 1

    Unfortunately, the try/catch code exists in the $pscmd within the initial runspace, so any errors are displayed out of sight within the runspace (I assume). I know there are errors because my WPF never displayed.

    Were you ever able to figure out how to provide feedback back to the parent process that spawned the runspace in the first place.

    Thank you.


    Also noticed that you edited the default WPF XML to get it to load in this lesson… specifically…

    <Window x:Name="Window_Main" x:Class="WpfApplication1.MainWindow"

    becomes… in order to load application…


    … took me an hour to figure this out since I was used to your other lessons that extracted this for me.



    • FoxDeploy March 12, 2017 / 12:36 pm

      Good points man, and this highlights that I need to revisit this post and revise it for clarity. I agree, me switching the xaml header makes it incompatible with my previous walk through.

      I’ll add that to the to do list. Also, good point about nesting try catch in with the runspaces. I am still trying to work through a good approach for threading and error handling. I plan to look into how this is handled in traditional app development, because I think there will be some good lessons I can learn and apply to PowerShell from there.


  16. Philip P. June 8, 2017 / 3:54 pm

    Best thing ever, just saved me a ton of coding by giving me the ability to not have to create some weird workaround to get my GUI forms to update.


  17. Eoin July 20, 2017 / 4:40 am

    Thanks for this series, I’ve followed every part and created a GUI for my admin tool. However, I had been putting off this last step for a while until I had my app created using the techniques discussed in Parts I-VI. Anyway, I’m having a bit of trouble with something that seems so simple. In the previous section I was populating a variable from text entered by a user as follows:
    $Username = $WPFtxtbox_ADCmds_username.Text
    I’ve tried the following but can’t seem to be able to read the text entered in the GUI
    $username = $syncHash.txtbox_ADCmds_username.Text
    $username = $syncHash.txtbox_ADCmds_username.Text.Value
    Do I need to do something to push the user input into the $syncHash


    • FoxDeploy July 25, 2017 / 7:44 pm

      I’m happy you’ve come so far! Would you mind posting a GitHub gist link of your code? Please remove your company name and any references for your privacy.


  18. superpowershell August 10, 2017 / 4:41 am

    Thanks for the great post! It finally made me understand how to make and like GUI script, since when I first started looking at that, I got the hung application so didn’t really like it at the time 🙂 .
    So I made a GUI script, which works great. My only question is how can you open it by double-clicking on it? I googled this, but so far no solution works (like create a shortcut,…). I’m thinking it might be because of the whole code being in a runspace?


    • FoxDeploy August 13, 2017 / 11:08 am

      Adam Driscoll made a cool tool recently to bundle a PowerShell script into an executable file. It’s called PowerTool (I think)

      I’ll be writing a guide to deploying our GUIs with this tool in the next week or two!


  19. Chipskin August 16, 2017 / 5:03 pm

    Stephen, thanks to this series, I feel like I am being spoiled by PowerShell. I haven’t touched C since. Will definitely point my friends to this blog in the future when they are bamboozled by the speed at which my GUIs are created.


  20. richmawdsley31 November 14, 2017 / 5:43 am

    Hi Stephen,

    Seems like an easy one i’m failing.. how would you add a button to exit the gui with this method?



  21. mavericksevmont December 12, 2017 / 12:29 am

    Hiya Stephen! Big fan of your blog and of foxes too! I hate to nag you, I’m certain you have better things to do, but I am desperate now. What would be the equivalent of using “Get-Job –Name Yourjobname1 | Stop-Job” or say, “ctrl + break” if I wanted to stop, something like an infinite ping, a loop or a very long running process in the runspace such as sending keystrokes for 10 straight hours, but without killing the UI? I got a responsive UI with jobs, but I was never able to make it read the strings from the UI’s input textboxes, and I got my synchash responsive UI thanks to your blog and a nice youtube bloke named Jim Moyle, it can read from the textbox strings, but I haven’t been able to figure out how to stop/abort the runspace function while on the fly, I just get to kill powershell, close the form and the runspace remains doing its thing. I tried messing around with the code used for the jobcleanup bit (.dispose() etc), but no luck there. An answer may grant you an eternal supply of cookies.
    Here is my code, sorry if it looks messy, I love using hashtags:


      • mavericksevmont December 15, 2017 / 12:59 pm

        I found PoshRSJobs. I guess that answers how to do the stop-job with runspaces huh! It also makes it easier to pass variables to the “jobs”, now I’m only stuck at the event handling to make the active $true flag work, i will give it a go with the “$using:” poshrsjob and see how it goes


  22. Paul Gordon January 30, 2018 / 12:46 pm

    Thanks for the article series, I’ve followed it along from beginning to end, and I’ve learned loads. At the completion of part IV I’d created a quite sophisticated GUI app (new AD user creation), which is working pretty much exactly as I need, although I did notice that it would constantly “crash” the PoSh session (whether ISE or console), which lead me to delve into part IV, as I assume it’s the resource starvation during the initial startup of the app which was causing the crash problem (It only *ever* crashed PoSh when starting up the app). I’d initially skim-read part IV & dismissed it as “that looks too difficult”, however I have persevered and I am making great progress, and more to the point, enhancing my understanding hugely. I do have a couple of questions/thoughts though…
    thought: – having created the single-threaded app, the process of conversion to the multi-threaded version is/was quite tiresome!
    Question: – I can’t currently understand the runspace cleanup element of the example… – I (think) I understand the principle of why it’s there and what it’s *supposed* to do – but for the life of me I cannot see that it ever actually *does* anything…
    Every time my app starts, I can see 2/3 more runspaces get created (using get-runspace) – I even changed the code to name the runspaces to be more recognisable… as long as the app is running, I can see the IsCompleted property on the handles are false, the runspace.availability is busy, and as soon as I close the app, all those runspaces change to IsCompleted = True and runspace.availability = Available…all of which makes complete sense to me… – however, what I *don’t* see, – and thought I was expecting to see, is that the cleanup code should then remove those runspaces when the form is closed? – that never happens.. – during testing when I’m stopping & starting the app dozens of times, I end up with hundreds of opened runspaces, and that strikes me as probably being “a very bad thing” ?? – to try to understand what it was doing, I even added some diagnostic logging within the code (the usual “got here” kind of messages) – going out to a text logfile since I can’t simply write to the screen from inside the separated runspace… – I never see any of my diagnostic messages in the log, and it simply appears that it’s never doing anything… – NB this isn’t just with my app, – I’ve also tested extensively with your unmodified example from GitHub. It could be of course, that my log writes are throwing an error, but since (as was mentioned previously) I don’t see the error stream from the isolated runspaces, I’m not aware of any errors that occur within them, which leads to my next thought…

    As each runspace is a powershell session, would I be right in imagining that each has it’s own set of system objects & variables, including the $error object? – Is there a way to add the error variable to the synchash object perhaps? – or any other way I can “get at” the $error variable inside the child runspaces? – especially since the runspaces are left intact after my app has closed, they would presumably still be available…

    And reading that back to myself just gave me another thought… – could not the error stream be redirected to a text file in each runspace?

    Hope you can help with some additional information, and many thanks for the uplift this has given my PowerShell skills so far!

    Liked by 2 people

    • FoxDeploy February 12, 2018 / 5:08 pm

      Hi Paul! Happy you liked the series.

      I agree with you too, I could refresh the syntax and make the transition from a single threaded to multi-threaded GUI simpler in hindsight. However, I’d hate to edit an old blog post so I think I’ll revisit this and add a module to make it simpler to add and manage runspaces instead of all of the code I used in my walktrhough today. This will be a new entry in the series.

      As for the issue you’re describing, would you mind making a GIST with the code and leave some notes in the gist as to how you ended up with dozens of threads? If you share it, I’ll review and try to see why it’s not taking out the garbage like it should.

      I’ll admit that I barely understood runspaces and all of that dotnetty goodness when I first wrote that post. In my current job I’ve been writing c# for months now, so I think I could do better next time.

      Thanks for commenting, have a good one 🙂

      Liked by 1 person

  23. Roly May 15, 2018 / 3:13 pm

    Hi There Stephen, I found this post extraordinarily helpful and am about 90% in understanding. The multi-threading capability is something I will make a point of building on.

    However, I am having trouble with the Update-Window function, Powershell ISE always tells me this: “The term ‘Update-Window’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.”

    It happens whenever I run it in either powershell ise and visual studio code – funnily enough it worked _once_ in VSC. I am stumped!

    Hope to hear from you


    • FoxDeploy May 21, 2018 / 11:08 am

      Can you upload the code to a GitHub GIST and share the link here?


    • kericson2016 May 29, 2018 / 9:23 am

      I’m having this issue now:

      Update-Window function, Powershell ISE always tells me this: “The term ‘Update-Window’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.”


  24. Oleg_D November 25, 2019 / 2:42 am

    Hi! First of all, thanks a lot for the great tutorial!
    However :-), I faced one issue that I would like to implement. Is there a way to check in real-time if the CheckBox was checked by user in GUI and therefore perform scenario according to user’s choice?

    Thank you.


Have a code issue? Share your code by going to and pasting your code there, then post the link here!

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.