C# - Klasse DirectoryCopy incl. Thread & Status

yooti

Grünschnabel
Hallo @ all:

Vorwort
Ich hab erst letztes jahr angefangen zu programmieren und bis jetzt habe ich mir alles selber beigebracht (lesen und testen). jetzt bräuchte ich doch etwas nachhilfe.

ich bin dabei eine win from anwendung für mich zu basteln, die einen ordner von einem anderen rechner im lan copieren soll.

ich möchte das problem in etwas so lösen !

in meiner "form1.cs" hab ich ein Button der den Copiervorgang starten soll:

Code:
string Src = @"\\Recher2\Download"
string Dst = @"D:\test"

private void Button_download_Click(object sender, EventArgs e)
        {
            DirectoryCopy copy = new DirectoryCopy();
            copy.copyDirectory(Src,Dst)
        }

so soll eigentlich der copiervorgang des ordners angeschups werden

nun möchte ich aber nicht das der komplette code in der form1.cs drin steht sondern in der z.b. "copy.cs" ausgelagert ist.

wenn ich jetzt den button anklicke soll der komplette code ablauf in der "copy.cs"
stadtfinden.
die copy.cs sollte in etwa so aussehen

Code:
public class DirectoryCopy
    {

        public void copyDirectory(string Src,string Dst)
        {
                      // anweisung zum copieren
        }

    }

bis hier hin hab ich mir schon alles so zusammen gebastelt wie ich es brauche und es funktioniert schon sehr gut. ab jetzt fangen meine fragen an!

ich möchte diese klasse so gestalten das gleichzeitig ein thread start erfolgt und auch eine metode mit drin ist die mit protokoliert vieviel daten schon kopiert worden sind. wichtig ist auch das ich die möglichkeit habe aus der "form1.cs" heraus einen
copy abbruch button zuclicken der den kopiervorgang abbricht bzw.den thread

ich hoffe ihr habt mich soweit erst mal verstanden wenn nicht einfach nochmal nachfragen

im moment bin ich erst mal an einem guten und klar strukturierten code gerüst interessiert wo ich später den code einfügen kann.

ich wäre für gute vorschläge sehr dankbar


mfg yooti
 
C# - DirectoryCopy incl. backgroundWorker & Status

na ja vieleicht hatte ich mich ein wenig unverständlich ausgedrückt und keiner weiß was ich wirklich wollte! macht aber nichts, mit meinem problem bin ich meinem ziel schon etwas näher gekommen

meine code gerüst was ich gesucht habe ist ein normaler button und der "backgroundWorker"

Code:
        private void button_start_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }
        
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {            
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
        }

soweit ist ja schon mal alles gut. Nun geht es darum meinen code den ich habe einzufügen aber auch so das es so funktioniert wie mann das erwartet

Code:
        string Src = @"E:\Fun";
        string Dst = @"C:\Test";
        long SrcFolderSize = 0;
        long bytesTransfered = 0;
        
        private void button_start_Click(object sender, EventArgs e)
        {
            SrcFolderSize = myToolBox.GetFolderSize(Src);
            label1.Text = Convert.ToString(SrcFolderSize);
            
            backgroundWorker1.RunWorkerAsync();
        }

Code:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            CopyFolder(Src, Dst);
        }

Code:
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }


und das ist meine "CopyFolder" Metode

Code:
        private static void CopyFolder(string sourceFolder, string targetFolder)
        {
            // Verzeichnis am Zielort erstellen
            Directory.CreateDirectory(targetFolder);

            // Alle Dateien kopieren
            string[] files = Directory.GetFiles(sourceFolder);
            foreach (string file in files)
            {
                string target = Path.Combine(targetFolder, Path.GetFileName(file));
                File.Copy(file, target, true);
            }

            // Alle Unterverzeichnisse kopieren
            string[] subSourceFolders = Directory.GetDirectories(sourceFolder);
            foreach (string subSourceFolder in subSourceFolders)
            {
                string subTargetFolder = subSourceFolder.Replace(sourceFolder, targetFolder);
                CopyFolder(subSourceFolder, subTargetFolder);
            }
        }

