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-ID: 242605

Url: https://administrator.de/forum/powershell-timer-funktion-und-progressbar-funktion-242605.html

Ausgedruckt am: 22.12.2024 um 11:12 Uhr

colinardo
Lösung colinardo 03.07.2014, aktualisiert am 10.07.2014 um 15:25:36 Uhr
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
SaschaRD
SaschaRD 10.07.2014 um 15:29:57 Uhr
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
colinardo
colinardo 10.07.2014 aktualisiert um 15:39:32 Uhr
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
SaschaRD
SaschaRD 10.07.2014 um 16:04:56 Uhr
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
colinardo
colinardo 10.07.2014 aktualisiert um 16:32:48 Uhr
Goto Top
Oh je, da ist alles miteinander vermischt, das wird so nichts. Mach es besser so wie oben geschrieben mit der einfacheren Methode.
SaschaRD
SaschaRD 10.07.2014 aktualisiert um 17:39:04 Uhr
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
colinardo
Lösung colinardo 10.07.2014, aktualisiert am 11.07.2014 um 09:13:03 Uhr
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
SaschaRD
SaschaRD 11.07.2014 um 07:24:44 Uhr
Goto Top
Morgen Uwe,

Funktioniert 1A.

Vielen Dank für deine Hilfe.

Gruß, Sascha