Autor |
Beitrag |
DJMesche
Hält's aus hier
Beiträge: 11
|
Verfasst: Mi 29.06.11 17:38
Einen wunderschönen Guten Tag
ich hab ein kleines Problem unter Windows-Forms und hab jetz schon eine ganze Weile im Internet gesucht aber anscheinend immernoch nichts passendes gefunden.
Erstmal zum Problem:
Ich habe ein kleines Backup-Tool für meinen Bruder geschrieben - dieses sucht Daten aus, erstellt eine Liste mit allen zu speichernden Dateien -> und diese werden dann (jede einzeln) einer Zip mit PW hinzugefügt.
Soweit so gut - das Programm an sich geht ja, aaaber:
Ich hab eine Schleife, welche die aktuelle Datei beinhaltet (geht die Liste durch),
in einem Label die Datei ausgibt/geben soll, und diese Zip't.
Leider geht das Zippen wahrscheinlich zu schnell und das Label wird nicht refresht....
Ich habe es schon mit Threading versucht - aber irgendwie es nicht so gebaut bekommen wie ich es brauch (hab davon kaum Ahnung).
Außerdem bringen Application.DoEvents und Update, sowie Refresh-Methoden und auch Sleep(xx) nichts. (Timer hat übrigens auch nicht geklappt)
Bitte helft mir! Es wäre wundervoll
Hier noch der benötigte Teil Code, um zu wissen, was ich mache
C#-Quelltext 1: 2: 3: 4: 5: 6:
| private void text(string p_text) { if (p_text.Length > 50) p_text = p_text.Substring(0, 50); lblAktuell.Text = p_text; Application.DoEvents(); } |
und hier die schleife
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| List<string[]> FileListGesamt = DateiListe.fileListAll; foreach (string[] arrFile in FileListGesamt) { ZipDatei.ZipFiles(arrFile[0], arrFile[1]); text("Verarbeite..." + arrFile[0]); p1.Value += Convert.ToInt32(arrFile[2]); } |
Ich bin euch jetzt schon sehr Dankbar und gespannt auf eure Antworten
Wenn jemand was zum Thema Threading sagt, dann bitte ein darauf bezogenes Schnipsel machen - wäre sehr toll 
|
|
Chiyoko
      
Beiträge: 298
Erhaltene Danke: 8
Win 98, Win Xp, Win 10
C# / C (VS 2019)
|
Verfasst: Mi 29.06.11 17:45
Nein das Label zeichnet sich selber, wenn du es richtig machst.
Ich denke, du musst nur das Label in die Schleife packen.
Threading brauchst du auf jedenfall nicht.
|
|
DJMesche 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mi 29.06.11 17:57
Chiyoko hat folgendes geschrieben : | Nein das Label zeichnet sich selber, wenn du es richtig machst.
Ich denke, du musst nur das Label in die Schleife packen.
Threading brauchst du auf jedenfall nicht. |
Danke für die schnelle Antwort - aber das bringt auch nichts - das hatte ich vorher so,
und das Label wurde nicht mehr gezeichnet, bis Schleife beendet ist.
Die CPU-Auslastung liegt auch bei ziemlich genau 100%
Danke
|
|
DJMesche 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mi 29.06.11 18:27
So jetzt habe ich glaube ich das Zippen in einen Thread ausgelagert
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| public void Zip_Threading(string Fil, string inputFolderPath) { p_File = Fil; p_InputFolderPath =inputFolderPath; Thread Zippy = new Thread(ZipFiles_Threading); Zippy.Priority = ThreadPriority.BelowNormal; Zippy.IsBackground = true; Zippy.Start(); Zippy.Join();
} |
und es hat sich mit einem Sleep(1) schonmal der Text des Labels geändert - jedoch immernoch nur sporadisch.
Was mich auch erstaunt - das Programm braucht immernoch 100% CPU (98-100) obwohl Zippy.Priority = ThreadPriority.BelowNormal;
Danke 
|
|
Trashkid2000
      
