saschard
Goto Top

Powershell: Timer-Funktion und Progressbar-Funktion

Hallo zusammen,

ich benötige bei 2 Funktionen in Powershell etwas Unterstützung:

Fall-1:

Ich habe eine Timer-Funktion die eine Anzahl(X) Sekunden abläuft bevor ein Installationsprozess gestartet wird:
function Waittimer {
	$pbTimer = 1*3000
	$pbLength = $pbTimer / 100		
	$pbTimer..0 | Foreach-Object {
		$pbMin = [int](([string]($pbTimer/60)).split('.'))  
		$Timerlabel.text = 'Starte Installation in: ' + $pbMin + ' Min ' + ($pbTimer % 60) + ' Sek'  
    	Start-Sleep -Seconds 1
		$pbTimer --
	}
	 $Timerlabel.Hide()
	 $Installlabel.Show()
}
Diese Funktion arbeitet wie gewünscht die Anzahl(X) Sekunden ab bevor der nächste Schritt folgt.
Es gibt aber einen "Abbruch"-Button der in diesem Zeitraum gedrückt werden kann, damit der Installationsprozess abgebrochen werden kann (deswegen diese 5 Minuten Wartezeit vor der eigentlich Installation).

Die CancelButton Funktion sieht wie folgt aus:
$CancelButton_Click = {
	EnabledButtons
	[System.Windows.Forms.MessageBox]::Show('Installationsprozess wurde abgebrochen.', 'Abbruch', 0)  
	$ProgressBar.Hide()
	$Installlabel.Hide()
	$CancelButton.visible = $false
}
Jemand eine Ahnung wie man es realisieren könnte? IF-Bedienung? in Batch hätte es ich es mit einer IF-Bediengung und dann einem Goto realisiert.
Wo ich grade von Batch fasel das kommt in Fall-2:

Gleiches Script (selbe GUI). Batch wird mittels Click-Funktion gestartet.
$SButton_Click = {
	DisabledButtons
	$CancelButton.visible = $true
	Mail
	$Maillabel.Hide()
	$Timerlabel.Show()
	Waittimer
	$ServerTimer.Start()
	$ProgressBar.Show()
		IF ($ProgressBar.Value = $ProgressBar.Maximum) {
			Start-Process C:\DailyBuild\install_script\server.bat -Wait
			EnabledButtons
	}
}
Jedoch füllt sich die Progressbar nicht während des Laufs der Batch sondern, erst nachdem diese durchgelaufen ist.

Danke im Vorraus.

Gruß, Sascha

Content-Key: 242605

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

Printed on: April 16, 2024 at 15:04 o'clock

Member: colinardo
Solution colinardo Jul 03, 2014, updated at Jul 10, 2014 at 13:25:36 (UTC)
Goto Top
Hallo Sascha,
entweder mit einem separaten Thread wie hier demonstriert:
Powershell GUI bleibt während einer While schleife hängen...
Alternativ gibt es auch das CMDLet Write-Progress wenn eine Progressbar für die Konsole ausreicht.

Eine einfachere Methode eine Batch laufen zu lassen und darauf zu warten das diese beendet wurde ist das folgende Konstrukt:

Beispiel um auf die Fertigstellung eines Befehls oder Batch zu warten um während dessen etwas anderes zu tun
# Ping starten und in der Konsole weiterarbeiten (nicht warten ! also ohne -wait)
$proc = Start-Process "ping.exe" -ArgumentList "-n 4 127.0.0.1" -NoNewWindow -PassThru  

# die Schleife arbeitet solange bis der Prozess beendet wurde.
while(!$proc.HasExited){
    # tu hier was du willst
    write-host "." -NoNewline  
    sleep -Milliseconds 200
}
Write-host "Finished"  
Grüße Uwe
Member: SaschaRD
SaschaRD Jul 10, 2014 at 13:29:57 (UTC)
Goto Top
Hallo Uwe,

danke für deine Hilfe.
Habe es jetzt nach einen Zwecken umgebaut.

Zwei kleine Fragen noch:
$psCmd = [powershell]::Create()
Hier wird eine neue Powershell-Session im Hintergrund geöffnet?

Und wie kann ich eine function bei
$psCmd.AddScript({
})
aufrufen?

Gruß, Sascha
Member: colinardo
colinardo Jul 10, 2014 updated at 13:39:32 (UTC)
Goto Top
Hallo Sascha,
Zitat von @SaschaRD:
Hier wird eine neue Powershell-Session im Hintergrund geöffnet?
Yip, ein neuer Runspace
Und wie kann ich eine function bei ... aufrufen
deine Function darin aufrufen, du musst aber beachten das du so aus dem separaten Thread kein Update deiner Controls im ersten Thread machen kannst (CrossThread-Exception), deswegen habe ich hier mit einer synced Hashtable gearbeitet, um die Progressbar aus dem zweiten Thread upzudaten.

Wenn das nicht verständlich ist, mach es wie oben unter der Alternative beschrieben, ist eventuell einfacher für dich.

Grüße Uwe
Member: SaschaRD
SaschaRD Jul 10, 2014 at 14:04:56 (UTC)
Goto Top
Leider nimmt er die Function nicht.

