derhoeppi
Goto Top

PowerShell GUI WPF

Hallo,

heute habe ich hoffentlich wieder ein einfaches Thema. Und zwar besitzt meiner GUI (WPF) einige Checkboxen und auch eine Progressbar. Während der Laufzeit meines Skriptes, sollen die Checkboxen deaktiviert werden (IsEnabled = $false). Für die Progressbar habe ich ebenfalls eine Funktion die mir den Wert errechnet, der durch die Progressbar abgebildet wird. Die Berechnung und auch der Befehl zum deaktivieren der Checkbox funktioniert, wird mir aber nicht zur Laufzeit in der GUI angezeigt. Den Wert für die Progressbar lasse ich im Hintergrund in der PowerShell Console ausgeben.

Den Aufruf der GUI habe ich sowohl mit einem einfachen ShowDialog() als auch mit der Dispatcher.InvokeAsync({ShowDialog()}) versucht. Das verhalten ändert sich allerdings nicht. Die Anzeige wird während des Skriptes erst aktualisiert, wenn ich durch das Skript eine Messagebox öffne.

Woran kann das liegen?

Gruß
derhoeppi

Content-Key: 285511

Url: https://administrator.de/contentid/285511

Ausgedruckt am: 29.03.2024 um 00:03 Uhr

Mitglied: 122990
122990 14.10.2015 aktualisiert um 13:57:08 Uhr
Goto Top
Moin,
heute habe ich hoffentlich wieder ein einfaches Thema
Für Anfänger eher ein schwierigeres Thema.
Stichwort MultiThreading während du eine andere Aktion im selben Thread-Kontext ausführst wie die GUI wird sich diese erst wieder aktualisieren wenn deine Aktion fertig ist. Also musst du deine Aktion die du im Hintergrund ausführen willst auch in einem separaten Thread oder Job laufen lassen. Ebenso blockieren Schleifen im Hauptkontext die GUI.

Hier ein paar Beispiele aus dem Forum

Gruß grexit
Mitglied: derhoeppi
derhoeppi 14.10.2015 um 14:33:32 Uhr
Goto Top
Hallo grexit,

okay, dann komme ich doch noch schneller zu einem Thema, welches ich in meinem Skript angehen will - runspace. Ich werde mir mal die Links von Dir und die weiterführenden Links in den Beiträgen durchlesen. Wenn ich Fragen zur Implementierung habe, würde ich mich wieder melden.

Gruß
derhoeppi
Mitglied: derhoeppi
derhoeppi 15.10.2015 um 22:16:50 Uhr
Goto Top
Hallo,

ich habe nun einiges zum Thema Runspace gelesen und auch diverse Beispiele gesehen. Aufgrund diverser Artikel im Internet möchte ich dieses gerne statt PSJob nutzen (aus Performance-Gründen).

Nun bin ich dabei das gelesene in mein Skript einzubauen. Prinzipiell möchte ich den Runspace innerhalb einer Funktion aufrufen. Folgendes habe ich dazu geschrieben.

Function Progressbar {
    Write-Host $Progress
    $syncHash = [hashtable]::Synchronized(@{})
    $syncHash.progress = $PB_Status
    $syncHash.value = $Progress
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)  
    $psCmd = [powershell]::Create()
    $psCmd.Runspace = $newRunspace
    $psCmd.AddScript({
    $syncHash.progress.Value = $syncHash.value
    })
    $psCmd.Runspace = $newRunspace
    $handle = $psCmd.BeginInvoke()
    While (-Not $handle.IsCompleted) {
        Start-Sleep -Milliseconds 100
    }
    $psCmd.EndInvoke($handle)
    $newRunspace.Close()
    $psCmd.Dispose()
}

$PB_Status ist meine Progressbar und $Progress ist ein Integer-Wert den ich zuvor berechnet habe. Wenn ich diese Funktion aufrufe, ändert sich jedoch nichts in der Progressbar, obwohl die Variable $Progress aufsteigende Werte annimmt.
Da ich die Funktion mehrmals in meinem Skript aufrufe - es soll ja auch ein Fortschritt zu erkennen sein - würde mich interessieren, ob ich den Runspace zum Skriptbegin initialisieren kann und dann immer wieder diesen Thread mit dieser Funktion starten kann?