Bis jetzt funktioniert der code und das tool kopiert auch alles fehlerfrei - nur kann ein abbruch button mit dem aufbau nicht funktionieren und wie ich das mit der progressBar löse hab ich auch leider keine idee

Die backgroundWorker1_DoWork möchte ich in etwa nach diesem chema lösen

Code:
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                for (        ; kopierteBytes <= SrcFolderSize; nächsteDateiKopieren  )
                {
                    // Abfragen ob abgebrochen werden soll
                    if (backgroundWorker1.CancellationPending)
                    {
                        // Angeben ob Abgebrochen wurde
                        e.Cancel = true;
                        // und raus aus der Metohde
                        return;
                    }
                    // FolderCopy ausführen
                    ...

                    // Thread kurz anhalten
                    Thread.Sleep(200);

                    // den Fortschritt melden
                    int percent = (int)((kopierteBytes / (float)SrcFolderSize) * 100);
                    backgroundWorker1.ReportProgress(percent, kopierteBytes);
                }

                // das Ergebnis zurückgeben
                e.Result = result;

            }
            catch (Exception ex)
            {
                throw new ArgumentException(" " + ex.Message);
            }

        }


Ich danke schon mal im vorraus für das durchlesen und hoffe weiter hin das ich ein wenig hilfe von euch bekommen kann

mfg
yooti
 
Erstmal vorweg warum Thread.Sleep?

Dann zum progress, wo genau besteht das problem?

Und zum Abbruch, beim BackgroundWorker müsste es eine Eigenschaft ähnlich WorkerSupportsCancelation oder so. Diese Auf true setzen.

Beim Click auf den Abbruch Button rufst du dann meinWorker.CancelAsync() auf.
Im DoWork prüfst du nun ob meinWorker.CancellationPending true ist.
Wenn ja wurde CancelAsync() aufgerufen und du beendest deine Schleife zum Beispiel durch ein break.

(Seh grad sowas ähnliches haste ja schon drin :rolleyes: )

Wo ist dann das problem :confused:

Nachtrag:
im DoWork brauchst du kein Try Catch
im RunworkerComplete Ereignis des BackgroundWorker gibt es im EventArgs eine EIgenschaft Error welches dir die Exception zurück gibt.
 
erst mal danke das sich du dir die mühe gemacht hast :)


ich hab mir aus meinem buch ein tutorial durch gearbeitet was wie folgt aussieht !

