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-Key: 285535

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

Printed on: April 23, 2024 at 07:04 o'clock

Mitglied: 122990
122990 Oct 14, 2015 at 15:41:58 (UTC)
Goto Top
Moin,
vernünftiges MultiThreading nutzen ...
Powershell progressbar AND multitask

Gruß grexit
Member: H41mSh1C0R
H41mSh1C0R Oct 14, 2015 at 15:52:19 (UTC)
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ß
Mitglied: 114757
Solution 114757 Oct 14, 2015 updated at 19:47:51 (UTC)
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
Member: H41mSh1C0R
H41mSh1C0R Oct 14, 2015 at 15:56:38 (UTC)
Goto Top
man man man, danke dir.

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

*weiterklöppel*
Mitglied: 114757
114757 Oct 14, 2015 updated at 16:01:54 (UTC)
Goto Top
Zitat von @H41mSh1C0R:
sauber Kopieren
?? Vielleicht mal den Rechner putzen face-big-smile
Member: H41mSh1C0R
H41mSh1C0R Oct 14, 2015 at 16:07:27 (UTC)
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.
Member: colinardo
Solution colinardo Oct 14, 2015 updated at 19:47:43 (UTC)
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
Member: H41mSh1C0R
H41mSh1C0R Oct 14, 2015 updated at 19:30:28 (UTC)
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ß
Member: colinardo
Solution colinardo Oct 15, 2015 updated at 11:28:13 (UTC)
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
Member: H41mSh1C0R
H41mSh1C0R Oct 15, 2015 updated at 11:04:24 (UTC)
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ß
Mitglied: 114757
114757 Oct 15, 2015 at 11:29:13 (UTC)
Goto Top
So ein Fenster hab ich hier testweise auch nicht, liegt bestimmt an deiner GUI ...
Background-Job = Keine GUI.

Gruß jodel32
Member: H41mSh1C0R
H41mSh1C0R Oct 15, 2015 at 11:46:27 (UTC)
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ß
Mitglied: 114757
114757 Oct 15, 2015 updated at 11:52:58 (UTC)
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.
Mitglied: 122990
122990 Oct 15, 2015 updated at 13:15:26 (UTC)
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.
Member: H41mSh1C0R
H41mSh1C0R Oct 15, 2015 at 15:04:10 (UTC)
Goto Top
=)

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