Gruß
derhoeppi
Mitglied: 122990
122990 16.10.2015 um 11:41:42 Uhr
Goto Top
Leider nutzt du de Hashtable nicht richtig.
Zur Erläuterung:
Die "synchronisierte Hashtable" hat den Zweck das der Sub-Thread Steuerelemente im GUI-Thread ändern kann weil normalerweise ein anderer Thread per Default nicht Threadübergeifend auf Steuerelemente eines anderen Threads zugreifen darf (Cross-Thread-Call). D.h. es wird zuerst eine leere Hashtable erstellt und dann eine Eigenschaft dieser hinzugefügt. Dieser neuen Eigenschaft wird das Progressbar-Objekt deines GUI hinzugefügt.
Diese Eigenschaft ist somit auch automatisch im SubThread erreichbar, es kann also im SubThread der Wert der Progressbar gesetzt werden z.B.
 $syncHash.progress.Value = 50
für 50 Prozent.

In deinem Teil oben machst du einige Fehler wie z.B. das hier hat keinen Nutzen:
$syncHash.value = $Progress
denn du hast ja schon eine Eigenschaft der Hashtable hiermit übergeben:
$syncHash.progress = $PB_Status
Und auf diese Eigenschaft kannst du aus dem SubThread zugreifen wie oben beschrieben. Da es eine synchronized Hashtable ist wird bei einer Änderung daran diese im GUI-Thread ebenfalls aktualisiert, und reflektiert die Änderungen an der Progressbar wieder.

Das ist die ganze Magie dahinter.

Gruß grexit
Mitglied: derhoeppi
derhoeppi 16.10.2015 um 12:29:11 Uhr
Goto Top
Hallo grexit,

ich habe im AddScript Block nur das
$syncHash.progress.Value = 50
eingetragen. Leider ändert sich in der GUI gar nichts. "$syncHash.value = $Progress" habe ich zuvor aus kommentiert. Zum Troubleshooting lasse ich
Write-Host $syncHash.progress.Value
vor und nach dem "$handle = $psCmd.BeginInvoke()" ausgeben. Der Wert beträgt immer 0, obwohl die Funktion mehrmals in meinem Skript aufgerufen wird. Warum die Progressbar keinen anderen Wert annimmt kann ich aktuell nicht nachvollziehen. Du schreibst ja selbst das "$syncHash.progress.Value = 50" eine Veränderung in meiner GUI herbeiführen sollte.

Mit dem "$syncHash.value = $Progress" wollte ich die Hashtabelle um eine Variable $Progress erweitern. In dieser Variable steht mein Wert, den ich der Progressbar zuweisen möchte. Diesen Wert möchte ich gerne in den Sub-Thread geben, damit ich diesen nicht im Sub-Thread berechnen muss. Dazu werte ich den Status von Checkboxen aus, die ich dann auch alle aus dem Sub Thread ansprechen müsste.

Gruß
derhoeppi
Mitglied: 122990
122990 16.10.2015 aktualisiert um 12:48:21 Uhr
Goto Top
Schau dir das verlinkte Beispiel nochmal ganz genau an und was du vergessen hast. Der Sinn und Zweck des Threads ist es ja nicht nur die Progressbar aus diesem upzudaten, sondern in diesem die Aufgabe ablaufen zu lassen die erledigt werden soll und gleichzeitig daraus die Progressbar upzudaten.

Irgendwie hast du da noch einen Knoten im Hirn.

Das Thema ist leider etwas komplexer aber da musst du durch wenn du das damit lösen willst. Aller Anfang ist schwer, da hilft nur Doku lesen und Debugging.

Viel Erfolg weiterhin
Gruß grexit
Mitglied: derhoeppi
derhoeppi 18.10.2015 um 19:55:10 Uhr
Goto Top
Hallo grexit,

