lennyy
Goto Top

Powershell GUI bleibt während einer While schleife hängen...

Hallo alle zusammen!

Ich habe zuzeit ein Powershellproblem wo ich einfach nicht mehr weiterkomme. Ich hoffe ihr könnt mir hier helfen.

Vorgeschichte:

Ich schreibe ein kleines Powershell Backupporgramm mit GUI für unsere Kunden.
Das Programm besteht aus 3 Scripten und einer XML-Datei für die Einstellungen.

Einstellungen.ps1 -> Hier werden die Ziel und Quellpfade eingestellt und in der XML Datei gespeichert.
BackupAufExternenDatenträger.ps1 -> In diesem Script befindet sich die GUI (mit Prograssbar für die Fortschrittsanzeige) und der Backupjob
BackupAufInternenDatenträger.ps1 -> Das gleiche wie BackupAufExternenDatenträger.ps1 nur mit andernen Pfaden

Das Backup erfolgt mit Robocopy über den Befehl Start-Job:

$BackupJob = start-job -Name RIS-Backup -ArgumentList $SicherungsQuelle,$sicherungsPfad,$LogPath -scriptblock{
$LogPath = $args[2]
$Robocopy = robocopy $args $args[1] /mir /V /LOG:$Logpath}

Nachdem der Job gestartet wurde gehts an die Fortschrittsberechnung.
Das habe ich einfach mit einer While-Schleife erledigt, die guckt wie groß sind die zu kopierenden Daten und wieviel wurde schon Kopiert das ganze wird dann noch in Prozent um gerechnet und in einer Progressbar angezeigt.


while ($BackupJob.State -eq "Running")
{

$Kopiert = (Get-ChildItem $sicherungsPfad -Recurse | Measure-Object -property length -sum)

$Kopiert = "{0:N2}" -f ($Kopiert.sum / 1GB) | % {$_ -replace ",","."}
$Prozentanzeiger = 0
$Prozentanzeiger = $Kopiert/$GesamtGrBackup*100
"{0:n1}" -f $Prozentanzeiger
$progressBar1.Value = $prozentanzeiger
### 2 Sekunden warten ###
ping -n 2 localhost | out-null
}


Das ganze Funktioniert wunderbar nur habe ich das Problem, dass solange die While schleife am laufen ist, ich nichts mehr in der GUI machen kann (z.b. den Button "Backup Stoppen" drücken oder das Fenster verschieben )

Ich hatte dann noch die Idee die Schleife auch einfach in einen eigenen Job zu packen dann allerdings wird die Progressbar nicht mehr aktualisiert.

Jetzt zu meiner Frage:

Wie schaffe ich es das die Progressbar durch die schleife aktualisiert wird (Mir also der Fortschritt in der GUI angezeigt wird) und ich gleichzeitig die GUI weiterbenutzen kann?

Content-ID: 223797

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

Ausgedruckt am: 19.11.2024 um 20:11 Uhr

colinardo
Lösung colinardo 05.12.2013, aktualisiert am 09.12.2013 um 17:01:37 Uhr
Goto Top
Hallo Lennyy,
da sei dir das Asynchrone Event-Handling der Powershell ans Herz gelegt:
http://blogs.technet.com/b/heyscriptingguy/archive/2011/06/16/use-async ...
Da du hier aber zusätzlich mit einer GUI arbeitest musst du dafür auch mit Delegates oder Runspaces via BeginInvoke arbeiten damit die GUI von einem anderen Thread aus "upgedated" werden kann.
Ein Beispiel kannst du hier sehen:
http://stackoverflow.com/questions/14401704/update-winforms-not-wpf-ui- ...
Du siehst das ganze ist nicht so simpel, les dich aber bitte erst mal ein ...

Grüße Uwe
Lennyy
Lennyy 06.12.2013 um 10:26:41 Uhr
Goto Top
Hallo Uwe,

Vielen Dank erstmal für die schnelle Hilfe!
Ich werde mich jetzt mal daransetzen mir das ganze durchlesen und versuchen da etwas zusammenzubasteln...

Die ergebnisse werde ich dann später posten...

