Wie kann ich in der Powershell mit Windows Forms noch Resultate vom Befehl in Echtzeit darstellen?

3 Antworten

Ja da hast Du eine hübsche "leblose" Schachtel konstruier...🤣

Mir wäre es lieber gewesen, wenn Du statt Deines Bildchens den Code gepostet hättest. ...Aber nein es ist prima, wenn ich Zeit mit dem tippen einer Oberfläche vergeude... Ich habe ja sonst nichts zu tun...

egal...

Palladin007 hat das Warum nichts passiert bereits erklärt.

Ich erklär wie man es löst. Du kannst dem Textfeld den Output deines Programms direkt zuweißen, dann passiert solange nichts bis der Download oder was auch immer fertig ist und der braucht Zeit , den Powershell macht schon Eins nach dem Anderen.

Zeit ist das Stichwort, wir müssen über die Zeit verteilte Aktionen ausführen damit sich etwas in der GUI verändert. (wenn Du 18 Uhr einkaufen möchtest, starrst Du doch auch nicht ab Mittag permanent die Uhr an, sondern schaust in gewissen Abständen mal nach...

Und so muss das Dein Programm auch machen. Ich möchte jetzt nicht das komplette Eventhandling erklären. nur soviel, Du kannst für bestimmte Ereignisse eine Aktion definieren .

Was soll passieren , wenn die innere Uhr einen Tick macht, oder die Maus auf eien Button klickt.

Ich habe erstmal auf einen parallelen Thread verzichtet, damit du nachvollziehen kannst was ungefähr abläuft. Einen parallelen Thread haben wir eigentlich schon, die Systemuhr... und die Fragen wir in Abständen ab und ergänzen den Text im Fenster.

Ein Timer ruft alle 5 Sekunden die Aktion auf und auf Knopfdruck wird diese ebenfalls aufgerufen. die Aktionsroutine schreibt auch gleich mit ins Fenster, wer sie gerufen hat...

Function DoAction ($Eventquelle)
{
  $output = &{
    'Ereignisquelle: {0}' -f $Eventquelle
    'die Aktuell Zeit ist: {0}' -f (Get-Date -f "HH:mm:ss")
    $myDice = 1..6 | Get-Random
    'Deine Glueckszahl lautet : {0}' -f $myDice
    ''
  }|out-string
  $MyTextbox.Text += $output
  $MyTextbox.SelectionStart = $MyTextbox.Text.Length;
  $MyTextbox.ScrollToCaret()
}

Add-Type -a System.Windows.Forms

$MyTimer = New-Object System.Windows.Forms.Timer
 #Auslösezeit für Timerevent in Millisekunden  wäre Blödsinn)
$MyTimer.Interval = 5000
$MyTimer.Add_Tick({DoAction 'Timmy der Timer'}) #definiere die Aktion für einen Clickevent dieses Timmers ausgelöst wird
$MyTimer.Start()

[System.Windows.Forms.Application]::EnableVisualStyles()
$MyActionButton = New-Object 'System.Windows.Forms.Button'
$MyActionButton.Location = '10, 10'
$MyActionButton.Name = "MyActionButton"
$MyActionButton.Size = '200, 25'
$MyActionButton.TabIndex = 0
$MyActionButton.Text = "&zwischendurch abrufen"
$MyActionButton.UseVisualStyleBackColor = $true
$MyActionButton.Add_Click({DoAction 'Der bloede Knopf da oben'}) #definiere die Aktion für einen Clickevent dieses Buttons ausgelöst wird

$MyTextbox = New-Object 'System.Windows.Forms.TextBox'
$MyTextbox.Location = '12, 50'
$MyTextbox.Multiline = $true
$MyTextbox.Name = "MyTextbox"
$MyTextbox.Size = '470, 150'
$MyTextbox.Scrollbars = "Vertical"
$MyTextbox.TabIndex = 1

$Form = New-Object 'System.Windows.Forms.Form'
$Form.Size = '500, 250'
$Form.Controls.Add($MyActionButton)
$Form.Controls.Add($MyTextbox)
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.Name = "Form"
$Form.Text = "Ich bin Glueckszeitsammler"
$Form.Add_Load({DoAction 'Einer muss anfangen'})
 # starte GUI
$Form.ShowDialog()

Den Output eines Programms in asynchron zu belauschen geht mit ganz wenigen Zeilen. Powershell hat dafür Jobs ...

Was in einem Job laufen soll ,läuft in der Regel ziemlich lange . Der Job stellt einen Buffer zur Verfügung, welchen man bei Bedarf leeren kann. (ein Briefkasten sozusagen) eine MiniDemo dazu:

$job = start-job -ScriptBlock {1..120|%{
        '{0}. Brief eingeworfen' -f $($_) 
        sleep 1
    }
}

while((get-job).state){
    'Jobstatus : ' + (get-job).state
    'ich  hole mal die Post aus dem Kasten'
    $output=Receive-Job -Job $job|out-string  #wir brachen nur jeweils die letzte Ausgabzeile, welche in den gesezten Zeitabständen verfügbar ist
    $output
    if (($output -eq '') -and ((get-job).state -eq 'Completed') ) {Remove-Job $job}
    sleep 3
}
' Da kommt nix mehr...'
pause

...und genauso machen wir es mit deinem Programm in einen parallelen Job verfrachten und die Aktionsroutine die Daten abholen lassen.

  # starte das Programm in einem parallelen Thread. (sonst wäre es nicht möglich in einer zeitlichen Abfolge die Ausgabe  "Stückenweise" an eine Variable, wie $MyTextbox.Text, zu übergeben)
  # das als Job ausgeführte Programm schreibt in einen Buffer, welcher mit "Receive-Job" bei jedem aufruf "leergelesen" wird.
$job = start-job  {."C:\Users\Erzesel Secure\Desktop\youtube-dl.exe" --output "testvideo" htt_usw.videoadresse |%{$_}2>$null}

Function DoAction () {
    $output = Receive-Job -Job $job |out-string #Daten aus der  Jobpipeline abholen      o
    $MyTextbox.Text += $output
    if (($output -eq '') -and ((get-job).state -eq 'Completed')) {
        Remove-Job $job;
        $MyTimer.Stop();
        $MyTextbox.Text += 'ich habe fertig...'|out-string
    }
    $MyTextbox.SelectionStart = $MyTextbox.Text.Length;
    $MyTextbox.ScrollToCaret()
}

Add-Type -a System.Windows.Forms

$MyTimer = New-Object System.Windows.Forms.Timer
  #Auslösezeit für Timerevent in Millisekunden
$MyTimer.Interval = 1000
$MyTimer.Add_Tick({DoAction}) #definiere die Aktion für einen Tickevent dieses Timers ausgelöst wird
$MyTimer.Start()

$MyTextbox = New-Object 'System.Windows.Forms.TextBox'
$MyTextbox.Name = "MyTextbox"
$MyTextbox.Location = '10, 20'
$MyTextbox.Size = '470, 170'
$MyTextbox.TabIndex = 1
$MyTextbox.Multiline = $true
$MyTextbox.WordWrap = $false;
$MyTextbox.Scrollbars = 'Both'

$Form = New-Object 'System.Windows.Forms.Form'
$Form.Name = "Form"
$Form.Size = '500, 250'
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.Text = "My Downloade"
$Form.Controls.Add($MyTextbox)
$Form.Add_Load({DoAction}) #Was gleich nach dem Aufbau des Form-Objekts passieren soll

$Null=$Form.ShowDialog()

...trivial, wenn man weiß wie es geht...

das nächtste mal Code und keine Bilder!😏😉

Die videoadresse musst Du selber einsetzen... die wird von GF als video angezeigt .

Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Ich habe mich für eine textbasierte UI entschieden!

Das Problem ist, dass dein Fenster mit einem UI-Thread arbeitet, der sich um das Fenster kümmert. Den Download lässt Du im UI-Thread laufen und solange der Download läuft, ist der UI-Thread beschäftigt und kann nichts aktualisieren.

Das Thema heißt asynchrone Programmierung.

Und in Powershell ... keine Ahnung, wie es da geht, aber vermutlich ist es eine Qual :D
Wenn Du also nicht gerade sehr ausgeprägte masochistische Züge hast, solltest Du lieber mit C# anstatt Powershell arbeiten, immerhin wurde das Zeug, mit dem Du da arbeitest, hauptsächlich für C# entwickelt - naja, eigentlich .NET, aber das wird meistens mit C# genutzt.


Erzesel  21.10.2021, 08:45
aber vermutlich ist es eine Qual :D

Das geht unter Powershell sogar einfacher als in C#.

Einfach einen parallelen Job definieren und die Daten aus dem Ausgabepuffer des Jobs auslesen, bis der Job sich für "fertig" erklärt.

...Da intern die Outputpipeline des Jobs als XML-Format definiert ist (viel Verwaltungsdaten für jedes popelige Integerchen), sollte man jobseitig mehrere kleine Ergebnisse nicht einzeln in die Pipe übergeben, sondern mehrere zu einen langen "Delimiter"-String joinen und diesen am Stück in die Pipe jagen und nach dem Auslesen zu einem Array splitten... aber das nur am Rande

0