derzeit werden es immer mehr Knoten im Kopf. face-smile
Ich habe mir im Internet nun nochmals mehrere Beiträge / Artikel angesehen um das Thema Runspace zu verstehen. Die Beispiele die gefunden habe, sind allerdings wenig hilfreich, weil eben genau das Verhalten der Progressbar oder das befüllen irgendwelcher TextBoxen direkt im Scriptblock stattfindet. An und für sich ist das auch in Ordnung und korrekt. Nach deinem zweiten Beitrag bin ich jedoch etwas verwirrt. Du hast geschrieben das "$syncHash.value = $Progress" unnötig ist. Wenn ich jedoch den fertigen Wert ($Progress = Value) in die Hashtabelle schreibe, muss ich diesen doch nicht darin berechnen lassen.

Ich habe soeben meine komplette GUI in einem Runspace laufen lassen. Somit war ich aus meinem Hauptthread in der Lage die Progressbar zu aktualisieren. Mit dem Starten der GUI in einem separaten Thread habe ich nun aber ein anderes Problem. Die GUI Objekte (Button, ListViews, Textboxen) liegen nun in einem anderen Thread. Wie kann ich denen Aktionen, wie zum Beispiel ein OnClick für einen Button hinzufügen, der wiederum in meinem Haupt-Thread ablaufen soll. Ich habe ebenfalls die Hashtabelle erzeugt in der alle WPF Elemente liegen. Diese kann ich ansprechen und Eigenschaften verändern. Wenn ich jedoch in meiner GUI ein Event z.B. ein Button_onClick durchführe, möchte ich das dies im Hauptthread ausgeführt wird.

Dann habe ich noch eine andere Frage. Bei dem Versuch meine GUI in dem SubThread laufen zu lassen, konnte ich ohne Probleme die Hashtabelle abfragen. Wie aber kann ich genau das realisieren, wenn die GUI in meinem Haupthread läuft und die Eingaben blockiert sind. Muss ich mit einer Textausgabe oder Messagebox arbeiten oder wie kann ich das Troubleshooten?

Gruß
derhoeppi
Mitglied: derhoeppi
derhoeppi 19.10.2015 aktualisiert um 08:24:09 Uhr
Goto Top
Guten Morgen,

das Thema wurmt mich face-smile . Wenn ich die GUI in einen separaten Thread auslagere, kann ich die Progressbar verändern. Wenn ich die GUI im Hauptthread laufen lasse, schaffe ich es nicht diese über einen SubThread zu verändern. Ich habe mir nun im SubThread eine Messagebox erstellt, in der ich die Werte von $syncHash.value ausgeben lasse. Die Messagebox meldet mir brav den Wert, der im Hauptthread in die Hashtabelle geschrieben wurde. Nach dem Ende des Skriptes schaue ich mir mit $syncHash den Inhalt der Hashtabelle an. Dort sehe ich genau die Objekte, die ich der Tabelle hinzugefügt habe.
Darin habe ich bis jetzt mein GUI-Window, den Fortschrittswert, die Progressbar und zum Test noch eine Checkbox. Und obwohl ich die Progressbar und die Checkbox in der Hashtabelle habe, kann ich deren Werte nicht abfragen. Einzig auf den Fortschrittswert habe ich Zugriff und kann ihn auslesen. Verändern darf ich aber auch diesen nicht! An diesem Punkt kommen mir Zweifel. Mit Hilfe der Hashtabelle soll es doch möglich sein Werte aus mehreren Thread lesen und bearbeiten zu können. Warum funktioniert das dann nicht?

#Folgende Zeilen stehen global im Skript und nicht verschachtelt
$script:syncHash = [hashtable]::Synchronized(@{})
$script:newRunspace =[runspacefactory]::CreateRunspace()
$script:newRunspace.ApartmentState = "STA"  
$script:newRunspace.ThreadOptions = "ReuseThread"           
$script:newRunspace.Open()
$script:newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)       

