none
FileSystemWatcher kommt nicht mit Form zurecht RRS feed

  • Frage

  • Hallo,

    ich habe in meinem Programm eine GUI programmiert und außerdem den FileSystemWatcher integriert, der ein Verzeichnis überwachen soll. Hier das ganze mal auf ein Minimalbeispiel runtergebrochen:

    [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
    [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null

    $Form = New-Object System.Windows.Forms.Form

    $F = New-Object System.IO.FileSystemWatcher
    $F.Path = "F:\Test"
    $F.EnableRaisingEvents = $true

    Register-ObjectEvent -InputObject  $F -EventName Created -Action{write-host new file created}

    $Form.ShowDialog()| Out-Null

    Das Problem bei der ganzen Sache ist, dass die Aktion, die ausgeführt werden soll, wenn eine neue Datei im Verzeichnis entsteht, erst dann ausgeführt wird, wenn ich die Form schließe, d.h. erst dann kommt in der Konsole die Ausgabe "new file created".

    Was kann ich tun, damit diese Ausgabe bereits während die GUI noch vorhanden ist gemacht wird?

    Mittwoch, 9. April 2014 01:57

Antworten

  • @Kamil die Methode Show() Funktioniert in PowerShell nicht, da PowerShell keine Nachrichtenschleife hat. Nur die Methode ShowDialog() erzeugt eine Nachrichtenschleife.

    Hier müssen 2  (oder mehrere) Dinge gleichzeitig von PowerShell abgearbeitet werden.

    PowerShell arbeitet immer seriell und synchron

    PowerShell arbeitet immer eine Aufgabe nach der anderen ab. Dies nennt man auch serielle Abarbeitung. Der Vorteil bei dieser Arbeitsweise ist, dass die Daten im gleichen Takt erstellt und abgeholt werden können dies ist die synchrone Abarbeitung.

    Während die PowerShell lang andauernde Aufträge abarbeitet, kann Sie nicht mehr auf eingaben reagieren.
    Die PowerShell ist blockiert.
    Ebenso geht es einer Grafischen Oberfläche (GUI). Während die GUI im Hintergrund etwas bearbeitet, kann sie nicht auf Tastatur oder Mausereignisse reagieren.
    Die GUI scheint eingefroren zu sein und ist ebenfalls blockiert.

    PowerShell kann parallel und asynchron arbeiten

    PowerShell bietet zum Parallelen (gleichzeitigen) und asynchronen abarbeiten von mehreren Aufgaben gleichzeitig 2 verschiedene Techniken:

    PowerShell Jobs
    PowerShell Runspaces

    PowerShell Jobs nutzen mehrere Prozesse Multiprocessing oder auch Multitasking genannt.
    PowerShell Runspaces nutzen Threads die Innerhalb eines einzigen Prozesses ablaufen, Multithreading genannt.

    Ich glaube mit den PowerShell Jobs kommst du nicht dahin wo du hin willst.

    Du brauchst Multithreading

    Der Begriff Multithreading (auch Nebenläufigkeit, Mehrsträngigkeit oder Mehrfädigkeit genannt) bezeichnet
    die Aufteilung eines Programmes in mehrere gleichzeitig abzuarbeitende Bearbeitungsstränge (Threads) innerhalb eines einzelnen Prozesses oder eines Tasks (ein Anwendungsprogramm).

    Prozess
    Ein Prozess ist eine ausführende Instanz einer Anwendung. Wenn man z. B. PowerShell.exe ausführt, dann startet man einen Prozess, der die Windows PowerShell ausführt.
    Ein Prozess bekommt von dem Betriebssystem eine Umgebung in dem der Prozess ablaufen kann.  Der Prozess bekommt seinen eigenen Speicherplatz, seinen eigenen Stack (Speicher-Stapel um Variablen abzulegen) und einen Satz von Windows-Umgebungsvariablen.

    Thread
    Beim Start eines Prozesse wird nur ein einziger (primärer) Thread (Handlungsfaden) erzeugt der alle Befehle nacheinander abarbeitet.

    Der Prozess erzeugt und Verwaltet die Umgebung, in der ein oder mehrere Threads arbeiten können.

    Da der PowerShell Prozess nur einen Thread hat erzeugt dies eine streng sequentiellen (serielle) Programmausführung.

    Man kann sich das Vorstellen wie eine Einbahnstraße mit Überholverbot. Alle Fahrzeuge müssen dem ersten Fahrzeug folgen und können nicht an dem ersten Fahrzeug vorbei.

    Bleibt das erste Fahrzeug stehen ist die Straße (der Thread) blockiert!

    Die Windows Form wird mit  der Methode  ShowDialog() gestartet diese Methode Zeigt die Form an und wartet auf Eingaben des Benutzers. Dies Blockiert den Thread!

    Bei Bedarf können Anwendung zusätzliche Threads erstellen.
    Möchte man Überholen oder mehrere Sachen gleichzeitig (Parallel) ablaufen lassen, benötigt man mehrere Straßen Spuren nebeneinander. Bei Multithreading erzeugt man neben dem primären Thread noch zusätzliche Threads (Handlungsfäden) die nebeneinander parallel Arbeiten können.

    Vorteile von Threads:

    Durchführen zeitaufwendiger Operationen in einem parallelen Ausführungsstrang.
    Gute Skalierung auf Mehrkern-Prozessor-Systemen. Jeder Thread läuft auf einem Prozessorkern und nutzt die vorhandenen Ressourcen effizient aus.
    Nicht blockierende Kommunikation über ein Netzwerk, mit einem Webserver und mit einer Datenbank.
    Unterscheiden von Aufgaben nach unterschiedlicher Priorität. Ein Thread mit hoher Priorität übernimmt beispielsweise Aufgaben mit hoher Dringlichkeitsstufe, während ein Thread mit niedriger Priorität andere Aufgaben ausführt.
    Aufrechterhalten der Benutzeroberflächen-Reaktivität, während Hintergrundaufgaben bearbeitet werden.

    Nachteile von Threads:

    Nachdem ein Threads gestartet wurde, ist er mit Arbeit beschäftig und man kann mit dem Thread nur sehr schwer kommunizieren um den Thread von außen zu steuern.
    Das Steuern der Codeausführung mit vielen Threads ist sehr komplex und kann zusätzliche Fehler verursachen.

    Ebenso kann es vorkommen, dass mehrere Threads im gleichen Moment eine Ressource (Variable) benötigen. Hier muss geregelt werden wann welcher Thread die Ressource haben kann.
    Mehrere Threads müssen synchronisiert werden und die Fehlersuche (Debuging) gestaltet sich sehr schwierig.

    Eine große Anzahl von Threads nimmt einen beträchtlichen Teil der CPU-Zeit in Anspruch. Bei zu vielen Threads kann die Ausführung der meisten nur ungenügend vorankommen. Das Betriebssystem muss sehr oft zwischen den Threads hin- und herschalten, was zu mehr Overhead führt. Wenn sich die meisten der aktuellen Threads in einem Prozess befinden, werden Threads in anderen Prozessen weniger häufig in den Ablaufplan aufgenommen.
    Das Betriebssystem nimmt Arbeitsspeicher für die Kontextinformationen in Anspruch, die für Prozesse, AppDomain-Objekte und Threads erforderlich sind. Deshalb ist die Anzahl der Prozesse, AppDomain-Objekte und Threads, die erstellt werden können, durch den verfügbaren Arbeitsspeicher begrenzt.
    Das Löschen von Threads erfordert Kenntnisse über mögliche Auswirkungen auf das Programm und deren Behandlung

    -----------------------------------------------------------------------------------------------------------------------------------

    Lange Rede Kurzer Sinn:
    Das ist nicht einfach  ;-)

    Du musst die Grafische Benutzeroberfläche in einem Thread Starten. Und den Filesystemwatcher in einem anderen Thread.
    Dann musst du es noch hinbekommen das diese beiden Treads miteinander kommunizieren.

    Hier ein paar Links für dich zum Start:

    Thread = Runspace !

    In der PowerShell wird ein Thread von einem so genannten Runspace zur Verfügung gestellt.

    Multithreading (Asynchronität) in der PowerShell:

    Mann muss mit Runspaces (Sessions) und der Methode Create [PowerShell]::Create Arbeiten.
    http://msdn.microsoft.com/en-us/library/system.management.automation.powershell_members%28v=vs.85%29.aspx

    Ein sehr gutes Video Tutorial gibt es von Dr. Tobias Weltner
    How to speed up Windows PowerShell using multithreading
    http://www.youtube.com/watch?v=hJwhyVXiOLg

    gute Blog Post:

    Concurrency in PowerShell: Multi-threading with Runspaces
    http://newsqlblog.com/2012/05/22/concurrency-in-powershell-multi-threading-with-runspaces/

    zum „übergeben“ von Daten kann man eine Synchronisierte Hashtable nehmen

    Asynchronicity in PowerShell
    http://cjoprey.blog.com/2009/08/17/asynchronicity-in-powershell/

    PowerShell and WPF: Writing Data to a UI From a Different Runspace
    http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/

    Beispiel Code von Bruce Payette [MSFT]:

    <#
        HowTo: Create Windows Form asynchronous without Stopping the PowerShell Script from Processing
        Link: http://www.vistax64.com/powershell/16998-howto-create-windows-form-without-stopping-script-processing.html
        Author: Bruce Payette [MSFT]  
    #>
    
    
    # Zuerst wird die Windows Form erstellt
    #########################################################
    # hier wird eine einfache Form mit einer Textbox erstellt
    # in der Textbox soll Text angezeigt werden
    # Die Form wird noch nicht gestartet!
    #
    $dForm = New-Object System.Windows.Forms.Form
    $dForm.Size = new-object System.Drawing.Size 640,480
    $dForm.Text = "Debug Information"
    
    # TextBox erstellen die die ganze Form ausfüllt
    $dTextBox = New-Object System.Windows.Forms.TextBox
    $dTextBox.ScrollBars = "both"
    $dTextBox.Dock = "fill"
    $dTextBox.Multiline = $true
    $dTextBox.Parent = $dForm
    $dTextBox.ReadOnly = $true
    
    # im ereignis Shown der Form wird die Form Aktiv gesetzt
    $dForm.Add_Shown({$dform.Activate()})
    
    
    # Der PowerShell Runspace (Thread) wird erstellt
    #########################################################
    # In diesem Runspace-Thread soll die Windows Forms GUI laufen
    #
    $Runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
    
    # öffnen des Runspaces
    $Runspace.Open()
    
    # Die Windows Form wird als Variable in die Umgebung des Runspace-Thread erstellt und übergeben
    # damit die Form auch in dem neuen Runspace-Thread vorhanden ist
    # Im neuen Runspace-Thread bekommt die Variable den selben Namen wie in diesem Runspace-Thread
    $Runspace.SessionStateProxy.SetVariable("dForm", $dForm)
    
    # Eine synchronisierte hashtable wird erstellt,
    # um Daten zwischen den Threads austauschen können
    $SynchronizedHash = [hashtable]::Synchronized(@{text=""})
    
    # die synchronisierte hashtable wird in den Runspace-Thread erstellt und übergeben
    # Im neuen Runspace-Thread bekommt die Variable den selben Namen wie in diesem Runspace-Thread
    $Runspace.SessionStateProxy.SetVariable("SynchronizedHash", $SynchronizedHash)
    
    # Eine PowerShell Pipeline wird erstellt in der die Windows Form gestartet werden soll 
    $RunspacePipeline = $Runspace.CreatePipeline({ [void] $dForm.ShowDialog()})
    # von der Pipeline wird der Inputstream geschlossen, sonnst würde dieser die Pipeline blockieren
    $RunspacePipeline.Input.Close()
    
    
    # Die Windows Form wird Asynchron (parallel) angezeigt
    #########################################################
    # hier wird die Pipeline Asynchron (parallel) gesartet
    # erst jetzt wird die Form mit ShowDialog() angezeigt
    $RunspacePipeline.InvokeAsync()
    
    
    
    # Hilfs-Funktion zum übergeben von beliebigen Text an die Form
    ##############################################################
    # Funktion die immer wieder aufgerufen werden kann, 
    # um Text an die Textbox in der Windows Form zu senden
    Function Add-TextToWindow {
    
        param(
            [Parameter(ValueFromPipeline=$True)]
            [String]$text
        )
    
        process {
    
            # Der Text der dieser Funktion übergeben wurde wird
            # in die synchronisierte hashtable geschrieben
            $SynchronizedHash.text = $text + "`n"
    
            # einen Eventhandler erstellen der später für die Invoke Methode benötigt wird
            # $This ist die Textbox
            # Dieser Eventhandler holt Text aus der synchronisierte hashtable und
            # ruft die Methode AppendText() von der Textbox auf
            [EventHandler] $EventHandler = {$this.AppendText($SynchronizedHash.text)}
    
            # Es könnte sein das die Windows Form noch nicht bereit ist
            # hier wird dieser Fehler abgefangen und darauf gewartet das die Form bereit ist
            do {
                $retry = @($false)
    
                trap [InvalidOperationException] {
                    Start-Sleep -milli 100
                    $retry[0] = $true
                    continue
                }
    
                # Steuerelemente in Windows Forms sind an einen bestimmten Thread gebunden und nicht threadsicher.
                # Abgesehen von der InvokeRequired-Eigenschaft können vier Methoden in einem Steuerelement threadsicher aufgerufen werden:
                # Invoke, BeginInvoke, EndInvoke und CreateGraphics, wenn das Steuerelement (das Handle) bereits erstellt wurde. 
                $dTextBox.Invoke($EventHandler, ($dTextBox, [eventargs]::empty))
    
            } while ($retry[0])
    
        }
    
    }
    
    # PowerShell kann hier nun Arbeiten, während die Windows Form angezeigt wird
    ############################################################################
    #
    # mit der Function Add-TextToWindow kann nun Text zum Fenster gesendet werden
    
    Add-TextToWindow "The date is $(Get-Date)"
    1..10 | ForEach-Object {
        Add-TextToWindow "Some text $_"
        start-sleep -milli 100
    }
    
    Get-ChildItem $env:windir | Select-Object -ExpandProperty Fullname | Add-TextToWindow
    
    Add-TextToWindow "The date is $(Det-Date)"
    Start-Sleep 2
    
    
    
    # Windows Forms GUI wieder entfernen (aufräumen ist wichtig ;-) )
    ################################################################
    #
    # Die Windows Form Schliessen
    $dForm.Close()
    # den Runspace-Thread schliessen 
    $Runspace.Close()
    # nun hat PowerShell wieder nur einen Thread!


    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Mittwoch, 9. April 2014 07:18

Alle Antworten

  • Hi,

    versuch es mal mit der Methode .Show() und nicht mit .ShowDialog()

    eine super Erklärung dazu findest du hier: http://msdn.microsoft.com/en-US/library/39wcs2dh%28VS.80%29.aspx

    Gruß

    Kamil

    Mittwoch, 9. April 2014 06:33
  • @Kamil die Methode Show() Funktioniert in PowerShell nicht, da PowerShell keine Nachrichtenschleife hat. Nur die Methode ShowDialog() erzeugt eine Nachrichtenschleife.

    Hier müssen 2  (oder mehrere) Dinge gleichzeitig von PowerShell abgearbeitet werden.

    PowerShell arbeitet immer seriell und synchron

    PowerShell arbeitet immer eine Aufgabe nach der anderen ab. Dies nennt man auch serielle Abarbeitung. Der Vorteil bei dieser Arbeitsweise ist, dass die Daten im gleichen Takt erstellt und abgeholt werden können dies ist die synchrone Abarbeitung.

    Während die PowerShell lang andauernde Aufträge abarbeitet, kann Sie nicht mehr auf eingaben reagieren.
    Die PowerShell ist blockiert.
    Ebenso geht es einer Grafischen Oberfläche (GUI). Während die GUI im Hintergrund etwas bearbeitet, kann sie nicht auf Tastatur oder Mausereignisse reagieren.
    Die GUI scheint eingefroren zu sein und ist ebenfalls blockiert.

    PowerShell kann parallel und asynchron arbeiten

    PowerShell bietet zum Parallelen (gleichzeitigen) und asynchronen abarbeiten von mehreren Aufgaben gleichzeitig 2 verschiedene Techniken:

    PowerShell Jobs
    PowerShell Runspaces

    PowerShell Jobs nutzen mehrere Prozesse Multiprocessing oder auch Multitasking genannt.
    PowerShell Runspaces nutzen Threads die Innerhalb eines einzigen Prozesses ablaufen, Multithreading genannt.

    Ich glaube mit den PowerShell Jobs kommst du nicht dahin wo du hin willst.

    Du brauchst Multithreading

    Der Begriff Multithreading (auch Nebenläufigkeit, Mehrsträngigkeit oder Mehrfädigkeit genannt) bezeichnet
    die Aufteilung eines Programmes in mehrere gleichzeitig abzuarbeitende Bearbeitungsstränge (Threads) innerhalb eines einzelnen Prozesses oder eines Tasks (ein Anwendungsprogramm).

    Prozess
    Ein Prozess ist eine ausführende Instanz einer Anwendung. Wenn man z. B. PowerShell.exe ausführt, dann startet man einen Prozess, der die Windows PowerShell ausführt.
    Ein Prozess bekommt von dem Betriebssystem eine Umgebung in dem der Prozess ablaufen kann.  Der Prozess bekommt seinen eigenen Speicherplatz, seinen eigenen Stack (Speicher-Stapel um Variablen abzulegen) und einen Satz von Windows-Umgebungsvariablen.

    Thread
    Beim Start eines Prozesse wird nur ein einziger (primärer) Thread (Handlungsfaden) erzeugt der alle Befehle nacheinander abarbeitet.

    Der Prozess erzeugt und Verwaltet die Umgebung, in der ein oder mehrere Threads arbeiten können.

    Da der PowerShell Prozess nur einen Thread hat erzeugt dies eine streng sequentiellen (serielle) Programmausführung.

    Man kann sich das Vorstellen wie eine Einbahnstraße mit Überholverbot. Alle Fahrzeuge müssen dem ersten Fahrzeug folgen und können nicht an dem ersten Fahrzeug vorbei.

    Bleibt das erste Fahrzeug stehen ist die Straße (der Thread) blockiert!

    Die Windows Form wird mit  der Methode  ShowDialog() gestartet diese Methode Zeigt die Form an und wartet auf Eingaben des Benutzers. Dies Blockiert den Thread!

    Bei Bedarf können Anwendung zusätzliche Threads erstellen.
    Möchte man Überholen oder mehrere Sachen gleichzeitig (Parallel) ablaufen lassen, benötigt man mehrere Straßen Spuren nebeneinander. Bei Multithreading erzeugt man neben dem primären Thread noch zusätzliche Threads (Handlungsfäden) die nebeneinander parallel Arbeiten können.

    Vorteile von Threads:

    Durchführen zeitaufwendiger Operationen in einem parallelen Ausführungsstrang.
    Gute Skalierung auf Mehrkern-Prozessor-Systemen. Jeder Thread läuft auf einem Prozessorkern und nutzt die vorhandenen Ressourcen effizient aus.
    Nicht blockierende Kommunikation über ein Netzwerk, mit einem Webserver und mit einer Datenbank.
    Unterscheiden von Aufgaben nach unterschiedlicher Priorität. Ein Thread mit hoher Priorität übernimmt beispielsweise Aufgaben mit hoher Dringlichkeitsstufe, während ein Thread mit niedriger Priorität andere Aufgaben ausführt.
    Aufrechterhalten der Benutzeroberflächen-Reaktivität, während Hintergrundaufgaben bearbeitet werden.

    Nachteile von Threads:

    Nachdem ein Threads gestartet wurde, ist er mit Arbeit beschäftig und man kann mit dem Thread nur sehr schwer kommunizieren um den Thread von außen zu steuern.
    Das Steuern der Codeausführung mit vielen Threads ist sehr komplex und kann zusätzliche Fehler verursachen.

    Ebenso kann es vorkommen, dass mehrere Threads im gleichen Moment eine Ressource (Variable) benötigen. Hier muss geregelt werden wann welcher Thread die Ressource haben kann.
    Mehrere Threads müssen synchronisiert werden und die Fehlersuche (Debuging) gestaltet sich sehr schwierig.

    Eine große Anzahl von Threads nimmt einen beträchtlichen Teil der CPU-Zeit in Anspruch. Bei zu vielen Threads kann die Ausführung der meisten nur ungenügend vorankommen. Das Betriebssystem muss sehr oft zwischen den Threads hin- und herschalten, was zu mehr Overhead führt. Wenn sich die meisten der aktuellen Threads in einem Prozess befinden, werden Threads in anderen Prozessen weniger häufig in den Ablaufplan aufgenommen.
    Das Betriebssystem nimmt Arbeitsspeicher für die Kontextinformationen in Anspruch, die für Prozesse, AppDomain-Objekte und Threads erforderlich sind. Deshalb ist die Anzahl der Prozesse, AppDomain-Objekte und Threads, die erstellt werden können, durch den verfügbaren Arbeitsspeicher begrenzt.
    Das Löschen von Threads erfordert Kenntnisse über mögliche Auswirkungen auf das Programm und deren Behandlung

    -----------------------------------------------------------------------------------------------------------------------------------

    Lange Rede Kurzer Sinn:
    Das ist nicht einfach  ;-)

    Du musst die Grafische Benutzeroberfläche in einem Thread Starten. Und den Filesystemwatcher in einem anderen Thread.
    Dann musst du es noch hinbekommen das diese beiden Treads miteinander kommunizieren.

    Hier ein paar Links für dich zum Start:

    Thread = Runspace !

    In der PowerShell wird ein Thread von einem so genannten Runspace zur Verfügung gestellt.

    Multithreading (Asynchronität) in der PowerShell:

    Mann muss mit Runspaces (Sessions) und der Methode Create [PowerShell]::Create Arbeiten.
    http://msdn.microsoft.com/en-us/library/system.management.automation.powershell_members%28v=vs.85%29.aspx

    Ein sehr gutes Video Tutorial gibt es von Dr. Tobias Weltner
    How to speed up Windows PowerShell using multithreading
    http://www.youtube.com/watch?v=hJwhyVXiOLg

    gute Blog Post:

    Concurrency in PowerShell: Multi-threading with Runspaces
    http://newsqlblog.com/2012/05/22/concurrency-in-powershell-multi-threading-with-runspaces/

    zum „übergeben“ von Daten kann man eine Synchronisierte Hashtable nehmen

    Asynchronicity in PowerShell
    http://cjoprey.blog.com/2009/08/17/asynchronicity-in-powershell/

    PowerShell and WPF: Writing Data to a UI From a Different Runspace
    http://learn-powershell.net/2012/10/14/powershell-and-wpf-writing-data-to-a-ui-from-a-different-runspace/

    Beispiel Code von Bruce Payette [MSFT]:

    <#
        HowTo: Create Windows Form asynchronous without Stopping the PowerShell Script from Processing
        Link: http://www.vistax64.com/powershell/16998-howto-create-windows-form-without-stopping-script-processing.html
        Author: Bruce Payette [MSFT]  
    #>
    
    
    # Zuerst wird die Windows Form erstellt
    #########################################################
    # hier wird eine einfache Form mit einer Textbox erstellt
    # in der Textbox soll Text angezeigt werden
    # Die Form wird noch nicht gestartet!
    #
    $dForm = New-Object System.Windows.Forms.Form
    $dForm.Size = new-object System.Drawing.Size 640,480
    $dForm.Text = "Debug Information"
    
    # TextBox erstellen die die ganze Form ausfüllt
    $dTextBox = New-Object System.Windows.Forms.TextBox
    $dTextBox.ScrollBars = "both"
    $dTextBox.Dock = "fill"
    $dTextBox.Multiline = $true
    $dTextBox.Parent = $dForm
    $dTextBox.ReadOnly = $true
    
    # im ereignis Shown der Form wird die Form Aktiv gesetzt
    $dForm.Add_Shown({$dform.Activate()})
    
    
    # Der PowerShell Runspace (Thread) wird erstellt
    #########################################################
    # In diesem Runspace-Thread soll die Windows Forms GUI laufen
    #
    $Runspace = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
    
    # öffnen des Runspaces
    $Runspace.Open()
    
    # Die Windows Form wird als Variable in die Umgebung des Runspace-Thread erstellt und übergeben
    # damit die Form auch in dem neuen Runspace-Thread vorhanden ist
    # Im neuen Runspace-Thread bekommt die Variable den selben Namen wie in diesem Runspace-Thread
    $Runspace.SessionStateProxy.SetVariable("dForm", $dForm)
    
    # Eine synchronisierte hashtable wird erstellt,
    # um Daten zwischen den Threads austauschen können
    $SynchronizedHash = [hashtable]::Synchronized(@{text=""})
    
    # die synchronisierte hashtable wird in den Runspace-Thread erstellt und übergeben
    # Im neuen Runspace-Thread bekommt die Variable den selben Namen wie in diesem Runspace-Thread
    $Runspace.SessionStateProxy.SetVariable("SynchronizedHash", $SynchronizedHash)
    
    # Eine PowerShell Pipeline wird erstellt in der die Windows Form gestartet werden soll 
    $RunspacePipeline = $Runspace.CreatePipeline({ [void] $dForm.ShowDialog()})
    # von der Pipeline wird der Inputstream geschlossen, sonnst würde dieser die Pipeline blockieren
    $RunspacePipeline.Input.Close()
    
    
    # Die Windows Form wird Asynchron (parallel) angezeigt
    #########################################################
    # hier wird die Pipeline Asynchron (parallel) gesartet
    # erst jetzt wird die Form mit ShowDialog() angezeigt
    $RunspacePipeline.InvokeAsync()
    
    
    
    # Hilfs-Funktion zum übergeben von beliebigen Text an die Form
    ##############################################################
    # Funktion die immer wieder aufgerufen werden kann, 
    # um Text an die Textbox in der Windows Form zu senden
    Function Add-TextToWindow {
    
        param(
            [Parameter(ValueFromPipeline=$True)]
            [String]$text
        )
    
        process {
    
            # Der Text der dieser Funktion übergeben wurde wird
            # in die synchronisierte hashtable geschrieben
            $SynchronizedHash.text = $text + "`n"
    
            # einen Eventhandler erstellen der später für die Invoke Methode benötigt wird
            # $This ist die Textbox
            # Dieser Eventhandler holt Text aus der synchronisierte hashtable und
            # ruft die Methode AppendText() von der Textbox auf
            [EventHandler] $EventHandler = {$this.AppendText($SynchronizedHash.text)}
    
            # Es könnte sein das die Windows Form noch nicht bereit ist
            # hier wird dieser Fehler abgefangen und darauf gewartet das die Form bereit ist
            do {
                $retry = @($false)
    
                trap [InvalidOperationException] {
                    Start-Sleep -milli 100
                    $retry[0] = $true
                    continue
                }
    
                # Steuerelemente in Windows Forms sind an einen bestimmten Thread gebunden und nicht threadsicher.
                # Abgesehen von der InvokeRequired-Eigenschaft können vier Methoden in einem Steuerelement threadsicher aufgerufen werden:
                # Invoke, BeginInvoke, EndInvoke und CreateGraphics, wenn das Steuerelement (das Handle) bereits erstellt wurde. 
                $dTextBox.Invoke($EventHandler, ($dTextBox, [eventargs]::empty))
    
            } while ($retry[0])
    
        }
    
    }
    
    # PowerShell kann hier nun Arbeiten, während die Windows Form angezeigt wird
    ############################################################################
    #
    # mit der Function Add-TextToWindow kann nun Text zum Fenster gesendet werden
    
    Add-TextToWindow "The date is $(Get-Date)"
    1..10 | ForEach-Object {
        Add-TextToWindow "Some text $_"
        start-sleep -milli 100
    }
    
    Get-ChildItem $env:windir | Select-Object -ExpandProperty Fullname | Add-TextToWindow
    
    Add-TextToWindow "The date is $(Det-Date)"
    Start-Sleep 2
    
    
    
    # Windows Forms GUI wieder entfernen (aufräumen ist wichtig ;-) )
    ################################################################
    #
    # Die Windows Form Schliessen
    $dForm.Close()
    # den Runspace-Thread schliessen 
    $Runspace.Close()
    # nun hat PowerShell wieder nur einen Thread!


    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Mittwoch, 9. April 2014 07:18