none
(Powershell + WPF + Runspaces) Cannot make work a multi-threaded WPF GUI using runspaces. RRS feed

  • Question

  • Hi Guys,

    I have a WPF GUI with one button and three TextBox.

    I have a function that when I click the button, a Thread is created and instantly should begin to write the numbers in the first TextBox. When I click a second time, a second Thread is created and should begin to write numbers in the second TextBox, and so on.

    My Script has one Thread in a runspace, where the WPF GUI Runs, and other threads are created within dedicated runspaces every time I clicked the button.

    I'm trying to update the WPF Controls through the dispatcher objects, from another Thread, but It doesn't work.

    I tried passing the Sync'd HashTable that owns the controls as a SessionStateProxy Variable for the other Threads, but the GUI hangs. I tried passing it directly to the thread's scriptblock too, but unfortunately, it doesn't work.

    Could you help me? Thanks.
    • Edited by JRLOPS Sunday, May 28, 2017 3:33 AM
    Sunday, May 28, 2017 3:29 AM

Answers

  • After debugging for about 10 hours, Finally, I found the issue, that prevented it from working.

    The issue was in the following line of code, rather in the logic:

    $EsyncHash.Output1.Dispatcher.Invoke([action]{1..10 | %{$EsyncHash.Output1.Text = $_; sleep -Seconds 2}},"Normal")

    I changed the logic to:

    1..10 | %{ $EsyncHash.Output1.Dispatcher.Invoke([action]{$EsyncHash.Output1.Text = $_},"Normal"); sleep -Seconds 2}

    It works.

    In summary, I think that in the first line of code when we invoke the dispatcher object for the TextBox "Output1" It is being slept 2 seconds and That's why It does not reach to update the GUI and freezes the UI.


    • Marked as answer by JRLOPS Monday, May 29, 2017 3:48 PM
    • Edited by JRLOPS Monday, May 29, 2017 3:50 PM
    Monday, May 29, 2017 3:46 PM

All replies

  • We cannot help you based on the limited information you are supplying.  Without an example of your issue wht you are asking is way too vague to even guess at an answer.

    It your code was copied from the Internet I recommend that you first learn how PowerShell nd WPF work before attempting to modify arbitrary code.


    \_(ツ)_/

    Sunday, May 28, 2017 4:13 AM
  • Hi jrv,

    I made the following WPF GUI:

    I created a function for the button, so each time that the button is clicked, It creates a thread. Each Thread should write numbers in TextBoxes. When I click the button the GUI hangs.

    Add_Click code:

    $syncHash.ButtonStart.Add_Click({
            $runspacecounter++
            Start-Runspace -RSCounter $runspacecounter       
        })
    
    $runspacecounter = 0
    function Start-Runspace{
        param($RSCounter)
        #Temp Variable for store the Created RunSpace
        New-Variable -Name tempvar -Value $null
        #Dynamic variable 
        New-Variable -Name "RS$RSCounter" -Value ([runspacefactory]::CreateRunspace())
        $tempvar = Get-Variable -Name "RS$RSCounter" -ValueOnly
        $tempvar.ApartmentState = "STA"
        $tempvar.ThreadOptions = "ReuseThread"
        $tempvar.Open()
        $ScriptB = {
            param($EsyncHash,$Erunspacecounter)
            switch($Erunspacecounter){
                1 {$EsyncHash.Output1.Dispatcher.Invoke([action]{1..10 | %{$EsyncHash.Output1.Text = $_; sleep -Seconds 2}},"Normal"); break}
                2 {$EsyncHash.Output2.Dispatcher.Invoke([action]{1..10 | %{$EsyncHash.Output2.Text = $_; sleep -Seconds 2}},"Normal"); break}
                3 {$EsyncHash.Output3.Dispatcher.Invoke([action]{1..10 | %{$EsyncHash.Output3.Text = $_; sleep -Seconds 2}},"Normal"); break}
                default {$EsyncHash.Start.Dispatcher.Invoke([action]{$EsyncHash.Start.IsEnabled = $false},"Normal"); break}
            }                  
        }
        $psCmd = [PowerShell]::Create().AddScript($ScriptB)
        #Passing the Synchronized Hashtable to the new thread
        $psCmd.AddArgument($syncHash)
        $psCmd.AddArgument($runspacecounter)
        $psCmd.Runspace = $tempvar
        #$AsyncObject = 
        $psCMD.BeginInvoke()
        #Delete the tempvar for the future dynamic variable
        Remove-Variable -Name tempvar -Force -Verbose   
    }

    According to the documentation and a lot of blogs of Boe Prox and https://www.codeproject.com/Tips/895840/Multi-Threaded-PowerShell-Cookbook, only exists three methods to do that:

    1. Sharing the Synchronized Hashtable that stores the controls in a Thread's StateSessionProxy variable.
    2. Passing the Synchronized Hashtable that stores the controls as a parameter for a Thread's Script block.
    3. Sharing the Synchronized Hashtable that stores the controls in a Thread's InitialStateSession variable.

    I tried the three methods, none of these work.

    Note: The only way that works is when GUI is executed, and then in ISE I launched the Threads.

    Why isn't working from inside the Add_Click() Function?

    Thanks.


    • Edited by JRLOPS Sunday, May 28, 2017 3:45 PM
    Sunday, May 28, 2017 4:42 AM
  • After debugging for about 10 hours, Finally, I found the issue, that prevented it from working.

    The issue was in the following line of code, rather in the logic:

    $EsyncHash.Output1.Dispatcher.Invoke([action]{1..10 | %{$EsyncHash.Output1.Text = $_; sleep -Seconds 2}},"Normal")

    I changed the logic to:

    1..10 | %{ $EsyncHash.Output1.Dispatcher.Invoke([action]{$EsyncHash.Output1.Text = $_},"Normal"); sleep -Seconds 2}

    It works.

    In summary, I think that in the first line of code when we invoke the dispatcher object for the TextBox "Output1" It is being slept 2 seconds and That's why It does not reach to update the GUI and freezes the UI.


    • Marked as answer by JRLOPS Monday, May 29, 2017 3:48 PM
    • Edited by JRLOPS Monday, May 29, 2017 3:50 PM
    Monday, May 29, 2017 3:46 PM