h41msh1c0r
Goto Top

Powershell Progressbar will nicht

Hi @all

auch wenn es mit Write-Progress ein cmdlet gibt würde ich aktuell den Progressbar in der eigenen GUI mit Fortschritt versorgen ohne ein extra Fenster zu haben.

function robocopy
{
	process
	{
		#count the source files
		$filecount = 0
		$lb_progressText.text = " Vorbereitung ..."  
		if ("C:\Quelle" -notlike $null)  
		{
			$sourcefiles = robocopy.exe "C:\Quelle" "C:\Quelle" /L /S /NJH /BYTES /FP /NC /NDL /TS /XJ /R:0 /W:0  
			If ($sourcefiles[-5] -match '^\s{3}Files\s:\s+(?<Count>\d+).*') { $filecount = $matches.Count }  
		}
		$lb_progressText.Text = $filecount
		$lb_progressText.Focus()
		
		$run = robocopy.exe "C:\Quelle" "C:\temp\Ziel" /MIR /ZB /R:10 /W:10 /ETA | foreach  
# ab hier scheint er ein Problem zu haben		
{
			#calculate percentage
			$i++
			[int]$pct = ($i/$filecount) * 100
			#update the progress bar
			$pgb_robocopy.Value = ($pct)
			$lb_progressText.AppendText($i)
			
			[void] [System.Windows.Forms.Application]::DoEvents()
		}
	}
	end { $pgb_robocopy.Value = 100 }
}

Das endet leider in folgendem Fehler:

ERROR: foreach : Cannot bind parameter 'Process'. Cannot convert the "" value of type "System.String" to type "System.Management.Automation.ScriptBlock".  
ERROR:
MainForm.psf (538): ERROR: At Line: 538 char: 91
ERROR: +             $run = robocopy.exe "C:\Quelle" "C:\temp\Ziel" /MIR /ZB /R:10 /W:10 /ETA | foreach <<<<  
ERROR:     + CategoryInfo          : InvalidArgument: (:) [ForEach-Object], ParameterBindingException
ERROR:     + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.ForEachObjectCommand
ERROR:

Wie krieg ich das aufgelöst, wo wäre der Ansatz?

Gruß

Content-ID: 285535

Url: https://administrator.de/forum/powershell-progressbar-will-nicht-285535.html

Ausgedruckt am: 16.04.2025 um 10:04 Uhr

122990
122990 14.10.2015 um 17:41:58 Uhr
Goto Top
Moin,
vernünftiges MultiThreading nutzen ...
Powershell progressbar AND multitask

Gruß grexit
H41mSh1C0R
H41mSh1C0R 14.10.2015 um 17:52:19 Uhr
Goto Top
Hi grexit,

schonmal danke für den Multithread Hinweis. Werde wenn ich zuhause bin versuchen mir ein Beispiel zusammenzubauen.

Allerdings bis auf das die UI dann nicht blockiert ist hilft das ja nicht bei der Fehlermeldung.

Gruß
114757
Lösung 114757 14.10.2015 aktualisiert um 21:47:51 Uhr
Goto Top
Moin,
dein Fehler ist, dass die öffnende geschweifte Klammer bei foreach erst in der nächsten Zeile beginnt, diese muss aber bei Verwendung der Pipe-Schreibweise in der selben Zeile stehen ...

