Ich habe folgenden Code:
info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents
Ich weiß, dass die Ausgabe des Prozesses, den ich starte, ungefähr 7 MB lang ist. Das Ausführen in der Windows-Konsole funktioniert einwandfrei. Leider hängt dies programmgesteuert auf unbestimmte Zeit bei WaitForExit. Beachten Sie auch, dass der Code für kleinere Ausgänge (wie 3 KB) NICHT hängt.
Ist es möglich, dass der interne StandardOutput in ProcessStartInfo 7 MB nicht puffern kann? Wenn ja, was soll ich stattdessen tun? Wenn nicht, was mache ich falsch?
c#
processstartinfo
Epaga
quelle
quelle
Antworten:
Das Problem ist, dass wenn Sie umleiten
StandardOutput
und / oderStandardError
der interne Puffer voll werden kann. Unabhängig von der Reihenfolge, die Sie verwenden, kann ein Problem auftreten:StandardOutput
, kann der Versuch, darauf zu schreiben, blockiert werden, sodass der Prozess niemals endet.StandardOutput
mit ReadToEnd lesen , kann Ihr Prozess blockieren, wenn der Prozess nie geschlossen wirdStandardOutput
(z. B. wenn er nie beendet wird oder wenn das Schreiben in blockiert wirdStandardError
).Die Lösung besteht darin, asynchrone Lesevorgänge zu verwenden, um sicherzustellen, dass der Puffer nicht voll wird. Um jeden Deadlocks zu vermeiden und alle Ausgaben von beiden Sammeln
StandardOutput
undStandardError
Sie können dies tun:BEARBEITEN: In den folgenden Antworten erfahren Sie, wie Sie eine ObjectDisposedException vermeiden, wenn das Zeitlimit auftritt.
quelle
using
Anweisungen für die Ereignishandler über derusing
Anweisung für den Prozess selbst liegen müssen?Die Dokumentation für
Process.StandardOutput
sagt zu lesen, bevor Sie warten, sonst können Sie Deadlock, Snippet unten kopiert:quelle
RedirectStandardOutput = true;
und nicht verwendenp.StandardOutput.ReadToEnd();
.Die Antwort von Mark Byers ist ausgezeichnet, aber ich möchte nur Folgendes hinzufügen:
Die
OutputDataReceived
undErrorDataReceived
-Delegierten müssen vor demoutputWaitHandle
und entfernt werdenerrorWaitHandle
entsorgt werden. Wenn der Prozess zur Ausgabe von Daten fortgesetzt , nachdem die Timeout überschritten wurde und beendet dann dieoutputWaitHandle
underrorWaitHandle
werden Variablen , nachdem sie entsorgt werden abgerufen.(Zu Ihrer Information, ich musste diesen Vorbehalt als Antwort hinzufügen, da ich seinen Beitrag nicht kommentieren konnte.)
quelle
Das Problem mit der nicht behandelten ObjectDisposedException tritt auf, wenn das Zeitlimit für den Prozess abgelaufen ist. In diesem Fall die anderen Teile der Bedingung:
werden nicht ausgeführt. Ich habe dieses Problem folgendermaßen gelöst:
quelle
output
underror
zuoutputBuilder
? Kann jemand bitte eine vollständige Antwort geben, die funktioniert?Dies ist eine modernere, auf TPL (Based Parallel Library) basierende Lösung für .NET 4.5 und höher.
Anwendungsbeispiel
Implementierung
quelle
Rob antwortete und ersparte mir noch ein paar Stunden Probezeit. Lesen Sie den Ausgabe- / Fehlerpuffer, bevor Sie warten:
quelle
WaitForExit()
?ReadToEnd
oder ähnlichen Methoden (wieStandardOutput.BaseStream.CopyTo
) wird zurückgegeben, nachdem ALLE Daten gelesen wurden. nichts wird danach kommenWir haben auch dieses Problem (oder eine Variante).
Versuche Folgendes:
1) Fügen Sie p.WaitForExit (nnnn) eine Zeitüberschreitung hinzu. Dabei ist nnnn in Millisekunden.
2) Setzen Sie den ReadToEnd-Aufruf vor den WaitForExit-Aufruf. Dies ist, was wir MS empfohlen gesehen haben.
quelle
Gutschrift an EM0 für https://stackoverflow.com/a/17600012/4151626
Die anderen Lösungen (einschließlich EM0s) waren für meine Anwendung aufgrund interner Zeitüberschreitungen und der Verwendung von StandardOutput und StandardError durch die erzeugte Anwendung immer noch blockiert. Folgendes hat bei mir funktioniert:
Bearbeiten: Initialisierung von StartInfo zum Codebeispiel hinzugefügt
quelle
Ich habe es so gelöst:
Ich habe sowohl Eingabe als auch Ausgabe und Fehler umgeleitet und das Lesen von Ausgabe- und Fehlerströmen behandelt. Diese Lösung funktioniert für SDK 7- 8.1, sowohl für Windows 7 als auch für Windows 8
quelle
Ich habe versucht, eine Klasse zu erstellen, die Ihr Problem mithilfe des asynchronen Stream-Lesens löst, indem ich die Antworten von Mark Byers, Rob und Stevejay berücksichtigt habe. Dabei wurde mir klar, dass es einen Fehler beim Lesen des asynchronen Prozessausgabestreams gibt.
Ich habe diesen Fehler bei Microsoft gemeldet: https://connect.microsoft.com/VisualStudio/feedback/details/3119134
Zusammenfassung:
Sie verwenden wahrscheinlich besser asynchrones Lesen, wie es von anderen Benutzern für Ihren Fall vorgeschlagen wurde. Sie sollten sich jedoch bewusst sein, dass Sie aufgrund der Rennbedingungen einige Informationen verpassen können.
quelle
Ich denke, dass dies ein einfacher und besserer Ansatz ist (wir brauchen ihn nicht
AutoResetEvent
):quelle
.FileName = Path + @"\ggsci.exe" + @" < obeycommand.txt"
Ihren Code vereinfachen? Oder vielleicht etwas Äquivalentes,"echo command | " + Path + @"\ggsci.exe"
wenn Sie wirklich keine separate obeycommand.txt-Datei verwenden möchten.Keine der obigen Antworten macht den Job.
Die Rob-Lösung hängt und die 'Mark Byers'-Lösung erhält die entsprechende Ausnahme (ich habe die "Lösungen" der anderen Antworten ausprobiert).
Also habe ich beschlossen, eine andere Lösung vorzuschlagen:
Dieser Code hat debuggt und funktioniert perfekt.
quelle
GetProcessOutputWithTimeout
Methode nicht angegeben wird.Einführung
Die derzeit akzeptierte Antwort funktioniert nicht (löst eine Ausnahme aus) und es gibt zu viele Problemumgehungen, aber keinen vollständigen Code. Dies verschwendet offensichtlich viel Zeit, da dies eine beliebte Frage ist.
Ich kombinierte die Antwort von Mark Byers und die Antwort von Karol Tyl und schrieb vollständigen Code basierend darauf, wie ich die Process.Start-Methode verwenden möchte.
Verwendung
Ich habe es verwendet, um einen Fortschrittsdialog um Git-Befehle zu erstellen. So habe ich es benutzt:
Theoretisch kann man auch stdout und stderr kombinieren, aber das habe ich nicht getestet.
Code
quelle
Ich weiß, dass das Abendessen alt ist, aber nachdem ich diese ganze Seite gelesen hatte, funktionierte keine der Lösungen für mich, obwohl ich Muhammad Rehan nicht ausprobiert habe, da der Code ein wenig schwer zu befolgen war, obwohl ich denke, dass er auf dem richtigen Weg war . Wenn ich sage, dass es nicht funktioniert hat, ist das nicht ganz richtig. Manchmal funktioniert es gut. Ich denke, es hat etwas mit der Länge der Ausgabe vor einer EOF-Markierung zu tun.
Wie auch immer, die Lösung, die für mich funktioniert hat, bestand darin, verschiedene Threads zu verwenden, um StandardOutput und StandardError zu lesen und die Nachrichten zu schreiben.
Hoffe das hilft jemandem, der dachte das könnte so schwer sein!
quelle
sw.FlushAsync(): Object is not set to an instance of an object. sw is null.
Wie / wo sollsw
definiert werden?Nachdem ich alle Beiträge hier gelesen hatte, entschied ich mich für die konsolidierte Lösung von Marko Avlijaš. Jedoch , es hat nicht alle meine Probleme zu lösen.
In unserer Umgebung haben wir einen Windows-Dienst, der Hunderte verschiedener .bat .cmd .exe, ... usw. Dateien ausführen soll, die sich im Laufe der Jahre angesammelt haben und von vielen verschiedenen Personen und in verschiedenen Stilen geschrieben wurden. Wir haben keine Kontrolle über das Schreiben der Programme und Skripte. Wir sind lediglich für die Planung, Ausführung und Berichterstattung über Erfolg / Misserfolg verantwortlich.
Also habe ich so ziemlich alle Vorschläge hier mit unterschiedlichem Erfolg ausprobiert. Markos Antwort war fast perfekt, aber wenn es als Dienst ausgeführt wurde, wurde stdout nicht immer erfasst. Ich bin nie auf den Grund gegangen, warum nicht.
Die einzige Lösung, die wir in ALLEN unseren Fällen gefunden haben, ist folgende: http://csharptest.net/319/using-the-processrunner-class/index.html
quelle
Problemumgehung, die ich letztendlich verwendet habe, um die Komplexität zu vermeiden:
Also erstelle ich eine temporäre Datei und leite sowohl die Ausgabe als auch den Fehler mithilfe von um
> outputfile > 2>&1
und lese die Datei dann einfach, nachdem der Prozess abgeschlossen ist.Die anderen Lösungen eignen sich gut für Szenarien, in denen Sie andere Aufgaben mit der Ausgabe ausführen möchten. Bei einfachen Aufgaben wird jedoch viel Komplexität vermieden.
quelle
Ich habe viele der Antworten gelesen und meine eigenen gemacht. Ich bin mir nicht sicher, ob dies auf jeden Fall behoben werden kann, aber es wird in meiner Umgebung behoben. Ich verwende WaitForExit einfach nicht und verwende WaitHandle.WaitAll sowohl für Ausgangs- als auch für Fehlerendesignale. Ich werde mich freuen, wenn jemand mögliche Probleme damit sieht. Oder ob es jemandem hilft. Für mich ist es besser, weil keine Timeouts verwendet werden.
quelle
Ich denke, mit Async ist es möglich, eine elegantere Lösung zu haben und keine Deadlocks zu haben, selbst wenn sowohl standardOutput als auch standardError verwendet werden:
Es basiert auf der Antwort von Mark Byers. Wenn Sie sich nicht in einer asynchronen Methode befinden, können Sie
string output = tStandardOutput.result;
stattdessen verwendenawait
quelle
Keine dieser Antworten hat mir geholfen, aber diese Lösung funktionierte gut mit der Handhabung von Hängen
https://stackoverflow.com/a/60355879/10522960
quelle
Dieser Beitrag ist möglicherweise veraltet, aber ich habe herausgefunden, dass die Hauptursache dafür, dass er normalerweise hängt, auf einen Stapelüberlauf für den redirectStandardoutput zurückzuführen ist oder wenn Sie redirectStandarderror haben.
Da die Ausgabedaten oder die Fehlerdaten groß sind, führt dies zu einer Wartezeit, da sie noch auf unbestimmte Zeit verarbeitet werden.
So beheben Sie dieses Problem:
quelle
Nennen wir den hier veröffentlichten Beispielcode den Redirector und das andere Programm den Redirector. Wenn ich es wäre, würde ich wahrscheinlich ein Testumleitungsprogramm schreiben, mit dem das Problem dupliziert werden kann.
So tat ich. Für Testdaten habe ich die PDF-Datei ECMA-334 C # verwendet. es ist ungefähr 5 MB. Das Folgende ist der wichtige Teil davon.
Der Datengrößenwert stimmt nicht mit der tatsächlichen Dateigröße überein, dies spielt jedoch keine Rolle. Es ist nicht klar, ob eine PDF-Datei am Ende der Zeilen immer sowohl CR als auch LF verwendet, aber das spielt dabei keine Rolle. Sie können jede andere große Textdatei zum Testen verwenden.
Dabei hängt der Beispiel-Redirector-Code, wenn ich die große Datenmenge schreibe, aber nicht, wenn ich eine kleine Menge schreibe.
Ich habe sehr viel versucht, die Ausführung dieses Codes irgendwie zu verfolgen, und ich konnte es nicht. Ich habe die Zeilen des umgeleiteten Programms auskommentiert, die die Erstellung einer Konsole für das umgeleitete Programm deaktiviert haben, um zu versuchen, ein separates Konsolenfenster zu erhalten, konnte dies jedoch nicht.
Dann fand ich heraus, wie man eine Konsolen-App in einem neuen Fenster, im Fenster der Eltern oder in keinem Fenster startet . Anscheinend können wir keine (einfache) separate Konsole haben, wenn ein Konsolenprogramm ein anderes Konsolenprogramm ohne ShellExecute startet. Da ShellExecute die Umleitung nicht unterstützt, müssen wir eine Konsole freigeben, auch wenn wir kein Fenster für den anderen Prozess angeben.
Ich gehe davon aus, dass das umgeleitete Programm, wenn es irgendwo einen Puffer füllt, auf das Lesen der Daten warten muss. Wenn zu diesem Zeitpunkt keine Daten vom Redirector gelesen werden, ist dies ein Deadlock.
Die Lösung besteht darin, ReadToEnd nicht zu verwenden und die Daten zu lesen, während die Daten geschrieben werden. Es ist jedoch nicht erforderlich, asynchrone Lesevorgänge zu verwenden. Die Lösung kann recht einfach sein. Das Folgende funktioniert für mich mit dem 5 MB PDF.
Eine andere Möglichkeit besteht darin, ein GUI-Programm zu verwenden, um die Umleitung durchzuführen. Der vorhergehende Code funktioniert in einer WPF-Anwendung, außer mit offensichtlichen Änderungen.
quelle
Ich hatte das gleiche Problem, aber der Grund war anders. Dies würde jedoch unter Windows 8 geschehen, jedoch nicht unter Windows 7. Die folgende Zeile scheint das Problem verursacht zu haben.
Die Lösung bestand darin, UseShellExecute NICHT zu deaktivieren. Ich habe jetzt ein Shell-Popup-Fenster erhalten, das unerwünscht ist, aber viel besser als das Programm, das darauf wartet, dass nichts Besonderes passiert. Deshalb habe ich die folgende Problemumgehung hinzugefügt:
Jetzt stört mich nur noch, warum dies überhaupt unter Windows 8 geschieht.
quelle
UseShellExecute
auf false gesetzt sein, wenn Sie die Ausgabe umleiten möchten.