Code:
        private void button_start_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync(textBox_maxNummer.Text);

            button_start.Enabled = false;
            button_abbruch.Enabled = true;            
        }

        private void button_abbruch_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                long maxNumber = 0;

                maxNumber = Convert.ToInt64(e.Argument);

                long result = 0;
                for (long number = 1; number <= maxNumber; number++)
                {
                    // Abfragen ob abgebrochen werden soll
                    if (backgroundWorker1.CancellationPending)
                    {
                        // Angeben ob Abgebrochen wurde
                        e.Cancel = true;
                        // und raus aus der Metohde
                        return;
                    }
                    // Berechnung ausführen
                    result += number;

                    // Thread kurz anhalten
                    Thread.Sleep(200);

                    // den Fortschritt melden
                    int percent = (int)((number / (float)maxNumber) * 100);
                    backgroundWorker1.ReportProgress(percent, number);
                }

                // das Ergebnis zurückgeben
                e.Result = result;
            }
            catch (Exception ex)
            {
                throw new ArgumentException("Das Argument der Thread-Metode " + "muße eine positive Ganzzahl sein: " + ex.Message);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // ProzessBar und Label Aktualisieren
            progressBar1.Value = e.ProgressPercentage;
            label_result.Text = e.UserState.ToString();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                // In der Thread metode ist ein Fehler aufgetreten
                MessageBox.Show(e.Error.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else if (e.Cancelled == true)
            {
                // Thread wurde durch Benutzer Abgebrochen
                label_result.Text = "Abgebrochen";
            }
            else
            {
                // Thread wurde normal beendet
                label_result.Text = "Thread wurde ausgeführt";
            }

            button_start.Enabled = true;
            button_abbruch.Enabled = false;
        }

wenn ich im backgroundWorker1_DoWork den Thread.Sleep(200) auf Thread.Sleep(0) setze friert die ganze form ein und deswegegen der Thread.Sleep

wie schon mal oben gesagt, ich hab erst letzes jahr angefangen zu programmieren und muß mir für alles gute tutorials suchen die ich dann auch verstehe

das problem was ich habe ist das ich den code

Code:
// Verzeichnis am Zielort erstellen
            Directory.CreateDirectory(targetFolder);

            // Alle Dateien kopieren
            string[] files = Directory.GetFiles(sourceFolder);
            foreach (string file in files)
            {
                string target = Path.Combine(targetFolder, Path.GetFileName(file));
                File.Copy(file, target, true);
            }

            // Alle Unterverzeichnisse kopieren
            string[] subSourceFolders = Directory.GetDirectories(sourceFolder);
            foreach (string subSourceFolder in subSourceFolders)
            {
                string subTargetFolder = subSourceFolder.Replace(sourceFolder, targetFolder);
                CopyFolder(subSourceFolder, subTargetFolder);
            }

in den backgroundWorker1_DoWork reinbringen möchte und das auch so, das ein abbruch zu jeder möglich ist. desweiteren möchte ich das ein fortschritt in den backgroundWorker1_ProgressChanged übergeben wird.

und ich hab echt kein plan wie ich das machen soll

mfg
yooti
 
das mit dem einfrieren hängt ev. damit zusammen das Progressbar zu oft neugezeichnet werden soll, kommentier am besten mal ReportProgress aus, dann sollte das Fenster nicht mehr einfrieren.

Ansonsten kann du den unteren Teil des Codes so nehmen wie er ist und ins DoWork kopieren.
Bei den foreach Schleifen prüfst du dann ob CancelationPending auf true ist und rufst break auf zum verlassen der Schleife.

Nachtrag:
Seh gerade CopyFolder wird denke ich mal rekursiv aufgerufen :D
Dann könntest du ev. eine Instanz deinen BackgroundWorkers als Parameter mit übergeben.
 
Zuletzt bearbeitet:
sorry aber ich kann dir nicht ganz folgen ! egal wie ich mein code drehe es funkitioniert nicht. irgendwie steig ich noch nicht ganz dahinter

wärst du mal bitte so nett und kannst mir mal ein beispiel geben wie du das machen würdest !?

mfg yooti
 
C#:
        private void button_start_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync(new Args(sourcePfad, destPfad));
        }
        
        private void button_abbruch_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Args a=(Args)e.Argument;
            CopyFolder((BackgroundWorker)sender, a.Source, a.Destination);
        }

        //Methode CopyFolder mit weiterem Parameter BackgroundWorker
        private static void CopyFolder(BackgroundWorker bw, string sourceFolder, string targetFolder)
        {
            // Verzeichnis am Zielort erstellen
            Directory.CreateDirectory(targetFolder);

            // Alle Dateien kopieren
            string[] files = Directory.GetFiles(sourceFolder);
            foreach (string file in files)
            {
                if(bw.CancellationPending)
                         break; //CanelAsync wurde aufgerufen, Schleife verlassen

                string target = Path.Combine(targetFolder, Path.GetFileName(file));
                File.Copy(file, target, true);
            }

            // Alle Unterverzeichnisse kopieren
            string[] subSourceFolders = Directory.GetDirectories(sourceFolder);
            foreach (string subSourceFolder in subSourceFolders)
            {
                if(bw.CancellationPending)
                         break; //CanelAsync wurd aufgerufen, Schleife verlassen

                string subTargetFolder = subSourceFolder.Replace(sourceFolder, targetFolder);
                CopyFolder(bw, subSourceFolder, subTargetFolder);
            }
        }

        //Hilfsstruct für Übergabe der Arguments an BackgroundWorker (Struct da hier keine Referenz gehalten wird, Vermeidung von Thread übergreifenden Zugriffen)
        private struct Args
        {
            public Args(string source, string destination)
            {
                this.Source=source;
                this.Destination=destination;
            }
            public readonly string Source;
            public readonly string Destination;
        }

