See the question and my original answer on StackOverflow

You can't really have a Console and a Windows run on the same thread. I suggest you create another thread to host the window, like this (It's a C# console app, but it should be easy to translate):

class Program
{
    static void Main(string[] args)
    {
        Form form = new Form();
        Label label = new Label();
        label.Text = "original";
        form.Controls.Add(label);

        // run this form on its own thread, so it can work properly
        Thread t = new Thread((state) => Application.Run((Form)state));
        t.Start(form);

        Console.ReadLine();

        // note you *could* do label.Text = "other" here, but BeginInvoke is always recommended
        label.BeginInvoke((Action)delegate() { label.Text = "other"; });

        Console.ReadLine();
        Application.Exit();
    }
}

EDIT: It took me a while to cook a Powershell version, but here it is. The trick is: you just can't create a standard .NET thread from the Powershell environement. Period. As soon as you try to do it, you will crash Powershell. The underlying error that causes the crash is this:

System.Management.Automation.PSInvalidOperationException: There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspace type

And, you can't really try to set this DefaultRunspace variable from another thread because it's marked threadstatic (one copy per thread)! Now, if you google for multi-threading Powershell, you will find many articles about Powershell Jobs. But these allow multi-tasking, but it's not really multi-threading, it's more heavyweight.

So... turns out the solution is really to use Powershell features, that is: Powershell runspace. Here it is:

[reflection.assembly]::LoadWithPartialName( "System.Windows.Forms")
$form = New-Object System.Windows.Forms.Form
$label = New-Object System.Windows.Forms.Label
$label.Text = 'original'
$form.Controls.add($label)

$ps = [powershell]::create()
$ps.AddScript(
     {
     [System.Windows.Forms.Application]::Run($form)
     })
$ps.Runspace.SessionStateProxy.SetVariable("form", $form)
$ps.BeginInvoke()

Read-Host

$label.BeginInvoke(
    [Action[string]] {
        param($Message)
        $label.Text = $Message
    },
    'changed'
)

Read-Host

[System.Windows.Forms.Application]::Exit()
$ps.Dispose()