Beiträge: 561
Erhaltene Danke: 137
|
Verfasst: Mi 29.06.11 18:55
Hallo,
Chiyoko hat folgendes geschrieben : | Nein das Label zeichnet sich selber, wenn du es richtig machst. [...]
Threading brauchst du auf jedenfall nicht. |
Naja, das sehe ich anders. Denn bei Funktionen, die längere Zeit dauern blockiert die GUI nunmal. Da hilt es auch nichts, das Zuweisen des Textes an das Label in die Schleife zu packen. Und das DoEvents() vergiss auch schnell wieder!
Der richtige Weg ist es wirklich, das Ganze in einen eigenen Thread auszulagern. Und das Aktualisieren der Form dann per Invoke zu lösen (was auch nicht wirklich anders möglich ist).
So, nun zu dem geposteten Code (den ich nicht so ganz verstehe):
Du hast eine Methode, der Du Werte übergibst, und in der Du dann einen neuen Thread startest.
Also die Methode "ZipFiles_Threading". Aber dem Thread wird absolut nichts übergeben.
Wo sich die Frage stellt: Was macht dieser überhaupt?? Wenn ihm keine Daten übergeben werden, was er zu zippen hat.
Und wie hast Du das mit der Aktualisierung der Form gelöst, wenn Du schreibst, dass die Aktualisierung nur sporadisch klappt?
Etwas mehr Code wäre an dieser Stelle nicht schlecht.
//edit: was das Thread.Join() hier zu suchen hat, weiß ich auch nicht. Du wartest doch auf keinen anderen Thread...
LG,
|
|
DJMesche 
Hält's aus hier
Beiträge: 11
|
Verfasst: Mi 29.06.11 19:11
Also ich habe die Parameter für die Zip-Funktion auf öffentliche Variablen in der Klasse zugewiesen.
C#-Quelltext 1: 2:
| public string p_File; public string p_InputFolderPath; |
Diese werden der Methode, welche den Thread erzeugt, übergeben und dann zugewiesen
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| public void Zip_Threading(string Fil, string inputFolderPath) { p_File = Fil; p_InputFolderPath =inputFolderPath; Thread Zippy = new Thread(ZipFiles_Threading); Zippy.Priority = ThreadPriority.BelowNormal; Zippy.IsBackground = true; Zippy.Start(); Zippy.Join();
} |
Die Methode, die der Thread startet, nimmt nun die öffentlichen Variablen zum Arbeiten.
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| private void ZipFiles_Threading() { string parent = Directory.GetParent(p_InputFolderPath).ToString(); parent = Directory.GetParent(parent).ToString();
int TrimLength = parent.Length; TrimLength += 1; FileStream ostream; byte[] obuffer;
ZipEntry oZipEntry; oZipEntry = new ZipEntry(p_File.Remove(0, TrimLength)); oZipStream.PutNextEntry(oZipEntry);
if (!p_File.EndsWith(@"/")) { ostream = File.OpenRead(p_File); obuffer = new byte[ostream.Length]; ostream.Read(obuffer, 0, obuffer.Length); oZipStream.Write(obuffer, 0, obuffer.Length); } } |
So...aufgerufen wird die Ganze Sache wie schon erwähnt in der Schleife
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| List<string[]> FileListGesamt = DateiListe.fileListAll; foreach (string[] arrFile in FileListGesamt) { text("Verarbeite..." + arrFile[3]); p1.Value += Convert.ToInt32(arrFile[2]); Thread.Sleep(1); ZipDatei.Zip_Threading(arrFile[0], arrFile[1]); } |
leider kommt jetz immer bei der Hälfte des Backups eine unbehandelte Ausnahme beim zippen - weis nicht ob das jetzt von dem Thread kommt und kann den Fehler auch niiht wirklich im Debugging erkennen..aber das ist erstmal fünftens
Danke
//edit: Das Join brauche ich, da dass Programm sonst unmittelbar nach Starten des Threads die nächste Datei schickt, was zum Überlauf führt
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 29.06.11 21:56
Hallo DJMesche,
Multithreading ist nicht mal gerade so nebenbei zu programmieren, sondern man muß genau wissen, wie man die einzelnen Funktionalitäten benutzt. Lies dir mal diesen Artikel bei www.mycsharp.de/wbb2...d.php?threadid=40787 durch.
Als Alternative zu eigenen Threads würde ich dir hier sowieso zum BackgroundWorker raten, so daß du im ProgressChanged-Ereignis den Fortschritt darstellen kannst (z.B. in einer ProgressBar).
Und dein genereller Fehler ist, daß du die gesamte Schleife in einem eigenen Thread (bzw. in der BackgroundWorker.DoWork-Methode) abhandeln mußt.
Und Trashkid2000 hat bzgl. Join() völlig Recht - das ergibt überhaupt keinen Sinn, s.a. Coding Styles Horrors (du bist also nicht der einzige  )
|
|
DJMesche 
Hält's aus hier
Beiträge: 11
|
Verfasst: Do 30.06.11 00:13
 Vielen Dank