(ungetesteter Code)
 
Zuletzt bearbeitet von einem Moderator:
danke - dein code funktioniert schon mal und hab es auch zum laufen bekommen !

mir sind mehrere dinge aufgefallen

1.
wenn ich auf start klicke,werden die ordner und dateien kopiert und wenn ich dann irgenwann auf abbruch klicke kopiert er die datei erst zu ende und bricht dann erst den backgroundWorker ab. das soll aber nicht so sein. weil ich möchte damit große datein (ca. bis 5GB eine Datei) übers LanPartyNetzwerk kopieren. wie man die netzwerke so kennt dauert das ewig, und da kann man nicht warten bis die datei fertig kopiert ist, bis er den rest abbricht.

2.
desweiteren fehlt mir mir noch die status anzeige
die ordner größe vom sourceFolder kann ich mir geben lassen ( kein Problem )
dei ordner größe vom destFolder kann ich mir nicht geben lassen weil aus vielen unterschiedlichen sourceFolders in die eine destFolder kopiert werden soll

Lösungsansatz
wenn mann einen FileStream einsetzt mit einem "byte[] puffer = new byte[512];"
sollte mann doch die beiden probleme doch auch in den griff bekommen oder ? ? ?

// --- Hilfsstruct --------------------------

dein Hilfsstruct habe ich zur kenntniss genommen nur verstehe ich nicht ganz was es mit der "Vermeidung von Thread übergreifenden Zugriffen" auf sich hat - ich kann mir nichts drunter vorstellen

mfg
yooti
 
danke - dein code funktioniert schon mal und hab es auch zum laufen bekommen !

mir sind mehrere dinge aufgefallen

1.
wenn ich auf start klicke,werden die ordner und dateien kopiert und wenn ich dann irgenwann auf abbruch klicke kopiert er die datei erst zu ende und bricht dann erst den backgroundWorker ab. das soll aber nicht so sein. weil ich möchte damit große datein (ca. bis 5GB eine Datei) übers LanPartyNetzwerk kopieren. wie man die netzwerke so kennt dauert das ewig, und da kann man nicht warten bis die datei fertig kopiert ist, bis er den rest abbricht.
Dann musst du die Datei wohl über StreamReader oder so selbst auslesen und beschreiben.
2.
desweiteren fehlt mir mir noch die status anzeige
die ordner größe vom sourceFolder kann ich mir geben lassen ( kein Problem )
dei ordner größe vom destFolder kann ich mir nicht geben lassen weil aus vielen unterschiedlichen sourceFolders in die eine destFolder kopiert werden soll

Lösungsansatz
wenn mann einen FileStream einsetzt mit einem "byte[] puffer = new byte[512];"
sollte mann doch die beiden probleme doch auch in den griff bekommen oder ? ? ?
Würde ich nicht machen, wenn dann würde ich in bstimmten abständen einfach die aktuelle Datei anzeigen die kopiert wird.
Warum in bestimmten abständen, wenn du zu oft das Formular zum zeichnen aufforderst (durch Anzeige des Dateinamens) friert das Fenster wieder ein.

// --- Hilfsstruct --------------------------

dein Hilfsstruct habe ich zur kenntniss genommen nur verstehe ich nicht ganz was es mit der "Vermeidung von Thread übergreifenden Zugriffen" auf sich hat - ich kann mir nichts drunter vorstellen

mfg
yooti
Dürfte dich im jetzigen Stadium auch nicht sonderlich groß treffen.
Im Prinzip geht es darum das der Hintergrund Thread vom BackgroundWorker nicht auf eine Klassen instanz vom Main Thread draufzugreift.
 
Zurück