Grüße
colinardo
Lösung colinardo 06.12.2013, aktualisiert am 09.12.2013 um 17:01:35 Uhr
Goto Top
Hallo Lennyy,
da das ganze doch etwas Zeit zur Einarbeitung benötigt habe ich dir hier mal ein Beispiel mit einer Demo-Form zusammen gestellt bei der eine Progress-Bar durch einen parallelen RunSpace(Thread) aktualisiert wird:

#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
#endregion Generated Form Objects

#Create synchronized Hashtable-Object for communication between sub-thread and form
$syncHash = [hashtable]::Synchronized(@{})
#set the controls in the hashtable we wish to manipulate
$syncHash.progress = $progress
# initialize second Powershell runspace
$psCmd = [powershell]::Create()
#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
$handler_button1_Click= 
{
    if ($button1.Text -eq "Start"){  
        # create new runspace to run our sub-thread in
        $newRunspace =[runspacefactory]::CreateRunspace()
        $newRunspace.ApartmentState = "STA"  
        $newRunspace.ThreadOptions = "ReuseThread"            
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)  

        $psCmd.AddScript({
                              # in this expample we set the progress-bar value every 500 ms
                              for ($i = 1;$i -le 10;$i++){
                                  sleep -Milliseconds 500
                                  $syncHash.progress.Value = $i * 10
                              }
                         })
        $psCmd.Runspace = $newRunspace
        #start the runspace
        $data = $psCmd.BeginInvoke()
        $button1.Text = "Stop"  
    }else{
        $psCmd.Stop()
        $button1.Text = "Start"  
    }

}

$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)
#----- Show Form
$form1.ShowDialog() | out-null
Grüße Uwe
Lennyy
Lennyy 06.12.2013 aktualisiert um 16:59:09 Uhr
Goto Top
Ich habe jetzt den halben nachmittag versucht die Runspaces in mein Script mit einzubauen...
Aber irgendwo habe ich noch einen Denkfehler!

Die Form startet ohne Fehler aber irgendwie passiert jetzt nischt mehr wenn ich auf den "Backup Starten"-Button drücke

Vielleicht sieht hier ja jemand wo mein Fehler liegt.

Hier erstmal das Script welches ich geschrieben habe:


Datensicherung mit Robocopy und Fortschritsanzeige in einem eigenen Job ###


  1. Load Assemblies
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.ProgressBar") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.Button") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.FormStartPosition.CenterScreen") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.Label") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.MessageBox") | Out-Null


### Der Pfad wo das Script ausgeführt wird wird ermittelt ###
### Wird Später zum holen der Quell und Zielpfade benötigt ###
$ScriptPath = [System.Management.Automation.InvocationInfo]$myInv=$myInvocation
$ScriptPath = $myInv.get_MyCommand().Definition
$ScriptPath = Split-Path $ScriptPath -parent

############################
### Variablen Definieren ###
############################

[XML]$xml = get-content $ScriptPath\Einstellungen.Xml
$SicherungsQuelle = $xml.Einstellungen.Quelle
$SicherungsZielExtern = $xml.Einstellungen.Extern
$SicherungPfad = "$SicherungsZielExtern\Backup"
$LogPath = "$SicherungsZielExtern\LOG\RIS-Backup.log"


$progressBar1 = new-object System.Windows.Forms.ProgressBar
$BackupAufExternenDatenträger = New-Object System.Windows.Forms.Form
$Label = new-object System.Windows.Forms.Label
$buttonBackupStarten = new-object System.Windows.Forms.Button
$buttonBeenden = new-object System.Windows.Forms.Button

###########################################################
### Überprüfen ob alle benötigten ordner vorhanden sind ###
###########################################################


  1. Es wird überprüft ob der Ordner für die logdateien vorhanden ist
$Test = Test-Path $SicherungsZielExtern\log
if($Test -eq $false){mkdir $SicherungsZielExtern\log|Out-Null;}


$Test = Test-Path ("$SicherungsZielExtern\Backup").ToString()
if($Test -eq $false){mkdir $SicherungsZielExtern\Backup|Out-Null;}


SCRIPT START ####

