multithreadingpowershellwinformsrunspace

How can I update a Powershell gui textbox from another thread?


I've tried variations of the below code without success, it both freezes the GUI and does not write to the textbox and update it. I'm not sure why it is freezing the UI as a thread. (I've seen some examples online using a synchash with an xaml, which I do not want to use.)

Here is the solution, which works for any control you might have:

function getAllControls()
{
    param(
        [System.Windows.Forms.Control]$ParentControl
    )
    $controls = @()
    foreach ($control in $ParentControl.Controls) {
        $controls += $control
        if ($control.Controls.Count -gt 0) {
            $controls += getAllControls -ParentControl $control
        }
    }
    return $controls
}

$btnTest2_Click = {

            $controls = getAllControls -ParentControl $Form;

            $rs = [runspacefactory]::CreateRunspace([initialsessionstate]::CreateDefault2())
            $rs.Open()
            $rs.SessionStateProxy.SetVariable('controls', $controls)
            $instance = [powershell]::Create().AddScript({
                $listBox = $controls | where {$_.Name -eq "myListBox"}
                if ($listBox -ne $null)
                {
                    [Console]::WriteLine($listBox.Name);
                }
                else
                {
                    [Console]::WriteLine("listBox -eq null");
                }

                $ran = [random]::new();
                $ceiling = 10;
                $index = 0;
                while($index -lt $ceiling) {
                    $ranNext = $ran.Next();
                    [Console]::WriteLine($ranNext);
                    Start-Sleep 1
                    $listBox.Items.Add($ranNext);
                    $index++;
                }
            })

            $instance.Runspace = $rs;
            $instance.BeginInvoke();

    }

old1

        $helperThread = {
            
            for ($i = 0; $i -lt 10; $i++)
            {

                $form.Invoke([Action]{
                    $tbxTest2Box.Text = "$($i). Updated from helper thread!"
                    [System.Windows.Forms.Application]::DoEvents();
                })

                Sleep -Seconds 1;
            }
        }

        # Start the helper thread
        $runspace = [runspacefactory]::CreateRunspace()
        $runspace.ApartmentState = "STA"
        $runspace.Open()
        $powershell = [powershell]::Create().AddScript($helperThread)
        $powershell.Runspace = $runspace
        $handle = $powershell.BeginInvoke()


        $powershell.EndInvoke($handle)
        $powershell.Dispose()
        $runspace.Close()

Solution

  • Solution

    function getAllControls()
    {
        param(
            [System.Windows.Forms.Control]$ParentControl
        )
        $controls = @()
        foreach ($control in $ParentControl.Controls) {
            $controls += $control
            if ($control.Controls.Count -gt 0) {
                $controls += getAllControls -ParentControl $control
            }
        }
        return $controls
    }
    
    $btnTest2_Click = {
    
                $controls = getAllControls -ParentControl $Form;
    
                $rs = [runspacefactory]::CreateRunspace([initialsessionstate]::CreateDefault2())
                $rs.Open()
                $rs.SessionStateProxy.SetVariable('controls', $controls)
                $instance = [powershell]::Create().AddScript({
                    $listBox = $controls | where {$_.Name -eq "myListBox"}
                    if ($listBox -ne $null)
                    {
                        [Console]::WriteLine($listBox.Name);
                    }
                    else
                    {
                        [Console]::WriteLine("listBox -eq null");
                    }
    
                    $ran = [random]::new();
                    $ceiling = 10;
                    $index = 0;
                    while($index -lt $ceiling) {
                        $ranNext = $ran.Next();
                        [Console]::WriteLine($ranNext);
                        Start-Sleep 1
                        $listBox.Items.Add($ranNext);
                        $index++;
                    }
                })
    
                $instance.Runspace = $rs;
                $instance.BeginInvoke();
    
        }