wake-up-neo.net

Erhalten Sie eine Live-Ausgabe von Process

Ich habe ein Problem in meinem Projekt. Ich möchte einen Prozess starten, 7z.exe (Konsolenversion). Ich habe drei verschiedene Dinge ausprobiert:

  • Process.StandardOutput.ReadToEnd ();
  • OutputDataReceived & BeginOutputReadLine
  • StreamWriter

Nichts funktioniert. Es "wartet" immer auf das Ende des Prozesses, um zu zeigen, was ich will. Ich habe keinen Code, den ich setzen möchte, nur wenn Sie meinen Code mit einem der oben aufgeführten Dinge haben möchten. Vielen Dank.

Edit: Mein Code:

        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;
        process.Start();

        this.sr = process.StandardOutput;
        while (!sr.EndOfStream)
        {
            String s = sr.ReadLine();
            if (s != "")
            {
                System.Console.WriteLine(DateTime.Now + " - " + s);
            }
        }

Oder

process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += new DataReceivedEventHandler(recieve);
process.StartInfo.CreateNoWindow = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
public void recieve(object e, DataReceivedEventArgs outLine)
{
    System.Console.WriteLine(DateTime.Now + " - " + outLine.Data);
}

Oder

process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
string output = p.StandardOutput.ReadToEnd();
process.WaitForExit();

Wo "Prozess" ist mein vorgefertigter Prozess

Ok, ich weiß, warum es nicht richtig funktioniert: 7z.exe ist der Fehler: Es wird eine Prozentladung in der Konsole angezeigt und es sendet nur dann Informationen, wenn die aktuelle Datei abgeschlossen ist. In der Extraktion zum Beispiel funktioniert es gut :). Ich werde nach einer anderen Möglichkeit suchen, um 7z-Funktionen ohne 7z.exe (möglicherweise mit 7za.exe oder mit einer DLL) zu verwenden. Vielen Dank an alle. Um die Frage zu beantworten, funktioniert die OuputDataRecieved-Veranstaltung einwandfrei!

18
Extaze

Schauen Sie sich diese Seite an, es sieht so aus, als wäre dies die Lösung für Sie: http://msdn.Microsoft.com/de-de/library/system.diagnostics.process.beginoutputreadline.aspx und http: //msdn.Microsoft.com/de-de/library/system.diagnostics.process.standardoutput.aspx

[Bearbeiten] Dies ist ein Arbeitsbeispiel:

        Process p = new Process();
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.FileName = @"C:\Program Files (x86)\gnuwin32\bin\ls.exe";
        p.StartInfo.Arguments = "-R C:\\";

        p.OutputDataReceived += new DataReceivedEventHandler(
            (s, e) => 
            { 
                Console.WriteLine(e.Data); 
            }
        );
        p.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine(e.Data); });

        p.Start();
        p.BeginOutputReadLine();
        p.BeginErrorReadLine();

Übrigens, ls -R C:\listet alle Dateien vom Stamm von C: rekursiv auf. Dies sind viele Dateien, und ich bin mir sicher, dass dies nicht der Fall ist, wenn die ersten Ergebnisse auf dem Bildschirm angezeigt werden. Es besteht die Möglichkeit, dass 7Zip die Ausgabe hält, bevor sie angezeigt wird. Ich bin nicht sicher, welche Params Sie dem Prozess geben.

19

Um die Ausgabe- und/oder Fehlerumleitung korrekt zu behandeln, müssen Sie auch die Eingabe umleiten. Es scheint, dass es sich um ein Feature/Fehler in Laufzeit der externen Anwendung handelt und von dem, was ich bisher gesehen habe, wird es an keiner anderen Stelle erwähnt .

Verwendungsbeispiel:

        Process p = new Process(...);

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardInput = true; // Is a MUST!
        p.EnableRaisingEvents = true;

        p.OutputDataReceived += OutputDataReceived;
        p.ErrorDataReceived += ErrorDataReceived;

        Process.Start();

        p.BeginOutputReadLine();
        p.BeginErrorReadLine();

        p.WaitForExit();

        p.OutputDataReceived -= OutputDataReceived;
        p.ErrorDataReceived -= ErrorDataReceived;

...

    void OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        // Process line provided in e.Data
    }

    void ErrorDataReceived(object sender, DataReceivedEventArgs e)
    {
        // Process line provided in e.Data
    }
5
user3042599

Ich weiß nicht, ob noch jemand nach einer Lösung für dieses Problem sucht, aber es ist mehrmals für mich aufgetaucht, weil ich in Unity ein Tool schreibe, das einige Spiele unterstützt, und wegen der eingeschränkten Interoperabilität bestimmter Systeme mit Mono (wie zum Beispiel PIA für das Lesen von Text aus Word), muss ich häufig ausführbare Dateien für Betriebssysteme (manchmal Windows, manchmal MacOS) schreiben und sie von Process.Start () aus starten.

