Moin, In einer C#-GUI wird extrerner Prozess gestartet, der einige Zeit braucht (Videokonvertierung á la ffmpeg - ähnlich dvdstyler) und somit die GUI blockiert, Um dem Benutzer zu signalisieren, dass das Programm noch läuft, würde ich gerne einen Progressbar implementieren, und natürlich auch die Möglichkeit der Bedienung zu erhalten. Jetzt stellt sich mir die Frage, wie ich den externen Prozess dazu bringe, den progessbar zu manipulieren. Das OutputDataReceived-Event funktioniert soweit, dass ich mir die Statusinformationen des ffmepg auslesen und in einer Messagebox anzeigen lassen kann. Muss ich den externen Prozess als eigenen Thread aufrufen bzw. mit Hilfe des Backgroundworkers laufen lasse? Wie mache ich das? Die von mir gefundenen Beispiele im Internet behandeln Threads alle sehr allgemein, und manupulieren immer nur Progressbars ohne einen externen Prozess. Gäbe es ggf. noch eine andere Lösung? Pribzipell könnte auch ein Fenster geöffnet werden, in dem es den Fortschrittsbalken und einn Knopf zum Abbrechen der Aktion vorhanden ist. Dadürfte aber mMn wieder das threadübergreifende Zugriffsproblem bestehen. Vielen Dank im Voraus!
wie startest du denn das externe Programm? Das läuft normalerweise schon in einem Extra Thread - geht gar nicht anders, weil es ja an andere Programm ist. Wenn du nicht gerade auf das Programm wartest, sollte deine GUI auch nicht blockieren.
Peter II schrieb: > Wenn du nicht gerade auf das Programm wartest, sollte deine GUI auch > nicht blockieren. Ich warte mit "WaitForExit()" auf das Ende des extewrnen Prozesses, weil danach noch mehr im Programmablauf kommt. Prinzipiell könnte ich das Programm auch so laufen lassen, was aber die Benutzer irritieren würde (Sachen, die keine Reaktion zeigen funktionieren nicht, obwohl sie es eigentlich tun...)
Rahul Der trollige schrieb: > Ich warte mit "WaitForExit()" auf das Ende des extewrnen Prozesses, weil > danach noch mehr im Programmablauf kommt. dann ist das doch das Problem. vewendet doch einfach das Event Exited.
Und Peter II schrieb: > dann ist das doch das Problem. und wie bekomme ich dann den Progressbar aktualisiert? > > vewendet doch einfach das Event Exited. Müsste ich dann nicht für jeden Programmpunkt ein eigenes Event einrichten und es per throw auslösen?
Rahul Der trollige schrieb: > und wie bekomme ich dann den Progressbar aktualisiert? das mach doch das OutputDataReceived Event. > Müsste ich dann nicht für jeden Programmpunkt ein eigenes Event > einrichten und es per throw auslösen? was meist du damit? was hat throw mit Events zu tun?
Schau dir mal den BackgroundWorker an. Soweit ich weiß bringt der alle Funktionen mit, die du brauchst. Die Konvertierung findet dann in der DoWork-Methode statt.
Frieder S. schrieb: > Schau dir mal den BackgroundWorker an. Soweit ich weiß bringt der alle > Funktionen mit, die du brauchst. Die Konvertierung findet dann in der > DoWork-Methode statt. hat er doch schon ein externen process - wozu dann noch ein thread ringsrum machen? Er darf nur nicht auf das Ende vom Process warten, sondern muss auf das Event reagieren.
Peter II schrieb: > das mach doch das OutputDataReceived Event. Das meckert, dass der progressbar einem anderen Thread gehört (sonst hätte ich auch gar nicht erst die Frage gestellt). Müsste ich Peter II schrieb: > was meist du damit? im weiteren Verlauf des Programms werden weitere externe Prozesse gestartet, die vom jeweils vorhergehenden abhängig sind. Im Normalfall würden die hintereinander aufgerufen werden. Wie soll ich das jetzt steuern? Bis jetzt hatte ich noch nie das Problem, dass ich auf externe Prozesse warten musste. > > was hat throw mit Events zu tun? ich bin einfach ratlos und am Rumstochern...
>Das meckert, dass der progressbar einem anderen Thread gehört (sonst >hätte ich auch gar nicht erst die Frage gestellt). Invoke ist hier das Stichwort
Mal als Pseudocode: starte 1 Externen Process solange Process nicht fertig frage Process Status ab übernehme (invoke ) progressbar thread ändere progressbar.value entsprechend des process fortschrittes starte 2 Externen Process An Welcher stelle hängts?
jibi schrieb: > Mal als Pseudocode: Danke. jibi schrieb: > übernehme (invoke ) progressbar thread hier hakt es
Naja das Prinzip/Syntaxs eines "invokes" ist eigentlich nicht schwer zu verstehen: Da threads nur auf sich selbst und ihre Kinder direkt zugreifen dürfen musst du in deinem Fall dem "fremden" thread einmal mitteilen das du gerne auf ihn zugreifen möchtest (genauer gesagt auf ein element das in ihm gespeichert hast) und da der sofortige zugriff nicht gewährt werden kann (da der thread vielleicht gerade was macht was nicht unterbrochen werden darf) musst du eine callback methode beim aufruf des invokes mit angeben - welche dann aufgerufen wird sobald der thread bereit dafür ist. in dieser callback methode hast du dann zugriff auf deine progressbar. Beispiele dafür findet man zu hauf. gruß Jonas
jibi schrieb: > Naja das Prinzip/Syntaxs eines "invokes" ist eigentlich nicht schwer zu > verstehen: Da threads nur auf sich selbst und ihre Kinder direkt > zugreifen dürfen musst du in deinem Fall dem "fremden" thread einmal > mitteilen das du gerne auf ihn zugreifen möchtest (genauer gesagt auf > ein element das in ihm gespeichert hast) und da der sofortige zugriff > nicht gewährt werden kann (da der thread vielleicht gerade was macht was > nicht unterbrochen werden darf) musst du eine callback methode beim > aufruf des invokes mit angeben - welche dann aufgerufen wird sobald der > thread bereit dafür ist. in dieser callback methode hast du dann zugriff > auf deine progressbar. > Beispiele dafür findet man zu hauf. > gruß Jonas Dann hast es scheinbar nicht verstanden. ob ein Thread auf einen anderen zugreifen kann hat nicht mit Invoke zu tun, sondern mit Threadsicherheit. Du kannst jederzeit auf jedes Element in einem anderen Thread zugreifen. Ein Objekt hat keine wirkliche Zugehörigkeit zu einen Thread es ist nur ein teil vom Ram vom Process. Invoke wird gebraucht, weil alle GUI Element von den Hauptthread bearbeitet werden sollen. Mit Invoke übergibt man die Aufgabe über eine Windowsmessage den Hauptthread, dieser kümmern sich dann um die GUI.
>Da threads nur auf sich selbst und ihre Kinder direkt >> zugreifen dürfen Ich hab doch geschrieben dürfen, nicht können. >Du kannst jederzeit auf jedes Element >in einem anderen Thread zugreifen. Eben nicht. >Ein Objekt hat keine wirkliche >Zugehörigkeit zu einen Thread es ist nur ein teil vom Ram vom Process. Du hast schon gemerkt das ich es versucht hab vereinfacht darzustellen, was fängst du jetzt an mit nur ein Teil vom Ram vom Process??? ASLR lässt grüßen... >Mit Invoke übergibt man die Aufgabe über eine >Windowsmessage den Hauptthread, dieser kümmern sich dann um die GUI. Warum hilfst du nicht dem Op statt hier Dinge zu erzählen die eh jeder weiss?
jibi schrieb: > Du hast schon gemerkt das ich es versucht hab vereinfacht darzustellen, > was fängst du jetzt an mit nur ein Teil vom Ram vom Process??? ASLR > lässt grüßen... kann musst du es richtig sagen, es geht nur im GUI Elemente die diese "Eigenart" haben. Um nichts anders. >Da threads nur auf sich selbst und ihre Kinder direkt was sollen Kinder von Threads sein, Threads haben kein Parent.
was sollen Kinder von Threads sein? Na Threads(Kinder) die gestartet werden in einem Thread (Eltern)... Wenn es für dich so ein Klags ist dann hau im doch schnell die paar Zeilen Code hier hin. gruß
Hier mal der betreffende Code:
1 | // Prozess-Aktivitäten
|
2 | public delegate void updateProgressbarDeligate(); |
3 | public updateProgressbarDeligate updateProgressbar; |
4 | private void StartProcess(string ProcName, string Arguments) |
5 | {
|
6 | using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true)) |
7 | {
|
8 | writer.Write("Prozess gestartet: "); |
9 | writer.WriteLine(ProcName + Arguments); |
10 | writer.WriteLine(""); |
11 | }
|
12 | |
13 | try
|
14 | {
|
15 | ProcessStartInfo psi = new ProcessStartInfo(ProcName, Arguments); |
16 | psi = new ProcessStartInfo(ProcName, Arguments); |
17 | psi.RedirectStandardError = true; |
18 | psi.RedirectStandardOutput = false; |
19 | psi.UseShellExecute = false; |
20 | psi.CreateNoWindow = true; |
21 | Process p = new Process(); |
22 | |
23 | //p.OutputDataReceived +=
|
24 | p.ErrorDataReceived += |
25 | new DataReceivedEventHandler(p_OutputDataReceived); |
26 | p.StartInfo = psi; |
27 | p.Start(); |
28 | |
29 | p.BeginErrorReadLine(); |
30 | |
31 | p.WaitForExit(); // wie sieht die Alternative aus? |
32 | |
33 | p.Close(); |
34 | p.Dispose(); |
35 | }
|
36 | catch (NullReferenceException ne) |
37 | {
|
38 | using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true)) |
39 | {
|
40 | writer.WriteLine(ne.StackTrace); |
41 | }
|
42 | }
|
43 | }
|
44 | private void Processfile() |
45 | {
|
46 | try
|
47 | {
|
48 | this.Invoke(new updateProgressbarDeligate(updateProgressbarMethod)); |
49 | }
|
50 | catch { MessageBox.Show("Kaputt!"); } |
51 | }
|
52 | void p_OutputDataReceived(object sender, DataReceivedEventArgs e) |
53 | { // Daten, die vom Prozess kommen, werden hier behandelt (funktioniert noch nicht optimal) |
54 | try
|
55 | {
|
56 | if (e.Data != null) |
57 | {
|
58 | // Behandlung Progressbar
|
59 | if (e.Data.IndexOf("frame=") > -1) |
60 | {
|
61 | string FramesString = e.Data.ToString(); |
62 | int FramesPos = FramesString.IndexOf("frame=") + 6; |
63 | int fpsPos = FramesString.IndexOf(" fps"); |
64 | int FramesLength = fpsPos - FramesPos; |
65 | |
66 | progressBarValue += 1; //= Convert.ToInt32(FramesString); |
67 | FramesString = e.Data.Substring(FramesPos, FramesLength); |
68 | MessageBox.Show(FramesString); |
69 | |
70 | if (progressBar1.InvokeRequired) |
71 | {
|
72 | // was muss hier rein?
|
73 | }
|
74 | }
|
75 | }
|
76 | }
|
77 | catch (NullReferenceException ne) |
78 | {
|
79 | using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true)) |
80 | {
|
81 | writer.WriteLine(ne.StackTrace); |
82 | }
|
83 | }
|
84 | using (StreamWriter writer = new StreamWriter(exportpath + tempexportpath + "\\log.txt", true)) |
85 | {
|
86 | writer.WriteLine(e.Data); |
87 | }
|
88 | |
89 | }
|
Ich bin wohl bzgl. Delegates, Invoke und Process-Threads etwas begriffsstutzig. In p_OutputDataReceived bekomme ich ganz wunderbar die aktuelle Frame-Zahl ausgelesen und würde die nun gerne im Progressbar (, einer Textbox o.dergl.) anzeigen lassen. Dass p_OutputDataReceived angesprungen wird, lasse ich mir durch eine Messagebox anzeigen. Im Debugger sehe ich auch, dass die if-Abfrage "progressBar1.Invokerequired" durchgeführt und der Code in den Klammern ausgeführt wird. Dort müsste ja auch eigentlich das Progressbar-Update erfolgen - nur wie? Vielen Dank für die Unterstützung.
Ich hab's übrigens hinbekommen: statt "p.WaitForExit();":
1 | while (!p.HasExited) |
2 | {
|
3 | progressBar1.Value = (Task*100) + (progressBarValue*100/frames); |
4 | this.Text = Convert.ToString(((Task * 100) + (progressBarValue * 100 / frames)) / 4) + "% Fortschritt"; |
5 | Application.DoEvents(); |
6 | //this.Refresh();
|
7 | }
|
Nebenbei wird auch noch der Fortschritt in der Titelzeile angezeigt. application.DoEvent ist zwar gefährlich, aber bei meiner Anwendung werden sämtliche Eingabefelder gesperrt (außer dem "X" oben rechts). Die Berechnung für den Progressbar hängt damit zusammen, dass dessen Maximumwert 400 ist und ich (momentan) vier Aufgaben im Ablauf habe - es werden aber noch mehr.
http://stackoverflow.com/questions/2367718/automating-the-invokerequired-code-pattern DoEvents() halte ich für Pfusch ;D
bluppdidupp schrieb: > http://stackoverflow.com/questions/2367718/automat... > > DoEvents() halte ich für Pfusch ;D ist es ja auch, funktioniert aber in gewissen Grenzen... ;) Auf welcher Basis das Programm funktioniert, ist dem "Kunden" doch egal, solange es einwandfrei funktioniert.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.