ich wollte mich eigentlich erfolgreich vor den kleinen Euro-Jobbern hüten, die jetzt in meinem Programm arbeiten - aber Sie tun das gewissenhaft und ordentlich
Jetzt aber ich aber nun wieder ein Frage zu den BackgroundWorkers:
Ich löse das Update meiner Progressbar und meines Labels jetzt so, dass ein Timer aller 500 ms
den Progress des BackgroundWorkers ändert. In diesem Event schreibe ich nun in die ProgressBar sowie in das Label,
musste hierfür aber extra öffentliche Variablen deklarieren, damit das Label auch den Text irgendwoher nehmen kann.
Leider konnte ich in keiner Weise Text aus dem BackgroundW_DoWork in ein Textfeld schreiben, wegen den unterschiedlichen Threads.
Aber wie löst man das profimäßig?
Ihr seit echt in Ordnung  Es gibt selten Foren, wo man gleich normal behandelt wird
Bye und Gute Nacht
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Do 30.06.11 09:50
Hallo und guten Morgen DjMesche,
es stimmt, aus der DoWork-Methode heraus darfst du nicht direkt auf GUI-Elemente zugreifen (da diese in einem eigenen Thread läuft).
Du kannst aber der ReportProgress-Methode ein beliebiges Objekt (als 2. Parameter) übergeben und diesen dann im ProgressChanged-Ereignis über e.UserState abfragen:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { string text = "show me"; backgroundWorker.ReportProgress(progress, text); }
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { string text = e.UserState as string; if (text != null) { label.Text = text; } } |
Nur das mit dem Timer verstehe ich nicht, denn diesen benötigst du eigentlich nicht (da du die gesamte Schleife des Zippens in der DoWork-Methode durchführen solltest und nur in der ProgressChanged dann auf die GUI-Elemente zugreifst).
|
|
DJMesche 
Hält's aus hier
Beiträge: 11
|
Verfasst: Do 30.06.11 14:15
Hallo und Danke für die Antwort
Naja wahrscheinlich hab ich einfach mal zu wenig Erfahrung mit der Verwendung des Backgroundworkers,
aber ich finde es in meinem Beispiel bisschen schlecht programmiert jetzt - aber es geht
Ich erklär mal - und wer Lust und Laune hat, kann ja ein paar Tipps geben.
Also erstes Stört mich, dass ich jetzt alle Klassen öffentlich deklarieren musste
(Klassen, die in bw1_DoWork() verwendet werden, jedoch in Form_Shown() deklariert werden müssen, um diesen Werte zuzuweisen:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| public partial class Form1 : Form { private System.ComponentModel.BackgroundWorker bw1; private FileList DateiListe; private const string softText = "AutoBackup sichert Dateien..."; private string pfadIniDatei = Application.StartupPath + @"\backup.config.ini"; private ZipUtil ZipDatei; private string infoAktuell; |
und Werte werden hier zugewiesen (und Zip-Datei erstellt):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| private void Form1_Shown(object sender, EventArgs e) {
infoAktuell = "Einlesen der Dateien: " + pfadDesktop; DateiListe.SetFiles(pfadDesktop); infoAktuell = "Einlesen der Dateien: " + pfadOutlook; DateiListe.SetFiles(pfadOutlook); infoAktuell = "Einlesen der Dateien: " + pfadRepdocBackup; DateiListe.SetDBBackup(pfadRepdocBackup);
infoAktuell = "Erzeugen der Backup-Datei: " + pfadZielDatei; ZipDatei = new ZipUtil(pfadZielDatei, "gbr010708"); ZipDatei.CreateZipFile();
p1.Value = 0; p1.Minimum = 0; p1.Maximum=DateiListe.sizeFileListAll+1; System.Timers.Timer t1 = new System.Timers.Timer(500); t1.AutoReset = true; t1.Elapsed += new System.Timers.ElapsedEventHandler(timer_tick); t1.Enabled = true;
bw1.RunWorkerAsync(); } |
Nachdem alle Werte ordentlich an die Klassen gegeben wurden, die Zip-Datei erstellt wurde, steht nun das Befüllen aus,
was mit dem Backgroundworker passiert
der wird am Ende mit: bw1.RunWorkerAsync(); gestartet.
Dieser Zippt nun, und verwendet die Eigenschaften der schon instanzierten Klasse
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| private void bw1_DoWork(object sender, DoWorkEventArgs e) { List<string[]> FileListGesamt = DateiListe.fileListAll; foreach (string[] arrFile in FileListGesamt) { infoAktuell = "Verarbeite..." + arrFile[3]; ZipDatei.ZipFiles_ExtendSize(arrFile[0], arrFile[1]);
}
} |
Jetzt wird aller 500 ms der Timer ausgelöst und startet das Event ProgressChanged - und greift ebenfalls auf die schon am Anfang instanzierte Zip-Klasse zu
C#-Quelltext 1: 2: 3: 4:
| private void timer_tick(object sender, System.Timers.ElapsedEventArgs e) { bw1.ReportProgress(ZipDatei.alreadyReadBytes); } |
Beim Event ProgressChanged wird nun in das Label und in die Progressbar geschrieben.
Am Ende des Spektakels wird mit ProgressComplete() die Zip Datei abgeschlossen und das Programm beendet.
 Sorry das ich soviel schreib grade - aber....wisst Ihr was ich mein mit "unprofessionell" bzw durcheinander
vielleicht hat ja jemand ein paar Kniffs
Grüße
|
|
|