Das Problem ist, wenn Sie eine solche ausführbare Datei starten, wird sie in einem anderen Thread hochgefahren, der Ihre Hauptanwendung blockiert, was zu einem Hang führt. Wenn Sie Ihren Benutzern während dieser Zeit nützliche Rückmeldungen geben möchten, die über die sich drehenden Symbole Ihres jeweiligen Betriebssystems hinausgehen, sind Sie irgendwie vermasselt. Die Verwendung eines Streams funktioniert nicht, da der Thread noch bis zum Abschluss der Ausführung blockiert ist.

Die Lösung, auf die ich gestoßen bin, die für manche Leute extrem erscheinen mag, aber für mich funktioniert das ganz gut, ist die Verwendung von Sockets und Multithreading, um zuverlässige synchrone Kommunikationsverbindungen zwischen den beiden Apps einzurichten. Dies funktioniert natürlich nur, wenn Sie beide Apps erstellen. Wenn nicht, denke ich, haben Sie kein Glück. Ich würde gerne sehen, ob es nur mit Multithreading unter Verwendung eines herkömmlichen Stream-Ansatzes funktioniert. Wenn also jemand das ausprobieren und die Ergebnisse hier posten möchte, wäre das großartig.

Wie auch immer, hier ist die Lösung, die derzeit für mich funktioniert:

In der Haupt- oder aufrufenden App mache ich so etwas:

/// <summary>
/// Handles the OK button click.
/// </summary>
private void HandleOKButtonClick() {
string executableFolder = "";

#if UNITY_EDITOR
executableFolder = Path.Combine(Application.dataPath, "../../../../build/Include/Executables");
#else
executableFolder = Path.Combine(Application.dataPath, "Include/Executables");
#endif

EstablishSocketServer();

var proc = new Process {
    StartInfo = new ProcessStartInfo {
        FileName = Path.Combine(executableFolder, "WordConverter.exe"),
        Arguments = locationField.value + " " + _ipAddress.ToString() + " " + SOCKET_PORT.ToString(), 
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

proc.Start();

Hier etabliere ich den Socket-Server:

/// <summary>
/// Establishes a socket server for communication with each chapter build script so we can get progress updates.
/// </summary>
private void EstablishSocketServer() {
    //_dialog.SetMessage("Establishing socket connection for updates. \n");
    TearDownSocketServer();

    Thread currentThread;

    _ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
    _listener = new TcpListener(_ipAddress, SOCKET_PORT);
    _listener.Start();

    UnityEngine.Debug.Log("Server mounted, listening to port " + SOCKET_PORT);

    _builderCommThreads = new List<Thread>();

    for (int i = 0; i < 1; i++) {
        currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage));
        _builderCommThreads.Add(currentThread);
        currentThread.Start();
    }
}

/// <summary>
/// Tears down socket server.
/// </summary>
private void TearDownSocketServer() {
    _builderCommThreads = null;

    _ipAddress = null;
    _listener = null;
}

Hier ist mein Socket-Handler für den Thread ... Beachten Sie, dass Sie in einigen Fällen mehrere Threads erstellen müssen. Aus diesem Grund habe ich die _builderCommThreads-Liste dort (ich habe sie von Code aus woanders kopiert, wo ich etwas Ähnliches gemacht habe, aber mehrere Instanzen hintereinander aufrief):

/// <summary>
/// Handles the incoming socket message.
/// </summary>
private void HandleIncomingSocketMessage() {
    if (_listener == null) return;

    while (true) {
        Socket soc = _listener.AcceptSocket();
        //soc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000);
        NetworkStream s = null;
        StreamReader sr = null;
        StreamWriter sw = null;
        bool reading = true;

        if (soc == null) break;

        UnityEngine.Debug.Log("Connected: " + soc.RemoteEndPoint);

        try {
            s = new NetworkStream(soc);
            sr = new StreamReader(s, Encoding.Unicode);
            sw = new StreamWriter(s, Encoding.Unicode);
            sw.AutoFlush = true; // enable automatic flushing

            while (reading == true) {
                string line = sr.ReadLine();

                if (line != null) {
                    //UnityEngine.Debug.Log("SOCKET MESSAGE: " + line);
                    UnityEngine.Debug.Log(line);

                    lock (_threadLock) {
                        // Do stuff with your messages here
                    }
                }
            }

            //
        } catch (Exception e) {
            if (s != null) s.Close();
            if (soc != null) soc.Close();
            UnityEngine.Debug.Log(e.Message);
            //return;
        } finally {

        //
        if (s != null) s.Close();
        if (soc != null) soc.Close();

        UnityEngine.Debug.Log("Disconnected: " + soc.RemoteEndPoint);
        }
    }

    return;
}

Natürlich musst du oben ein paar Sachen deklarieren:

private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();

... dann in der aufgerufenen ausführbaren Datei das andere Ende einrichten (ich habe in diesem Fall Statik verwendet, Sie können verwenden, was immer Sie möchten):

private static TcpClient _client = null;
private static Stream _s = null;
private static StreamReader _sr = null;
private static StreamWriter _sw = null;
private static string _ipAddress = "";
private static int _port = 0;
private static System.Object _threadLock = new System.Object();

/// <summary>
/// Main method.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
    try {
        if (args.Length == 3) {
            _ipAddress = args[1];
            _port = Convert.ToInt32(args[2]);

            EstablishSocketClient();
        }

        // Do stuff here

        if (args.Length == 3) Cleanup();
    } catch (Exception exception) {
        // Handle stuff here
        if (args.Length == 3) Cleanup();
    }
}