# Funktion zum Errechnen des Fortschritt in der Statusbar
Function Statusbar{
    $ChB_checkedCount = 0
    $ChB_disabledCount = 0      
           
            if($ChB_1.IsChecked -eq $true){
               $ChB_checkedCount = $ChB_checkedCount +1
               if($ChB_1.IsEnabled -eq $false){
                   $ChB_disabledCount = $ChB_disabledCount +1
                    }
                }
            if($ChB_2.IsChecked -eq $true){
               $ChB_checkedCount = $ChB_checkedCount +1
               if($ChB_2.IsEnabled -eq $false){
                    $ChB_disabledCount = $ChB_disabledCount +1
                    }
                }
               $ProgressBase = 100 / $ChB_checkedCount
    [int]$Progress = $ProgressBase * $ChB_disabledCount
    write-host $Progress
    $script:syncHash.progress = $PB_Status #PB_Status ist meine Progressbar
    $script:syncHash.value = $Progress
    $psCmd = [PowerShell]::Create().AddScript({   
        $script:syncHash.progress.Value = $script:syncHash.value
        [System.Windows.Forms.MessageBox]::Show($script:syncHash.value,"Fortschritt",0,[System.Windows.Forms.MessageBoxIcon]::Information)  
    
    })
    $psCmd.Runspace = $script:newRunspace
    $data = $psCmd.BeginInvoke()
}
Sorry wenn ich damit nerve, aber ich möchte es nur verstehen.

Gruß
derhoeppi
Mitglied: 122990
122990 19.10.2015 aktualisiert um 09:18:34 Uhr
Goto Top
Da sind wieder einige Dinge falsch, erstens brauchst du $syncHash.value nicht da du die ganze Progressbar schon einer Hashtable-Property zugewiesen hast, und du den Progressvalue Wert damit live verändern kannst !!!
Zweitens greifst du falsch auf die Hashtable im SubThread zu ($script:syncHash), das ist nicht korrekt weil hiermit
$script:newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
eine extra Variable, welche ebenfalls "syncHash" heißt, für den Zugriff im SubThread erzeugt wird.
Deswegen musst du auch über diese aus dem SubThread drauf zugreifen. $syncHash.progress.value = XX

Der zweite Thread ist ja dazu da das wir im ersten nicht mit einer Schleife warten müssen, denn das würde die GUI ja unresponsive machen!

Das es einwandfrei klappt siehst du ja an @colinardo 's Beispiel, du setzt es nur nicht richtig um.

Und das mit der GUI im SubThread, vergess es, als Anfänger ist das zu komplex. Dazu solltest du erst mal mit c# oder Vb.net Threading-Programme programmieren und üben, damit du das Threading-Konzept erst mal grundlegend verstehst, sonst wird das nie was.
Mitglied: derhoeppi
derhoeppi 19.10.2015 um 10:26:51 Uhr
Goto Top
Zitat von @122990:

Da sind wieder einige Dinge falsch, erstens brauchst du $syncHash.value nicht da du die ganze Progressbar schon einer Hashtable-Property zugewiesen hast, und du den Progressvalue Wert damit live verändern kannst !!!
...
Und das mit der GUI im SubThread, vergess es, als Anfänger ist das zu komplex. Dazu solltest du erst mal mit c# oder Vb.net Threading-Programme programmieren und üben, damit du das Threading-Konzept erst mal grundlegend verstehst, sonst wird das nie was.

Hallo Grexit,

die GUI im SubThread habe ich eigentlich nur zum Verständnis erzeugt. Als das funktionierte, habe ich mich gefreut überhaupt etwas aus einem anderen Thread an der GUI zu verändern. Die GUI im SubThread zu belassen ist nicht meine Intention, aber dadurch das ich von außen auf den Thread zugreifen kann, ist das Troubleshooting "einfacher".
Den Part mit $syncHash.value muss ich leider aufgreifen, weil ich das Gefühl habe, dass ich Dich entweder komplett falsch verstehe oder wir aneinander vorbei reden. $syncHash.value beinhaltet einen Integer Wert, den ich der Progressbar als Value zuweisen möchte. $syncHash.value berechne ich aus den Checkboxen.

Um wenigstens eine einfache Funktion sicherzustellen, habe ich nun in meiner Skriptanweisung folgendes zu stehen: $syncHash.progress.value = 12
Leider passiert in der GUI nichts. Wenn ich jedoch den Runspace richtig initialisiert habe, und den Runspace mit dem Skript aufrufe - warum passiert nichts?