Die Function ist ein einfacher Mail versandt.
	function MailDone {
		$PC = gc env:computername
		$SMTP = 'XXX'  
		$SUBJECT = 'Installiere --> Fertig '+$PC  
		$BODY = ' '  
		$FROM = 'XXX@'+$PC  
		$TO = 'XXX@XXX.de'  
			Send-MailMessage -To $TO -Subject $SUBJECT -Body $BODY -SmtpServer $SMTP -From $FROM
				$Maillabel.Show()
				$Maillabel.Text = 'E-Mail wird an XXX versandt...'  
				Sleep -Seconds 3
	}

Diese Function möchte ich nun in $psCmd.AddScript({ }) aufrufen.
	$SButton_Click = {
		$syncHash.Cancel = $pbBreak = $false
		DisabledButtons
		$CancelButton.Visible = $true
		MailGo
		Maillabel.Hide()
		$Timerlabel.Show()
		IF ($ServerButton.Text -eq 'Server') {  
			$newRunspace =[runspacefactory]::CreateRunspace()
			$newRunspace.ApartmentState = 'STA'  
			$newRunspace.ThreadOptions = 'ReuseThread'  
			$newRunspace.Open()
			$newRunspace.SessionStateProxy.SetVariable('syncHash',$syncHash)  
			
			$psCmd.AddScript({
				$syncHash.Install.Text = 'Installation wird durchgeführt...'	  
				$pbTimer = 1*3
				$pbLength = $pbTimer / 100
				#$pbTimer..0 | Foreach-Object {
				while($pbTimer -ge 0 -and $syncHash.Cancel -eq $false) {
					$pbMin = [int](([string]($pbTimer/60)).split('.'))  
					$syncHash.Timer.Text = 'Starte Installation in: ' + $pbMin + ' Min ' + ($pbTimer % 60) + ' Sek'  
					Sleep -Seconds 1
					$pbTimer --
				}
				$syncHash.Timer.Visible = $false
				$syncHash.Install.Visible = $true
				IF ($syncHash.Cancel -eq $false) {
					$proc = Start-Process "C:\Users\saschad\Desktop\abcd.bat" -ArgumentList "-n 4 127.0.0.1" -NoNewWindow -PassThru  
					$syncHash.Progress.Visible = $true
					while(!$proc.HasExited -and $syncHash.Cancel -eq $false) {
						#-and !$proc.ExitCode -eq 1
						$syncHash.Progress.Value ++
						Sleep -Milliseconds 500		
							IF($syncHash.Progress.Value -ge $syncHash.Progress.Maximum) {
							$syncHash.Progress.Value = 1
						}
					}
					IF ($proc.HasExited -eq $true) {
						$syncHash.Progress.Value = 100
						$syncHash.Install.Visible = $false
						$syncHash.Done.Visible = $true
						$syncHash.Done.Text = 'DailyBuild erfolgreich installiert!'  
						$syncHash.SButton.Enabled = $true
						$syncHash.NButton.Enabled = $true
						$syncHash.DButton.Enabled = $true
						$syncHash.IButton.Enabled = $true
						$syncHash.CancelButton.Visible = $false
						$syncHash.CloseButton.Enabled = $true
						MailDone
					}					
				}
			})
			$psCmd.Runspace = $newRunspace
			$data = $psCmd.BeginInvoke()
		} ELSE {
			$psCmd.Stop()			
			$CancelButton.Text = "Abbrechen"  
	 	}
	}
Habe jetzt schon die Buttons einzeln übergeben (Enabled/Visible).

Nur wie übergebe ich dem (Sub)-Prozess "psCmd" eine Function?

Gruß, Sascha

Danke für deine Hilfe @uwe
Member: colinardo
colinardo Jul 10, 2014 updated at 14:32:48 (UTC)
Goto Top
Oh je, da ist alles miteinander vermischt, das wird so nichts. Mach es besser so wie oben geschrieben mit der einfacheren Methode.
Member: SaschaRD
SaschaRD Jul 10, 2014 updated at 15:39:04 (UTC)
Goto Top
Es funktioniert aber so wie es soll.
Ich kann lediglich keine Functions aufrufen face-sad

Diesen Block
$syncHash.SButton.Enabled = $true
$syncHash.NButton.Enabled = $true
$syncHash.DButton.Enabled = $true
$syncHash.IButton.Enabled = $true
$syncHash.CancelButton.Visible = $false
$syncHash.CloseButton.Enabled = $true
Hätte ich auch gerne durch die Function EnableButtons ersetzt.

Gruß, Sascha
Member: colinardo
Solution colinardo Jul 10, 2014, updated at Jul 11, 2014 at 07:13:03 (UTC)
Goto Top
pack den Funktionsaufruf in einen Scriptblock den du einer Property der Hashtable zuweist, und rufe dann diesen Scriptblock auf:
# deine Function
function enableButtons(){ ........}

#Funktionsaufruf in eine Property der Hashtable packen
$syncHash.enableButtonsDelegate = {enableButtons}

#im RunSpace Thread rufst du dann den Scriptblock so auf (das & am Anfang nicht vergessen)
&$syncHash.enableButtonsDelegate
Member: SaschaRD
SaschaRD Jul 11, 2014 at 05:24:44 (UTC)
Goto Top
Morgen Uwe,

Funktioniert 1A.

Vielen Dank für deine Hilfe.

Gruß, Sascha