#Create synchronized Hashtable-Object for communication between sub-thread and form
$syncHash = [hashtable]::Synchronized(@{})
#set the controls in the hashtable we wish to manipulate
$syncHash.progress = $progressbar1
  1. initialize second Powershell runspace
$psCmd = [powershell]::Create()
#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
$handler_button1_Click=
{


### Größe des Backups ermitteln ###
$GesamtGrBackup = 0
$GesamtGrBackup = (Get-ChildItem $SicherungsQuelle -Recurse| Where-Object { `
($_.FullName -notlike "$sicherungsquelle\log*") -and `
($_.FullName -notlike "$sicherungsquelle\Foxfilm*") -and `
($_.FullName -notlike "$sicherungsquelle\datsic*") -and `
($_.FullName -notlike "$sicherungsquelle\Backup*") -and `
($_.FullName -notlike "$sicherungsquelle\ups*") } | Measure-Object -property length -sum)
$GesamtGrBackup = "{0:N2}" -f ($GesamtGrBackup.sum / 1GB) | % {$_ -replace ",","."}

$prozentanzeiger = 0
$progressBar1.Value = $prozentanzeiger

### Backup Starten ###
$label.Text = "Datensicherung läuft..."

$BackupJob = start-job -Name RIS-Backup -ArgumentList $SicherungsQuelle,$SicherungPfad,$LogPath -scriptblock{
$LogPath = $args[2]
$Robocopy = robocopy $args $args[1] /mir /V /XD "Foxfilm" "log" "datsich" "backup" "upd" /LOG:$Logpath}

### Zeitstempel erfassen ###
$vorher = get-date


if ($buttonBackupStarten.Text = "Backup Starten"){
                          1. create new runspace to run our sub-thread in
                          $newRunspace =[runspacefactory]::CreateRunspace()
                          $newRunspace.ApartmentState = "STA"
                          $newRunspace.ThreadOptions = "ReuseThread"
                          $newRunspace.Open()
                          $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)

                          $psCmd.AddScript({
                          ### Nachdem das Backup gestartet wurde, ###
                          ### Wird hier der Fortschritt berechnet ###
                          while ($BackupJob.State -eq "Running")
                          {

                          $Kopiert = (Get-ChildItem $SicherungPfad -Recurse | Measure-Object -property length -sum)

                          $Kopiert = "{0:N2}" -f ($Kopiert.sum / 1GB) | % {$_ -replace ",","."}
                          $Prozentanzeiger = 0
                          $Prozentanzeiger = $Kopiert/$GesamtGrBackup*100
                          "{0:n1}" -f $Prozentanzeiger
                          $progressBar1.Value = $prozentanzeiger
                          ### 1 Sekunde warten ###
                          ping -n 2 localhost | out-null

                          if ($BackupJob.State -eq "Completed"){
                          ### Zeitstempel erfassen ###
                          $nachher = get-date
                          $zeit = "{0:N3}" -f ($nachher - $vorher).TotalMinutes

                          $label.Text = "Datensicherung Abgeschlossen in $zeit Minuten"; $label.ForeColor = "Green"}
                          }
                          })
                          $psCmd.Runspace = $newRunspace
                          #start the runspace
                          $data = $psCmd.BeginInvoke()

                          }

                          #else{
                                  1. $psCmd.Stop()
                                  2. $button1.Text = "Start"
                          #}


                          ### Der Button backuo Starten wird ausgeblendet und Backup beenden eingeblendet
                          $buttonBackupStarten.Visible = $false
                          $buttonBeenden.Visible = $true

                          }


                          function showform{


                          1. Init Form
                          #

                          $BackupAufExternenDatenträger.width = 546
                          $BackupAufExternenDatenträger.height = 163
                          $BackupAufExternenDatenträger.Text = "Passwort eingabe"
                          $BackupAufExternenDatenträger.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
                          #
                          #progressBar1
                          #

                          $progressBar1.Location = new-object System.Drawing.Point(12, 47);
                          $progressBar1.Name = "progressBar1";
                          $progressBar1.Size = new-object System.Drawing.Size(353, 23);
                          #
                          #Label
                          #

                          $Label.AutoSize = $true;
                          $Label.Location = new-object System.Drawing.Point(71, 19);
                          $Label.Name = "Label";
                          $Label.Size = new-object System.Drawing.Size(226, 25);
                          $Label.Text = "Backup NICHT Gestartet";
                          #
                          #Logbutton
                          #
                          $Logbutton = new-object System.Windows.Forms.Button
                          $Logbutton.Location = new-object System.Drawing.Point(254, 76);
                          $Logbutton.Name = "Logbutton";
                          $Logbutton.Size = new-object System.Drawing.Size(111, 23);
                          $Logbutton.Text = "Logdatei Anzeigen";
                          $Logbutton.UseVisualStyleBackColor = $true;
                          #
                          1. buttonBackupStarten
                          #

                          $buttonBackupStarten.Location = New-Object System.Drawing.Point(12, 76);
                          $buttonBackupStarten.Name = "buttonBackupStarten";
                          $buttonBackupStarten.Size = New-Object System.Drawing.Size(94, 23);
                          $buttonBackupStarten.Text = "Backup Starten";
                          $buttonBackupStarten.UseVisualStyleBackColor = $true;
                          #
                          1. buttonBeenden
                          #

                          $buttonBeenden.Location = New-Object System.Drawing.Point(12, 76);
                          $buttonBeenden.Name = "buttonBeenen";
                          $buttonBeenden.Size = New-Object System.Drawing.Size(94, 30);
                          $buttonBeenden.Text = "Programm Beenden";
                          $buttonBeenden.UseVisualStyleBackColor = $true;
                          $buttonBeenden.Visible = $false
                          #
                          #BackupAufFestplatte
                          #
                          $BackupAufExternenDatenträger.AutoScaleDimensions = new-object System.Drawing.SizeF(6, 13);
                          $BackupAufExternenDatenträger.ClientSize = new-object System.Drawing.Size(377, 127);
                          $BackupAufExternenDatenträger.controls.Add($Logbutton);
                          $BackupAufExternenDatenträger.controls.Add($buttonBackupStarten);
                          $BackupAufExternenDatenträger.controls.Add($Label);
                          $BackupAufExternenDatenträger.controls.Add($progressBar1);
                          $BackupAufExternenDatenträger.controls.Add($Buttonbeenden);
                          $BackupAufExternenDatenträger.Name = "Backup auf Externen Datenträger";
                          $BackupAufExternenDatenträger.Text = "Backup auf Externen Datenrtäger";


                          #############################
                          ### Funktionen Definieren ###
                          #############################

                          1. Wenn ESC gedrückt wird wird das Programm beendet
                          $BackupAufExternenDatenträger.KeyPreview = $True
                          $BackupAufExternenDatenträger.Add_KeyDown({if ($_.KeyCode -eq "Escape") {$BackupAufExternenDatenträger.Close()} })


                          $buttonBackupStarten.Add_Click({ $handler_button1_Click })


                          $Logbutton.Add_Click({
                          if (Test-Path $LogPath){notepad $LogPath}
                          else{[System.Windows.Forms.MessageBox]::Show("Es wurde keine Logdatei gefunden"," ",0)}
                          })


                          $buttonBeenden.add_click({
                          $BackupAufExternenDatenträger.Close()
                          })


                          $BackupAufExternenDatenträger.ShowDialog()

                          }

                          showform


                          Und hier noch die XML-Datei aus der die Pfadeinstellungen ausgelesen wird:

                          <Einstellungen>
                          <Quelle>C:\TEST</Quelle>
                          <Intern>C:\Temp</Intern>
                          <Extern>C:\Temp2</Extern>
                          </Einstellungen>


                          Ich verstehe einfach nicht wo der Fehler liegt nach meinem Verständnis ist alles richtig (Ich bin allerdings auch noch grün hinter den Ohren was Powershell betrifft)

                          Ach ja und noch eine Frage habe ich: Wie bette ich den Quellcode hier so schön ein wie colinardo das oben gemacht hat?


                          Grüße
                          Lenny


                          EDIT: Überall wo die runden Punkte sind oder ne 1 steht sind eigentlich "#" keine ahnung warum das hier falsch angezeigt wird
colinardo
Lösung colinardo 06.12.2013, aktualisiert am 09.12.2013 um 17:01:25 Uhr
Goto Top
Zitat von @Lennyy:
Ich verstehe einfach nicht wo der Fehler liegt nach meinem Verständnis ist alles richtig (Ich bin allerdings auch noch
grün hinter den Ohren was Powershell betrifft)
sieh dir mal das Beispiel genauer an, das ich dir erstellt habe, bei Dir fehlt ja die Hälfte davon...
Ach ja und noch eine Frage habe ich: Wie bette ich den Quellcode hier so schön ein wie colinardo das oben gemacht hat?
das machst du mit Tags zur Codeformatierung.

Grüße Uwe
Lennyy
Lennyy 09.12.2013 um 17:00:42 Uhr
Goto Top
Zitat von @colinardo:

> Zitat von @Lennyy:
> Ich verstehe einfach nicht wo der Fehler liegt nach meinem Verständnis ist alles richtig (Ich bin allerdings auch noch
> grün hinter den Ohren was Powershell betrifft)
sieh dir mal das Beispiel genauer an, das ich dir erstellt habe, bei Dir fehlt ja die Hälfte davon...

Ich habe mir heute dein Beispiel nochmal genauer angesehen und jetzt habe ich ein Funktionierendes Script wo die Progressbar Aktualisiert wird ohne das dass Script hängen bleibt!

Vielen Dank für deine Hilfe - Ohne das Beispiel von dir hättte ich warscheinlich noch ne ganze weile daran gesessen...

Hier noch einmal das Fertigt Script:

#################################################################################
#### Datensicherung mit Robocopy und Fortschritsanzeige in einem eigenen Job  ###
#################################################################################


            ### HIER BEGINNT DAS SCRIPT ###

#region Import the Assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null  
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null  
#endregion

            ### Der Pfad wo das Script ausgeführt wird wird ermittelt  ###
            ### Wird Später zum holen der Quell und Zielpfade benötigt ###

$ScriptPath = [System.Management.Automation.InvocationInfo]$myInv=$myInvocation
$ScriptPath = $myInv.get_MyCommand().Definition
$ScriptPath = Split-Path $ScriptPath -parent


            ###########################################################
            ### Überprüfen ob alle benötigten ordner vorhanden sind ###
            ###########################################################


# Es wird überprüft ob der Ordner für die logdateien vorhanden ist
$Test = Test-Path $SicherungsZielExtern\log
if($Test -eq $false){mkdir $SicherungsZielExtern\log|Out-Null;}


$Test = Test-Path  ("$SicherungsZielExtern\Backup").ToString()   
if($Test -eq $false){mkdir $SicherungsZielExtern\Backup|Out-Null;}

            ############################
            ### Variablen Definieren ###
            ############################

[XML]$xml = get-content $ScriptPath\Einstellungen.Xml
$SicherungsQuelle = $xml.Einstellungen.Quelle
$SicherungsZielExtern = $xml.Einstellungen.Extern


$SicherungPfad = "$SicherungsZielExtern\Backup"  
$LogPath = "$SicherungsZielExtern\LOG\RIS-Backup.log"  
 

#region Generated Form Objects
$BackupAufExternenDatenträger = New-Object System.Windows.Forms.Form
$buttonBackupStarten = New-Object System.Windows.Forms.Button
$progressBar1 = New-Object System.Windows.Forms.ProgressBar
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
$Logbutton = New-Object System.Windows.Forms.Button
$Label = New-Object System.Windows.Forms.label
#endregion Generated Form Objects



#Create synchronized Hashtable-Object for communication between sub-thread and form
$syncHash = [hashtable]::Synchronized(@{})
#set the controls in the hashtable we wish to manipulate
$syncHash.progress = $progressBar1
# initialize second Powershell runspace
$psCmd = [powershell]::Create()
#----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
$handler_button1_Click= 
{
 ### Größe des Backups ermitteln ###
$GesamtGrBackup = 0
$GesamtGrBackup = (Get-ChildItem $SicherungsQuelle -Recurse| Where-Object { `
    ($_.FullName -notlike "$sicherungsquelle\log*") -and  `  
    ($_.FullName -notlike "$sicherungsquelle\Foxfilm*") -and  `  
    ($_.FullName -notlike "$sicherungsquelle\datsic*") -and  `  
    ($_.FullName -notlike "$sicherungsquelle\Backup*") -and  `  
    ($_.FullName -notlike "$sicherungsquelle\ups*")  }    | Measure-Object -property length -sum)   
$GesamtGrBackup = "{0:N2}" -f ($GesamtGrBackup.sum / 1GB) | % {$_ -replace ",","."}  

$prozentanzeiger = 0
$progressBar1.Value = $prozentanzeiger

### Zeitstempel erfassen ###
$vorher = get-date
    
### Backup Starten ###
$label.Text = "Datensicherung läuft..."  

$BackupJob = start-job -Name RIS-Backup -ArgumentList $SicherungsQuelle,$SicherungPfad,$LogPath -scriptblock{ 
    $LogPath = $args[2]
    $Robocopy = robocopy $args $args[1] /mir /V /XD "Foxfilm" "log" "datsich" "backup" "upd" /LOG:$LogPath }  
       
### Zeitstempel erfassen ###
$vorher = get-date      

### 1 Sekunde warten ### WENN NICHT GEWARTET WIRD KANN ES PROBLEME MIT DER PROGRESSBAR GEBEN !
#ping -n 1 localhost | out-null


        # create new runspace to run our sub-thread in
        $newRunspace =[runspacefactory]::CreateRunspace()
        $newRunspace.ApartmentState = "STA"  
        $newRunspace.ThreadOptions = "ReuseThread"            
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)  
        $newRunspace.SessionStateProxy.SetVariable("SicherungPfad",$SicherungPfad)  
        $newRunspace.SessionStateProxy.SetVariable("BackupJob",$BackupJob)  
        $newRunspace.SessionStateProxy.SetVariable("vorher",$vorher)  
        $newRunspace.SessionStateProxy.SetVariable("label",$label)  
        $newRunspace.SessionStateProxy.SetVariable("buttonBackupStarten",$buttonBackupStarten)  
        $newRunspace.SessionStateProxy.SetVariable("buttonBeenden",$buttonBeenden)  
        $newRunspace.SessionStateProxy.SetVariable("GesamtGrBackup",$GesamtGrBackup)  
        $newRunspace.SessionStateProxy.SetVariable("psCmd",$psCmd)  



        $psCmd.AddScript({


                                    ### Nachdem das Backup gestartet wurde, ### 
                                    ### Wird hier der Fortschritt berechnet ###
                                    while ($BackupJob.State -eq "Running")  
                                    {

                                    

                                        $Kopiert = (Get-ChildItem $SicherungPfad -Recurse | Measure-Object -property length -sum) 

                                        $Kopiert = "{0:N2}" -f ($Kopiert.sum / 1GB) | % {$_ -replace ",","."}  
                                        $Prozentanzeiger = 0
                                        $Prozentanzeiger = $Kopiert/$GesamtGrBackup*100
                                        "{0:n1}" -f $Prozentanzeiger    

                                        $syncHash.progress.Value = $prozentanzeiger
                                     }
                                     
                                     

                                                if ($BackupJob.State -eq "Completed"){  
                                                    ### Zeitstempel erfassen ###
                                                    $nachher = get-date
                                                    $zeit = "{0:N3}" -f ($nachher - $vorher).TotalMinutes  

                                                    $label.Text = "Datensicherung Abgeschlossen in $zeit Minuten"; $label.ForeColor = "Green"  
                                                
                                                    #[System.Windows.Forms.MessageBox]::Show("dümdüm"," ",0)  
                                                                                     }


                                                ### Der Button backuo Starten wird ausgeblendet und Backup beenden eingeblendet        
                                                $buttonBackupStarten.Visible = $false
                                                $buttonBeenden.Visible = $true
                                                #$psCmd.Stop() 


                         })
        $psCmd.Runspace = $newRunspace
        #start the runspace
        $data = $psCmd.BeginInvoke()


}

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