Gruß
derhoeppi
Mitglied: 122990
122990 19.10.2015 aktualisiert um 10:47:22 Uhr
Goto Top
Zitat von @derhoeppi:
$syncHash.value beinhaltet einen Integer Wert, den ich der Progressbar als Value zuweisen möchte.
Eben das geht nicht, weil das nicht automatisch der Progressbar zugewiesen wird, im Gegensatz zu dem ganzen Progressbar-Objekt mit welchem sich automatisch der Wert der Progressbar setzen lässt

Um wenigstens eine einfache Funktion sicherzustellen, habe ich nun in meiner Skriptanweisung folgendes zu stehen: $syncHash.progress.value = 12
Leider passiert in der GUI nichts. Wenn ich jedoch den Runspace richtig initialisiert habe, und den Runspace mit dem Skript aufrufe - warum passiert nichts?

Läuft denn das Beispiel von Colinardo bei dir überhaupt ? Geht hier einwandfrei ..
Mitglied: derhoeppi
derhoeppi 19.10.2015 um 11:38:23 Uhr
Goto Top
Hallo,

das Beispiel (im Beitrag "Powershell progressbar AND multitask") mit dem Timer von Uwe läuft bei mir einwandfrei. In dem Zuge wird aber nicht mit Runspaces gearbeitet, so dass es schwer fällt dieses Beispiel in meinen Fall zu übertragen. Der Timer hört in Uwes Skript nur beim Betätigen des Buttons auf zu laufen. Bei mir soll das Skript einfach durchlaufen und nach Abschluss einer Funktion die Progressbar aktualisieren.

Nun nochmal zum $syncHash.value. Ich nehme sowohl diesen Wert als auch die Progressbar unabhängig voneinander über die Hashtabelle mit in den SubThread. Dort nehme ich dann die Zuweisung manuell vor. Das muss doch gehen oder? Es ist doch letztlich nichts anderes als eine Variablenzuweisung.

Gruß
derhoeppi
Mitglied: 122990
122990 19.10.2015 aktualisiert um 11:42:33 Uhr
Goto Top
Ich meinte den zweiten Link
Powershell GUI bleibt während einer While schleife hängen...
Dort werden sehr wohl RunSpaces eingesetzt!
Das muss doch gehen oder?
Klar.
Nur wird eben der Wert für die Progressbar nur "automatisch" von der GUI übernommen wenn du es über das Progressbar-Objekt machst.
Mitglied: derhoeppi
derhoeppi 19.10.2015 um 12:47:35 Uhr
Goto Top
Hallo grexit,
bevor ich mir das zweite Beispiel noch einmal genauer ansehe. Ich habe jetzt in dem $psCmd Script eine For-Schleife eingebaut, die von 1 bis 100 zählt und diesen Wert an die Statusbar weitergibt. Leider verändert sich in meiner Progressbar gar nichts. Ich muss also ein Problem beim Ansprechen der Progressbar haben.

Gruß
derhoeppi
Mitglied: derhoeppi
derhoeppi 19.10.2015 aktualisiert um 20:42:21 Uhr
Goto Top
Hi,

ich habe nun noch einmal von 0 angefangen. Dazu habe ich mir eine XAML Datei erstellt, die mein Frontend enthält. Diese spreche ich aus der ps1 Datei an. Hier mein Code:

Code der ps1
 #Dll einbinden 

	Add-Type -AssemblyName PresentationFramework
	Add-Type -AssemblyName System
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

# Einbinden der Windows Form XAML Datei
	[XML]$XAML = Get-Content -Path "C:\Scripts\Statusbar\MainWindow.xaml"  
	$XAML.Window.RemoveAttribute("x:Class")  
	$Reader = New-Object System.Xml.XmlNodeReader $XAML
	$Form = [Windows.Markup.XAMLReader]::Load($Reader)

$Btn_Close_Click = ({Beenden})
$Btn_Start_Click = ({Main})