/// <summary>
/// Establishes the socket client.
/// </summary>
private static void EstablishSocketClient() {
    _client = new TcpClient(_ipAddress, _port);

    try {
        _s = _client.GetStream();
        _sr = new StreamReader(_s, Encoding.Unicode);
        _sw = new StreamWriter(_s, Encoding.Unicode);
        _sw.AutoFlush = true;
    } catch (Exception e) {
        Cleanup();
    }
}

/// <summary>
/// Clean up this instance.
/// </summary>
private static void Cleanup() {
    _s.Close();
    _client.Close();

    _client = null;
    _s = null;
    _sr = null;
    _sw = null;
}

/// <summary>
/// Logs a message for output.
/// </summary>
/// <param name="message"></param>
private static void Log(string message) {
    if (_sw != null) {
        _sw.WriteLine(message);
    } else {
        Console.Out.WriteLine(message);
    }
}

... Ich verwende dies, um ein Befehlszeilentool unter Windows zu starten, das PIA-Daten verwendet, um Text aus einem Word-Dokument abzurufen. Ich habe PIA die .dlls in Unity ausprobiert, bin aber auf Interop-Probleme mit Mono gestoßen. Ich verwende es auch unter MacOS, um Shell-Skripts aufzurufen, die zusätzliche Unity-Instanzen im Stapelmodus starten und Editor-Skripts in diesen Instanzen ausführen, die über diese Socket-Verbindung mit dem Tool kommunizieren. Es ist großartig, weil ich jetzt Feedback an den Benutzer senden, bestimmte Schritte im Prozess debuggen, überwachen und darauf reagieren kann, und so weiter, usw.

HTH

5
user2848240

Ich habe die CmdProcessor-Klasse here in mehreren Projekten mit großem Erfolg verwendet. Es sieht auf den ersten Blick etwas entmutigend aus, ist aber sehr einfach zu bedienen.

1
joebalt

Versuche dies.

        Process notePad = new Process();

        notePad.StartInfo.FileName = "7z.exe";
        notePad.StartInfo.RedirectStandardOutput = true;
        notePad.StartInfo.UseShellExecute = false;

        notePad.Start();
        StreamReader s = notePad.StandardOutput;



        String output= s.ReadToEnd();


        notePad.WaitForExit();

Das obige sei in einer thread.

Jetzt können Sie zum Aktualisieren der Ausgabe auf die Benutzeroberfläche eine timer mit zwei Zeilen verwenden

  Console.Clear();
  Console.WriteLine(output);

Das kann dir helfen

0
sonu thomas

Das Problem wird durch Aufrufen der Methode Process.WaitForExit verursacht. Laut Dokumentation bedeutet dies:

Legt fest, wie lange gewartet werden soll, bis der zugeordnete Prozess beendet ist, und blockiert den aktuellen Ausführungsthread , bis die Zeit abgelaufen ist oder der Prozess beendet ist. Verwenden Sie das Exited-Ereignis , um das Blockieren des aktuellen Threads zu vermeiden.

Um zu verhindern, dass der Thread blockiert, bis der Prozess beendet ist, verbinden Sie das Ereignis Process.Exited des Objekt verarbeiten, wie unten. Das Exited-Ereignis kann nur auftreten, wenn der Wert der EnableRaisingEvents-Eigenschaft true ist.

    process.EnableRaisingEvents = true;
    process.Exited += Proc_Exited;


    private void Proc_Exited(object sender, EventArgs e)
    {
        // Code to handle process exit
    }

Auf diese Weise können Sie die Ausgabe Ihres Prozesses über das Ereignis Process.OutputDataReceived abrufen, während er noch ausgeführt wird wie bisher. (PS - das Codebeispiel auf dieser Ereignisseite macht auch den Fehler, Process.WaitForExit zu verwenden.)

Ein weiterer Hinweis ist, dass Sie sicherstellen müssen, dass Ihr Process-Objekt nicht bereinigt wird, bevor Ihre Exited-Methode ausgelöst werden kann. Wenn Ihr Prozess in einer using-Anweisung initialisiert wird, ist dies möglicherweise ein Problem.

0
Bork Blatt