Geht:
 | foreach {
geht nicht:
  | foreach
{
Deswegen auch die Fehlermeldung bezüglich "Skript-Block" face-wink Denn ein {} alleine stehend ist ein Skriptblock.

Gruß jodel32
H41mSh1C0R
H41mSh1C0R 14.10.2015 um 17:56:38 Uhr
Goto Top
man man man, danke dir.

Jetzt muss ich ihn nur noch zum sauber Kopieren bekommen. =)

*weiterklöppel*
114757
114757 14.10.2015 aktualisiert um 18:01:54 Uhr
Goto Top
Zitat von @H41mSh1C0R:
sauber Kopieren
?? Vielleicht mal den Rechner putzen face-big-smile
H41mSh1C0R
H41mSh1C0R 14.10.2015 um 18:07:27 Uhr
Goto Top
pron pron pron *gg* ne Spaß beiseite

Ziel für Heute Abend/Nacht ist das das robocopy von Quelle nach Ziel kopiert und mir der Progressbar das anzeigt.
colinardo
Lösung colinardo 14.10.2015 aktualisiert um 21:47:43 Uhr
Goto Top
Moin zusammen,
Einfaches Beispiel für Robocopy:
#region Import the Assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null  
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null  
#endregion

#region Generated Form Objects
$form1 = New-Object System.Windows.Forms.Form
$button1 = New-Object System.Windows.Forms.Button
$progress = New-Object System.Windows.Forms.ProgressBar
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
$timer = New-Object System.Windows.Forms.Timer
#endregion Generated Form Objects


$global:job = $null
$global:currentcnt = 0
$global:totalcnt = 0

$handler_button1_Click= {
    if ($button1.Text -eq "Start"){  
        $timer.Start()
        $button1.Text = "Running"  
        $button1.Enabled = $false
        $progress.Value = 0

        $global:currentcnt = 0
        # Anzahl der Dateien ermitteln
        $global:totalcnt = (robocopy "C:\quelle" "C:\ziel" /S /ZB /ETA /L /NS /NC /NJH /NJS /NDL ).length -1  

        $i = 0
        $global:job = Start-Job -ScriptBlock {
            robocopy "C:\quelle" "C:\ziel" /S /ZB /ETA /NS /NC /NJH /NJS /NDL /NP /R:10 /W:10  
        }
    }else{
        $timer.Stop()
        $global:job | stop-job
        $global:job | remove-job -Force
        $button1.Text = "Start"  
    }

}
$handler_tick = {
    $global:currentcnt += ($global:job | Receive-Job).Count
    $percent = ($global:currentcnt / $global:totalcnt) * 100

    if ($percent -lt 100) {
        $progress.Value = $percent
    }else{
        $progress.Value = 100
        $timer.Enabled = $false
        $button1.Enabled = $true
        $button1.Text = "Start"  
       $global:job | remove-job -ErrorAction SilentlyContinue
    }
}

$OnLoadForm_StateCorrection=
{#Correct the initial state of the form to prevent the .Net maximized form issue
	$form1.WindowState = $InitialFormWindowState
}

#----------------------------------------------
#region Generated Form Code
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 74
$System_Drawing_Size.Width = 292
$form1.ClientSize = $System_Drawing_Size
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.MaximizeBox = $False
$form1.Name = "form1"  
$form1.Text = "ProgressForm"  


$button1.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 41
$button1.Location = $System_Drawing_Point
$button1.Name = "button1"  
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 268
$button1.Size = $System_Drawing_Size
$button1.TabIndex = 1
$button1.Text = "Start"  
$button1.UseVisualStyleBackColor = $True
$button1.add_Click($handler_button1_Click)

$form1.Controls.Add($button1)

$progress.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 12
$progress.Location = $System_Drawing_Point
$progress.Name = "progress"  
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 268
$progress.Size = $System_Drawing_Size
$progress.TabIndex = 0

$form1.Controls.Add($progress)

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($OnLoadForm_StateCorrection)

#Timer settings
$timer.add_tick($handler_tick)
$timer.Enabled = $false
$timer.Interval = 500

#----- Show Form
$form1.ShowDialog() | out-null

# WICHTIG Timer resourcen freigeben 
$timer.dispose()
Grüße Uwe
H41mSh1C0R
H41mSh1C0R 14.10.2015 aktualisiert um 21:30:28 Uhr
Goto Top
Hallo Uwe,

fettes Danke an dich.

Erscheint bei dir auch kurz ein Consolenfester bevor der Kopiervorgang startet?
Wenn ich den Log Parameter zum Robocopy hinzufüge läuft der Progressbar nicht los.

Gruß
colinardo
Lösung colinardo 15.10.2015 aktualisiert um 13:28:13 Uhr
Goto Top
Zitat von @H41mSh1C0R:
Erscheint bei dir auch kurz ein Consolenfester bevor der Kopiervorgang startet?
Nein, das kann schon nicht weil es ja ein Background-Job ist.
Wenn ich den Log Parameter zum Robocopy hinzufüge läuft der Progressbar nicht los.
Normal, weil mein Skript die Anzahl der Zeilen in der Ausgabe nutzt um den Status zu ermitteln, und bei Verwendung von /LOG:C:\logfile.txt wird die Ausgabe der Dateien unterdrückt deswegen kann der Status nicht ermittelt werden, wenn du die Logging-Option verwenden willst musst du zusätzlich den Parameter /tee verwenden der die kopierten Dateien trotzdem des LOG-Parameters auflistet.
Hätte man aber auch selber gesehen wenn man sich die Ausgabe von robocopy mal angesehen hätte face-wink

Grüße Uwe
H41mSh1C0R
H41mSh1C0R 15.10.2015 aktualisiert um 13:04:24 Uhr
Goto Top
Hallo Uwe,

e11efe9b95b2958a53de87dc39e34d7f

Das Konsolenfenster erscheint nur beim ersten auslösen des Ereignisses was den Kopiervorgang auslöst.
Ab dem 2ten auslösen tritt dieses Fenster nicht mehr auf.

Danke dir für die Erläuterung bzgl. des LOG Parameters.

Gruß
114757
114757 15.10.2015 um 13:29:13 Uhr
Goto Top
So ein Fenster hab ich hier testweise auch nicht, liegt bestimmt an deiner GUI ...
Background-Job = Keine GUI.

Gruß jodel32
H41mSh1C0R
H41mSh1C0R 15.10.2015 um 13:46:27 Uhr
Goto Top
Die Vermutung hab ich auch.

$datagridview1_CellContentClick=[System.Windows.Forms.DataGridViewCellEventHandler]{	
	if ($_.ColumnIndex -eq 0)
	{
		if ((Call-Start_psf) -eq 'OK')  
		{
			# hier kommt die funktion für den Start rein
		}
	}
		
	if ($_.ColumnIndex -eq 5)
	{
		# Ablauf beim Klick auf diesen aktualisieren Button
		$timer.Start()
		$pgb_robocopy.Value = 0
		$global:currentcnt = 0
			
		# Anzahl der Dateien ermitteln
		$global:totalcnt = (robocopy "C:\Program Files" "C:\temp\enc" /MIR /S /ETA /L /NC /NP ).length - 1  
			
		$i = 0
		
		# Erstellt einen Job und legt ein LogFile für diesen Tag und die aktuelle Aktion an
		$global:job = Start-Job -ScriptBlock {
			robocopy "C:\Quelle" "C:\temp\ziel" /MIR /S /ETA /NC /NP /R:10 /W:10 /TEE /LOG:"C:\temp\logfile_$(get-date -f MM-dd-yyyy).log"  
		}
		
		# Wenn Kopiervorgang erfolgreich schreibe den Status in das Entries File und aktualisiere das DataGrid
		....
		$datagridview1.Refresh
	}
	
	if ($_.ColumnIndex -eq 6)
	{
		if ((Call-Upload_psf) -eq 'OK')  
		{
			# hier kommt die Funktion für den Upload rein, ist mit Download identisch bis auf die Parameter für Quelle und Ziel und wo das Log abgelegt wird
		}
	}
}


Den Timer starte ich erst beim auslösen des Clicks auf den Button.
Das dispose hab ich im Handler Tick im else Zweig, somit gibt er die Ressourcen frei wenn die Kopieraktion durchgelaufen ist oder irre ich mich da?

Gruß
114757
114757 15.10.2015 aktualisiert um 13:52:58 Uhr
Goto Top
Das dispose hab ich im Handler Tick im else Zweig
Keine gute Idee, damit würdest du dir den Event-Handler unter dem Arsch wegziehen face-smile

Achtung: Wenn dein Skript irgendwie crashed, und er Timer nicht korrekt zerstört wurde macht die ISE hinterher die komischsten Sachen !!!

Teilweise muss man die Maschine dann neu starten, das es wieder läuft.
122990
122990 15.10.2015 aktualisiert um 15:15:26 Uhr
Goto Top
Sehr wahrscheinlich kommt das Fenster vom ersten ausführen von Robocopy für das Zählen der Files, denn das läuft im Vordergrund-Thread, dann musst du es mit process-start hidden ausführen und die Anzahl der Zeilen abfragen.
H41mSh1C0R
H41mSh1C0R 15.10.2015 um 17:04:10 Uhr
Goto Top
=)

Hab das dispose ins Main verschoben wenn das Form beendet wird.