#Create synchronized Hashtable-Object for communication between sub-thread and form
$syncHash = [hashtable]::Synchronized(@{})
# initialize second Powershell runspace
$psCmd = [powershell]::Create()

Function GenerateForm {
    $Btn_Close = $Form.FindName('Btn_Close')  
    $Btn_Start = $Form.FindName('Btn_Start')  
	$ChB_Test = $Form.FindName('ChB_Test')  
	$PB_Status = $Form.FindName('PB_Status')  
    $Btn_Close.Add_Click($Btn_Close_Click)
	$Btn_Start.Add_Click($Btn_Start_Click)
    $PB_Status.Value = 0
	#set the controls in the hashtable we wish to manipulate
    $syncHash.Status = $PB_Status


    $async = $Form.Dispatcher.InvokeAsync({$Form.ShowDialog() | Out-Null})
    $async.Wait() | Out-Null
    }

Function Beenden{
$Form.Close()
}

Function Main{
    # create new runspace to run our sub-thread in
    
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"  
    $newRunspace.ThreadOptions = "ReuseThread"            
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)  

    $psCmd.AddScript({
                          # in this expample we set the progress-bar value every 500 ms
                          for ($i = 1;$i -le 10;$i++){
                              sleep -Milliseconds 500
                              $syncHash.Status.Value = $i * 10
                              [System.Windows.Forms.MessageBox]::Show($i,"Aufruf Deploy Skript",0,[System.Windows.Forms.MessageBoxIcon]::Information)  
                          }
                     })
    $psCmd.Runspace = $newRunspace
    #start the runspace
    $data = $psCmd.BeginInvoke()

}

GenerateForm

Code der XAML
<Window x:Name="Form" x:Class="Multithreading.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="MainWindow" Height="180" Width="525">  
    <Grid>
        <Button x:Name="Btn_Start" Content="Start" Margin="340,113,102,0" VerticalAlignment="Top"/>  
        <ProgressBar x:Name="PB_Status" HorizontalAlignment="Left" Height="30" Margin="64,43,0,0" VerticalAlignment="Top" Width="219"/>  
        <CheckBox x:Name="ChB_Test" Content="CheckBox" HorizontalAlignment="Left" Margin="340,71,0,0" VerticalAlignment="Top"/>  
        <Button x:Name="Btn_Close" Content="Beenden" HorizontalAlignment="Left" Margin="432,113,0,0" VerticalAlignment="Top" Width="75"/>  

    </Grid>
</Window>

Nachdem ich diese beiden Dateien erzeugt habe, dachte ich wird es funktionieren. Ich habe lediglich die XAML Datei erzeugt und anschließend den Code aus Uwe's Beispiel eingefügt. Die Progressbar wird aber nicht angesprochen, obwohl ich sie im $syncHash nach der Skriptlauzeit finde. Es scheint mir so als hätte ich ein trivaleres Problem. Den SubThread arbeitet er zwar ab, aber er kann aus dem SubThread nicht meine Progressbar ansprechen.

Gruß
derhoeppi
Mitglied: 122990
Lösung 122990 19.10.2015 aktualisiert um 21:15:54 Uhr
Goto Top
Ist ja auch ein Unterschied zwischen Tag und Nacht @colinardo verwendet im Beispiel Windows Forms nicht WFP, das sind zwei ganz unterschiedliche Schuhe ...
Zu WPf müsste ich erst nochmal nachsehen wie dort die Progressbar angesprochen werden muss.
Mitglied: derhoeppi
derhoeppi 19.10.2015 um 21:15:46 Uhr
Goto Top
Hallo grexit,

so ich habe nun die Lösung face-smile . Für das ansprechen der WPF Elemente muss ich mit der Methode Invoke() arbeiten. Als Fazit sieht meine for-Schleife nun so aus:

for ($i = 1;$i -le 10;$i++){
                              sleep -Milliseconds 500
                              #$syncHash.Status.Value = 10
                              $syncHash.Status.Dispatcher.Invoke("Normal", [action]{  
        $syncHash.Status.Value=$i *10})
}

