title: Update WinForms (NOT WPF) UI from another thread
body: <p>EDIT: The sample code is far off from the original goal, but there's a legitimate answer to the question that I actually posed, so I've edited the question and title.</p>
  
  <p>I am writing a multithread PowerShell script with a WinForms GUI and would like to update the GUI from one of the other threads in the script. I'm having no luck doing this at all. In fact, I can't even get <code>BeginInvoke</code> to work.</p>
  
  <p>Here's a very stripped down example. This is sample code, not production, so it's not pretty, but it's replicating the issue:</p>
  
  <pre><code>$script:SynchronizedUIHash = [Hashtable]::Synchronized(@{});
  
  [reflection.assembly]::LoadWithPartialName( "System.Windows.Forms")
  $form= New-Object Windows.Forms.Form
  $label = New-Object Windows.Forms.Label
  $label.Text = 'original'
  $script:SynchronizedUIHash.label = $label
  
  $form.Controls.add($label)
  $form.show()
  [System.Windows.Forms.Application]::DoEvents()
  
  Read-Host
  
  $label.BeginInvoke(
      [Action[string]] {
          param($Message)
          [Console]::Beep(500,300)
          $l = $SynchronizedUIHash.label
          $l.Text = $Message
          [System.Windows.Forms.Application]::DoEvents()
      },
      'changed'
  )
  
  Read-Host
  
  $form.close()
  </code></pre>
  
  <p>I don't even hear the console beep, so it's as if the <code>BeginInvoke</code> code isn't being executed at all.</p>
  
  <p>If I throw in a <code>[System.Windows.Forms.Application]::DoEvents()</code> after the <code>Read-Host</code> then I see the change so it seems to be a timing issue, but I don't want to go into a spin loop or something silly like that - I want to fire and forget the <code>BeginInvoke</code> and have the results visible as appropriate. I see other answers like the one to <a href="https://stackoverflow.com/questions/7414187/powershell-job-event-action-with-form-not-executed">PowerShell: Job Event Action with Form not executed</a> but that needs to be in a check, sleep, check loop. </p>
  
  <p>There's got to be a simpler way that I'm missing... what is it?</p>
  
  <p>What is the right solution here?</p>
  
  <p>EDIT: Keith's comment made me realize I missed a key point going from production to the stripped sample code - launching a job is indeed multi-process, but the sample code isn't, so the sample doesn't reflect real life, and I'll have to rethink the whole thing. That all said - the sample code doesn't use jobs, and it doesn't work as I expect either, so I'd like to understand why, even if that doesn't help me in this specific case.</p>