$handler_Logbutton_Click ={
    if (Test-Path $LogPath){notepad $LogPath}
    else{[System.Windows.Forms.MessageBox]::Show("Es wurde keine Logdatei gefunden"," ",0)}  
                            }



$handler_ButtonBeenden_Click ={
$BackupAufExternenDatenträger.Close()
                                }


            ###########################################################
            ########## Ab hier wird die Windows.Form Designt ##########
            ###########################################################


# Init Form
#
$BackupAufExternenDatenträger.width = 546
$BackupAufExternenDatenträger.height = 163
$BackupAufExternenDatenträger.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen 
#
#progressBar1
#
$progressBar1.Location = new-object System.Drawing.Point(12, 47);
$progressBar1.Name = "progressBar1";  
$progressBar1.Size = new-object System.Drawing.Size(353, 23);
$progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0
$progressBar1.TabIndex = 0
#
#Label
#
$Label.AutoSize = $true;
$Label.Location = new-object System.Drawing.Point(71, 19);
$Label.Name = "Label";  
$Label.Size = new-object System.Drawing.Size(226, 25);
$Label.Text = "Backup NICHT Gestartet";  
#
#Logbutton
#
$Logbutton.Location = new-object System.Drawing.Point(254, 76);
$Logbutton.Name = "Logbutton";  
$Logbutton.Size = new-object System.Drawing.Size(111, 23);
$Logbutton.Text = "Logdatei Anzeigen";  
$Logbutton.UseVisualStyleBackColor = $true;
$Logbutton.add_Click($handler_Logbutton_Click)
#
# buttonBackupStarten
#
$buttonBackupStarten.Location = New-Object System.Drawing.Point(12, 76);
$buttonBackupStarten.Name = "buttonBackupStarten";  
$buttonBackupStarten.Size = New-Object System.Drawing.Size(94, 23);
$buttonBackupStarten.Text = "Backup Starten";  
$buttonBackupStarten.UseVisualStyleBackColor = $true;
$buttonBackupStarten.TabIndex = 1
$buttonBackupStarten.add_Click($handler_button1_Click)
$buttonBackupStarten.DataBindings.DefaultDataSourceUpdateMode = 0
#
# buttonBeenden
#
$buttonBeenden = new-object System.Windows.Forms.Button
$buttonBeenden.Location = New-Object System.Drawing.Point(12, 76);
$buttonBeenden.Name = "buttonBeenen";  
$buttonBeenden.Size = New-Object System.Drawing.Size(94, 30);
$buttonBeenden.Text = "Programm Beenden";  
$buttonBeenden.UseVisualStyleBackColor = $true;
$buttonBeenden.Visible = $false
$buttonBeenden.add_click({$handler_ButtonBeenden_Click})
#
#BackupAufFestplatte
#
$BackupAufExternenDatenträger.AutoScaleDimensions = new-object System.Drawing.SizeF(6, 13);
$BackupAufExternenDatenträger.ClientSize = new-object System.Drawing.Size(377, 127);
$BackupAufExternenDatenträger.controls.Add($Logbutton);
$BackupAufExternenDatenträger.controls.Add($buttonBackupStarten);
$BackupAufExternenDatenträger.controls.Add($Label);
$BackupAufExternenDatenträger.controls.Add($progressBar1);
$BackupAufExternenDatenträger.controls.Add($Buttonbeenden);
$BackupAufExternenDatenträger.Name = "Backup auf Externen Datenträger";  
$BackupAufExternenDatenträger.Text = "Backup auf Externen Datenrtäger";  
$BackupAufExternenDatenträger.DataBindings.DefaultDataSourceUpdateMode = 0
$BackupAufExternenDatenträger.MaximizeBox = $False


#Save the initial state of the form
$InitialFormWindowState = $BackupAufExternenDatenträger.WindowState
#Init the OnLoad event to correct the initial state of the form
$BackupAufExternenDatenträger.add_Load($OnLoadForm_StateCorrection)
#----- Show Form
$BackupAufExternenDatenträger.ShowDialog() | out-null



UND die Einstellungen.xml:

<Einstellungen>
  <Quelle>C:\Temp</Quelle>
  <Intern>C:\Temp3</Intern>
  <Extern>C:\temp2</Extern>
</Einstellungen>