So und nun kann ich endlich weitermachen. face-smile Danke für den Hinweis das ich wieder einmal nicht nach WPF Beispielen, sondern Windows Forms gesehen habe.

Gruß
derhoeppi
Mitglied: derhoeppi
derhoeppi 20.10.2015 um 10:02:25 Uhr
Goto Top
Hallo,
ich muss das Thema noch einmal hochholen. Nachdem ich gestern einen Aha Effekt hatte und mich über den Fortschritt gefreut habe, habe ich heute morgen versucht, den Code in mein bestehendes Skript zu integrieren. Daran bin ich gescheitert und bin deshalb zu dem obigen Beispiel zurückgekehrt. Ich habe das Beispiel um eine Funktion erweitert.
Folgendes wollte ich erreichen. Beim Klick auf den Button Start, soll die Funktion Main aufgerufen werden. In der Funktion Main gibt es eine For-Schleife, die drei Mal durchlaufen soll. In der For-Schleife wird die Funktion Statusbar aufgerufen, die zur Aufgabe hat, die Progressbar von 0-100% zu füllen.
Bei zwei von drei Durchläufen erhalte ich folgende Fehlermeldungen:
1. Exception calling "AddScript" with "1" argument(s): "The state of the current PowerShell instance is not valid for this operation."
2. Exception setting "Runspace": "The state of the current PowerShell instance is not valid for this operation."
3. Exception calling "BeginInvoke" with "0" argument(s): "The operation cannot be performed because a command has already been started. Wait for the command to complete, or stop it, and then try the operation again."

Derzeitiger Code:
#Dll einbinden 

	Add-Type -AssemblyName PresentationFramework
	Add-Type -AssemblyName System
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

# Einbinden der Windows Form XAML Datei
	[XML]$XAML = Get-Content -Path "C:\Scripts\Statusbar\MainWindow2.xaml"  
	$XAML.Window.RemoveAttribute("x:Class")  
	$Reader = New-Object System.Xml.XmlNodeReader $XAML
	$Form = [Windows.Markup.XAMLReader]::Load($Reader)

$Btn_Close_Click = ({Beenden})
$Btn_Start_Click = ({Main})

#Create synchronized Hashtable-Object for communication between sub-thread and form
$syncHash = [hashtable]::Synchronized(@{})
#set the controls in the hashtable we wish to manipulate
    
# initialize second Powershell runspace
$psCmd = [powershell]::Create()

Function GenerateForm {
    $Btn_Close = $Form.FindName('Btn_Close')  
    $Btn_Start = $Form.FindName('Btn_Start')  
	$ChB_Test = $Form.FindName('ChB_test')  
	$PB_Status = $Form.FindName('PB_Status')  
    $Btn_Close.Add_Click($Btn_Close_Click)
	$Btn_Start.Add_Click($Btn_Start_Click)
    $PB_Status.Value = 0
    $syncHash.Status = $PB_Status


    $async = $Form.Dispatcher.InvokeAsync({$Form.ShowDialog() | Out-Null})
    $async.Wait() | Out-Null
    }

Function Beenden{
$Form.Close()
}

Function Statusbar{
    # create new runspace to run our sub-thread in
    
    $newRunspace =[runspacefactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"  
    $newRunspace.ThreadOptions = "ReuseThread"            
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)  

    $psCmd.AddScript({
                          for ($i = 1;$i -le 10;$i++){
                              sleep -Milliseconds 100
                              $syncHash.Status.Dispatcher.Invoke([action]{$syncHash.Status.Value= $i * 10},"Normal")  
                          }
                     })
    $psCmd.Runspace = $newRunspace
    #start the runspace
    $data = $psCmd.BeginInvoke()
    sleep -Seconds 5
}

Function Main{
    for ($k = 1;$k -le 3;$k++){
    Statusbar
    sleep -Seconds 5
    $newRunspace.Dispose()
    }
}

GenerateForm
$newRunspace.Close()

Woran könnte mein Problem liegen. Aufgrund der Fehlermeldung 3 habe ich die Zeit in Vermutung. Aber selbst Ruhezeiten habe es nicht verbessert.

Gruß
derhoeppi