none
Powergui in Job auslagern? RRS feed

  • Frage

  • Hallo,

    folgender Sachverhalt: 

    - Eine Form mit eine Abfrage (Button: Ja/Nein) soll erzeugt werden.

    - Nach Bestätigung soll zunächst ein externes Programm ausgeführt werden 

    - Die Form soll weiterhin ausgeführt werden und einen Abbruch - Button enthalten der das externe Programm und

      die Powershell jederzeit beenden kann

    - Wenn das externe Programm beendet ist, soll das Script weiter ausgeführt werden:

      Der Inhalt eines Labels in der Form soll geändert und eine weitere Routine soll ausgeführt werden. 

    Nun mein Problem:

    Ich starte das externe Programm und anschließend einen Job der in bestimmten Abständen abfragt, ob das Programm noch läuft. Wenn das Programm beendet ist, wird der Job geschlossen. Über ein Register - EventObjekt wird nun der nächste Programmschritt ausgeführt. Allerdings funktioniert das nicht, da das Register - EventObjekt erst ausgeführt wird, wenn die erzeugte Form (Form.showdialog) geschlossen wird. 

    Kurzes Bsp.:

    $Erg=[reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
    $Form1=New-Object "System.Windows.Forms.Form"		
    $Form1.Text="Testfenster"
    $testjob = Start-Job { Start-Sleep -Seconds 2 }  
    $null = Register-ObjectEvent $testjob -EventName StateChanged -Action {
    	[Console]::Beep(1000,500)	
    }
    [void] $Form1.Showdialog() 

    Ich könnte nun die Form ebenfalls in einem Job erzeugen. Ein Abbruch - Button könnte beide Jobs (bzw. das ganze Programm beenden). Das Register - ObjectEvent würde im eigenen Job ausgeführt werden. Nun könnte ich mit Hilfe des Register - ObjectEvents den anderen Job (also die Form) beenden und anschließend neu erzeugen und so das Programm fortführen. Das scheint mir aber keine optimale Lösung zu sein.

    Gibt es z.B. eine Möglichkeit, einen Eventhandler zu erzeugen (wie z.B. ein Add-Click Event) der auf die Beendigung des Jobs reagiert bzw. die Möglichkeit, dass beide Jobs kommunizieren und so ein Event innerhalb der Form ausgelöst wird? Oder hat jemand eine Idee wie sich mein Vorhaben effizienter realisieren lässt? Steh gerade etwas auf dem Schlauch!

    (PS: ich bin Anfänger was die PowerShell Programmierung angeht) 

    Dienstag, 13. Mai 2014 12:36

Antworten

  • PowerShell  wurde NICHT dazu gedacht Grafische Benutzer Oberflächen (GUI) zu Programmieren. (Auch wenn man es machen kann)

    PowerShell Arbeitet immer einen Befehl nach dem anderen ab, dies ist streng sequentiell (seriell) oder auch synchron genannt.
    Wie eine Einbahnstraße mit Überholverbot.

    Wenn die PowerShell eine längerdauernde Aufgabe erledigt wie z.B. das durchsuchen der Festplatte, kann der User während dessen keine eingaben mehr in der PowerShell GUI tätigen. Die GUI ist aus Sicht des Benutzers „eingefroren“.
    Oder Umgekehrt das Script läuft nach dem Erzeugen eines Fensters mit $Form.OpenDialog() nicht weiter, weil die PowerShell in der GUI auf Benutzereingaben wartet.
    Erst nach dem schließen des Fensters läuft das Script weiter.

    Deshalb sollte man die GUI Entwicklung sehr sparsam einsetzen und nur einfache Oberflächen Programmieren.

    PowerShell kann in einem Prozess also immer nur eine Sache gleichzeitig erledigen (Single Thread).
    Deshalb kommt schnell das Thema Multiprozessing oder Multithreading auf den Tisch.

    Nun kann man die PowerShell Jobs nehmen um in mehreren PowerShell Prozessen gleichzeitig parallel zu  Arbeiten. Das ist Multiprozessing, weil jedes Mal Aufwändig ein neuer PowerShell.exe Prozess erzeugt wird.

    Oder man benutzt die schlankeren PowerShell Runspaces um echtes Multithreading zu erzeugen.
    Multithreading ist das „gleichzeitige“ abarbeiten von mehreren Aufgaben in einem einzigen (PowerShell) Prozess.
    Stabile Programme mit Multithreading zu entwickeln ist selbst für erfahrenen Programmierer keine leichte Aufgabe.

    Die Prozesse oder Threads (Runspaces) arbeiten nicht nur Parallel sondern sind völlig unabhängig von einander. Hier kann man nicht voraussagen wann eine Aufgabe fertig ist und wann nicht. Die Arbeit geschieht Asynchron!
    Deshalb muss sich selbst um die Synchronisierung (Abholung) der Ergebnisse kümmern.
    Ebenso kann jederzeit ein Fehler in einem Prozess/Tread auftreten, der auch behandelt werden muss sonnst wartet man eventuell ewig auf das Ergebnis.

    Ein Prozess oder ein Thread läuft in seiner eigenen Umgebung, die Prozesse oder Threads können sich nur sehr schwer gegenseitig Nachrichten / Daten schicken. Da der Prozess oder der Thread mit Arbeiten beschäftigt ist.

    Wenn sich Prozesse oder Threads Daten Teilen, muss der zugriff auf diese Daten Synchronisiert werden.
    Wenn z.B. Ein Prozess die Daten lesen will und ein anderer will die Daten gleichzeitig schreiben was dann?

    Hier muss der Programmierer / Scripter die „Unfallverhütung“ sprich Synchronisierung der zugriffe regeln!
    Dafür gibt es nur wenige Automatismen.
    (z.B. Bei PowerShell die Synchronized Hashtable)

    Wie bei einer Mehrspurige Straße (Autobahn), wo fröhlich überholt werden darf.
    Versuch mal auf einer Autobahn, etwas von einem Lastwagen auf den Anderen umzuladen. ;-)

    PowerShell wurde konzipiert um Administrative Aufgaben im Hintergrund OHNE Fenster meist remote zu erledigen. Automation eben. Dafür reicht ein Prozess meistens aus.

    Bei der GUI Programmierung muss man sich mit den Events beschäftigen die in der PowerShell fast keine Rolle spielen.
    Windows Forms oder WPF Programme haben eine schleife die die Events fast in Echtzeit behandelt, da diese auch Multithreading nutzen.
    Diese fehlt bei PowerShell.
    In der PowerShell werden Events erst behandelt, wenn der eine PowerShell Prozess gerade mal Zeit hat.
    Wann ein Event behandelt wird, ist also in der PowerShell nicht vorhersehbar!
    Das Eventsystem von PowerShell ist deshalb nur sehr schlecht geeignet um Events zu bearbeiten.

    GUI Programmierung erfordert also grundlegenden Kenntnisse in der.NET Programmierung im Multithreading UND PowerShell Kenntnisse.
    Aus diesem Grund gibt es wenige PowerShell Nutzer die  GUI Programmierung beherrschen (wollen oder müssen).
    Bei der GUI Programmierung muss man natürlich immer bei den C# .NET Entwicklern nachschauen da dies kein Gebiet der PowerShell ist!
    Man sollte auch bei der Internet Recherche mit dem Suchwort C# arbeiten, anstatt mit dem Suchwort PowerShell.

    Lange rede Kurzer Sinn.
    Du musst PowerShell Jobs oder Runspaces (Multithreading) benutzen und diese selbst Synchronisieren, wenn du PowerShell GUI benutzen willst

     

    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+

    Dienstag, 13. Mai 2014 13:32
  • Für alle die es interessiert, ich hab mich jetzt für folgenden Weg entschieden:

    #WINDOWS-FORM:
    $dForm = New-Object System.Windows.Forms.Form
    $dForm.Text = "Fenster"
    
    #TEXTBOX:
    $dText = New-Object System.Windows.Forms.TextBox
    $dText.Dock = [System.Windows.Forms.DockStyle]::fill
    $dText.Multiline = $true
    $dText.Parent = $dForm
    $dForm.Add_Shown({$dform.Activate()})
    
    #THREAD: Erzeugt einen Powershell Runspace (Instance von Powershell)
    $newrs = [RunspaceFactory]::CreateRunspace()
    #MTA = multi-threaded appartment, STA = single-threaded appartment
    $newrs.ApartmentState = "MTA"
    $newrs.Open()
    
    #VARIABLE: Erzeugt eine Variable mit dessen Hilfe der Runspace angesprochen werden kann:
    $newrs.SessionStateProxy.SetVariable("dForm", $dForm)
    
    #PIPLINE: Erzeugt eine Pipeline im neuen Runspace die die Form darstellt 
    # Anschließend wird der Inputstream geschlossen und die Pipeline asynchron gestartet
    $p = $newrs.CreatePipeline( { [void] $dForm.ShowDialog() } )
    $p.Input.Close()
    $p.InvokeAsync()
    
    #TEXT
    $dText.AppendText("Programm gestartet`n")
    
    #EXTERNES PROGRAMM STARTEN
    & "C:\Windows\System32\taskmgr.exe" 
    Start-Sleep -s 4
    do {
    $p = Get-Process taskmgr
    Start-Sleep -Seconds 3
    }
    while ($p -ne $Null)
    $dText.AppendText("Programm beendet`n")
    Start-Sleep -s 4
    
    #FENSTER & RUNSPACE BEENDEN
    $dForm.Close()
    $newrs.Close()
    Natürlich stellt es nur ein grobes Gerüst dar!

    • Als Antwort vorgeschlagen Peter Kriegel Donnerstag, 15. Mai 2014 05:06
    • Als Antwort markiert Alex Pitulice Montag, 19. Mai 2014 14:48
    Mittwoch, 14. Mai 2014 16:32

Alle Antworten

  • PowerShell  wurde NICHT dazu gedacht Grafische Benutzer Oberflächen (GUI) zu Programmieren. (Auch wenn man es machen kann)

    PowerShell Arbeitet immer einen Befehl nach dem anderen ab, dies ist streng sequentiell (seriell) oder auch synchron genannt.
    Wie eine Einbahnstraße mit Überholverbot.

    Wenn die PowerShell eine längerdauernde Aufgabe erledigt wie z.B. das durchsuchen der Festplatte, kann der User während dessen keine eingaben mehr in der PowerShell GUI tätigen. Die GUI ist aus Sicht des Benutzers „eingefroren“.
    Oder Umgekehrt das Script läuft nach dem Erzeugen eines Fensters mit $Form.OpenDialog() nicht weiter, weil die PowerShell in der GUI auf Benutzereingaben wartet.
    Erst nach dem schließen des Fensters läuft das Script weiter.

    Deshalb sollte man die GUI Entwicklung sehr sparsam einsetzen und nur einfache Oberflächen Programmieren.

    PowerShell kann in einem Prozess also immer nur eine Sache gleichzeitig erledigen (Single Thread).
    Deshalb kommt schnell das Thema Multiprozessing oder Multithreading auf den Tisch.

    Nun kann man die PowerShell Jobs nehmen um in mehreren PowerShell Prozessen gleichzeitig parallel zu  Arbeiten. Das ist Multiprozessing, weil jedes Mal Aufwändig ein neuer PowerShell.exe Prozess erzeugt wird.

    Oder man benutzt die schlankeren PowerShell Runspaces um echtes Multithreading zu erzeugen.
    Multithreading ist das „gleichzeitige“ abarbeiten von mehreren Aufgaben in einem einzigen (PowerShell) Prozess.
    Stabile Programme mit Multithreading zu entwickeln ist selbst für erfahrenen Programmierer keine leichte Aufgabe.

    Die Prozesse oder Threads (Runspaces) arbeiten nicht nur Parallel sondern sind völlig unabhängig von einander. Hier kann man nicht voraussagen wann eine Aufgabe fertig ist und wann nicht. Die Arbeit geschieht Asynchron!
    Deshalb muss sich selbst um die Synchronisierung (Abholung) der Ergebnisse kümmern.
    Ebenso kann jederzeit ein Fehler in einem Prozess/Tread auftreten, der auch behandelt werden muss sonnst wartet man eventuell ewig auf das Ergebnis.

    Ein Prozess oder ein Thread läuft in seiner eigenen Umgebung, die Prozesse oder Threads können sich nur sehr schwer gegenseitig Nachrichten / Daten schicken. Da der Prozess oder der Thread mit Arbeiten beschäftigt ist.

    Wenn sich Prozesse oder Threads Daten Teilen, muss der zugriff auf diese Daten Synchronisiert werden.
    Wenn z.B. Ein Prozess die Daten lesen will und ein anderer will die Daten gleichzeitig schreiben was dann?

    Hier muss der Programmierer / Scripter die „Unfallverhütung“ sprich Synchronisierung der zugriffe regeln!
    Dafür gibt es nur wenige Automatismen.
    (z.B. Bei PowerShell die Synchronized Hashtable)

    Wie bei einer Mehrspurige Straße (Autobahn), wo fröhlich überholt werden darf.
    Versuch mal auf einer Autobahn, etwas von einem Lastwagen auf den Anderen umzuladen. ;-)

    PowerShell wurde konzipiert um Administrative Aufgaben im Hintergrund OHNE Fenster meist remote zu erledigen. Automation eben. Dafür reicht ein Prozess meistens aus.

    Bei der GUI Programmierung muss man sich mit den Events beschäftigen die in der PowerShell fast keine Rolle spielen.
    Windows Forms oder WPF Programme haben eine schleife die die Events fast in Echtzeit behandelt, da diese auch Multithreading nutzen.
    Diese fehlt bei PowerShell.
    In der PowerShell werden Events erst behandelt, wenn der eine PowerShell Prozess gerade mal Zeit hat.
    Wann ein Event behandelt wird, ist also in der PowerShell nicht vorhersehbar!
    Das Eventsystem von PowerShell ist deshalb nur sehr schlecht geeignet um Events zu bearbeiten.

    GUI Programmierung erfordert also grundlegenden Kenntnisse in der.NET Programmierung im Multithreading UND PowerShell Kenntnisse.
    Aus diesem Grund gibt es wenige PowerShell Nutzer die  GUI Programmierung beherrschen (wollen oder müssen).
    Bei der GUI Programmierung muss man natürlich immer bei den C# .NET Entwicklern nachschauen da dies kein Gebiet der PowerShell ist!
    Man sollte auch bei der Internet Recherche mit dem Suchwort C# arbeiten, anstatt mit dem Suchwort PowerShell.

    Lange rede Kurzer Sinn.
    Du musst PowerShell Jobs oder Runspaces (Multithreading) benutzen und diese selbst Synchronisieren, wenn du PowerShell GUI benutzen willst

     

    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+

    Dienstag, 13. Mai 2014 13:32
  • Vielen Dank für die sehr schön erklärten Grundlagen von sequentiellen Befehlsketten und Multithreading/Multiprozessing. 

    Das Prinzip ist mir bereits annähernd geläufig. Mein Projekt soll eigentlich auch nur eine einfache administrative Aufgabe übernehmen, weshalb ich Powershell und nicht beispielsweise VB.NET einsetzen wollte. Dennoch ist der Wunsch einer grafischen Oberfläche da, die lediglich dem User einen Status anzeigen soll, sowie eine Abfrage ob der User das Skript ausführen möchte. Die wesentliche Programmroutine hatte ich bereits vor einiger Zeit in einem einfachen Shell - Script umgesetzt.  Ich hab mir schon fast gedacht, dass ich mit meiner Vorstellung (einbinden einer GUI) einen Spagat anfange, da Powershell dafür nicht vorgesehen ist. Zudem hatte ich die Hoffnung, dass es beim Multithreading oder Multiprozessing eine einfache Möglichkeit der Kommunikation gibt :( 

    Dann werde ich mal versuchen, ob ich es dennoch mit Jobs oder Multithreading umsetzen kann... 

    Vielen Dank für Deine Bemühungen!

    Dienstag, 13. Mai 2014 14:26
  • Für alle die es interessiert, ich hab mich jetzt für folgenden Weg entschieden:

    #WINDOWS-FORM:
    $dForm = New-Object System.Windows.Forms.Form
    $dForm.Text = "Fenster"
    
    #TEXTBOX:
    $dText = New-Object System.Windows.Forms.TextBox
    $dText.Dock = [System.Windows.Forms.DockStyle]::fill
    $dText.Multiline = $true
    $dText.Parent = $dForm
    $dForm.Add_Shown({$dform.Activate()})
    
    #THREAD: Erzeugt einen Powershell Runspace (Instance von Powershell)
    $newrs = [RunspaceFactory]::CreateRunspace()
    #MTA = multi-threaded appartment, STA = single-threaded appartment
    $newrs.ApartmentState = "MTA"
    $newrs.Open()
    
    #VARIABLE: Erzeugt eine Variable mit dessen Hilfe der Runspace angesprochen werden kann:
    $newrs.SessionStateProxy.SetVariable("dForm", $dForm)
    
    #PIPLINE: Erzeugt eine Pipeline im neuen Runspace die die Form darstellt 
    # Anschließend wird der Inputstream geschlossen und die Pipeline asynchron gestartet
    $p = $newrs.CreatePipeline( { [void] $dForm.ShowDialog() } )
    $p.Input.Close()
    $p.InvokeAsync()
    
    #TEXT
    $dText.AppendText("Programm gestartet`n")
    
    #EXTERNES PROGRAMM STARTEN
    & "C:\Windows\System32\taskmgr.exe" 
    Start-Sleep -s 4
    do {
    $p = Get-Process taskmgr
    Start-Sleep -Seconds 3
    }
    while ($p -ne $Null)
    $dText.AppendText("Programm beendet`n")
    Start-Sleep -s 4
    
    #FENSTER & RUNSPACE BEENDEN
    $dForm.Close()
    $newrs.Close()
    Natürlich stellt es nur ein grobes Gerüst dar!

    • Als Antwort vorgeschlagen Peter Kriegel Donnerstag, 15. Mai 2014 05:06
    • Als Antwort markiert Alex Pitulice Montag, 19. Mai 2014 14:48
    Mittwoch, 14. Mai 2014 16:32