Oppure

Loading
11/09/14 9:49
GN
Salve a tutti, ho questo problema in un'applicazione .NET. Ho bisogno di catturare l'output di un processo, sia ciò che viene scritto sullo standard output che sullo standard error; per ora, in particolare, sono interessato all'output del compilatore java (javac.exe, che in realtà scrive tutto su stderr, ma vorrei una soluzione "pulita" in modo che in futuro possa riutilizzare il codice per altri comandi che mandano alcuni messaggi di output su stdout e altri su stderr). In altre parole, ho bisogno di leggere in tempo reale stdout e stderr man mano che vengono scritti dal processo e nell'ordine giusto (ovvero non prima tutto stdout e poi tutto stderr o viceversa), esattamente come vengono mostrati quando il processo viene lanciato da terminale.

Detto questo, il codice che sono riuscito a scrivere è questo.

    Delegate Sub onOutputDelegate(ByVal output As String, ByVal isError As Boolean) 'funzione che verrà chiamata ogni volta che il processo scrive qualcosa in uno dei due flussi di output

    Private Sub runCommand(ByVal cmd As String, ByVal onOutput As onOutputDelegate, Optional ByVal cwd As String = "")
        Dim p As New Process()
        p.StartInfo.FileName = cmd
        p.StartInfo.UseShellExecute = False
        p.StartInfo.CreateNoWindow = True
        p.StartInfo.RedirectStandardOutput = True
        p.StartInfo.RedirectStandardError = True
        If cwd <> "" Then p.StartInfo.WorkingDirectory = cwd
        p.Start()
        Dim thOut As New Thread(Sub()
                                    While Not p.HasExited
                                        onOutput(p.StandardOutput.ReadLine(), False)
                                    End While
                                End Sub)
        Dim thErr As New Thread(Sub()
                                    While Not p.HasExited
                                        onOutput(p.StandardError.ReadLine(), True)
                                    End While
                                End Sub)
        thOut.Start()
        thErr.Start()
        p.WaitForExit()
    End Sub

Come potete vedere la soluzione che mi è venuta in mente è di lanciare contemporaneamente due thread separati, uno per stdout e uno per stderr.
Inizialmente sembrava funzionare tutto, ma poi mi sono accorto di un comportamento strano: a volte certe righe di output non vengono lette, mente altre volte si (lanciando lo stesso identico comando con gli stessi parametri), in maniera apparentemente casuale.
Qualcuno saprebbe aiutarmi? Inoltre, secondo voi la soluzione dei due thread vi sembra una buona idea o ci sono alternative migliori? Grazie in anticipo a chiunque risponderà ;)

PS: se qualcuno fosse interessato a vedere l'intero file di codice lo trovate qui: sourceforge.net/p/universalide/java-addon-code/ci/master/tree/Java%20for%20UniversalIDE/…
Ultima modifica effettuata da GN 11/09/14 9:51
aaa
12/09/14 9:54
vankraster
Io sono riuscito con questo codice:
     string q = "";
            using (Process p = new Process())
            {
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.CreateNoWindow = true;
                p.StartInfo.FileName = @"c:\temp\test.bat";

                p.Start();

                while (!p.HasExited)
                    q += p.StandardOutput.ReadToEnd();
            }


            textBox1.Text = q; 



eseguo un file bat che ho fatto io tipo: echo 'prova' ma questo non conta ...
su textBox1 mi da il risultato.
aaa
12/09/14 21:05
GN
Il probelma è che così leggi solo lo Standard Output, io invece, come avevo specificato, ho bisogno di leggere contemporaneamente Standard Output e Standard Error... Grazie comunque
aaa
15/09/14 7:22
vankraster
Nel ciclo while potresti aggiungere

   
q + = p.StandardError.ReadToEnd();


Perché ti serve leggere i dati contemporaneamente? Forse troviamo un'altra soluzione.
aaa
18/09/14 16:43
GN
Postato originariamente da vankraster:
Perché ti serve leggere i dati contemporaneamente? Forse troviamo un'altra soluzione.

Ti spiego, sto programmando un IDE che deve lanciare dei compilatori e mostrarne e parsarne l'output (in modo da mostrare una lista di errori presenti nel codice), attualmente sono al lavoro con il compilatore java ma vorrei generalizzare il codice per l'avvio di un processo e la lettura del rispettivo output in modo da poterlo riutilizzare se e quando ne supporterò altri; vorrei quindi riuscire a ricevere sia l'stdout che l'stderr in modo che su qualsiasi di questi flussi scriva il programma che lancio, l'output venga mostrato all'utente nel mio programma.
Usando ReadToEnd ho parzialmente risolto in questo modo, dovendo però rinunciare ella lettura in tempo reale:
        p.Start()
        p.WaitForExit()
        Dim out As String = p.StandardOutput.ReadToEnd()
        For Each l As String In out.Split(Environment.NewLine)
            onOutput(l, False)
        Next
        Dim err As String = p.StandardError.ReadToEnd()
        For Each l As String In err.Split(Environment.NewLine)
            onOutput(l, True)
        Next

E così funziona, anche se ovviamente l'output arriva solo quando il processo è terminato e non in ordine (prima tutto stdout e poi tutto stderr). Possibile che non sia possibile monitorare entrambi gli stream e leggerli riga per riga man mano che il processo li scrive? E' una settimana ormai che ci sto sbattendo la testa :om: :pat:

P.S. ho provato anche con gli eventi OutputDataReceived e ErrorDataReceived, in questo modo:
        AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                             onOutput(e.Data, False)
                                         End Sub
        p.BeginOutputReadLine()
        AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
                                            onOutput(e.Data, True)
                                        End Sub
        p.BeginOutputReadLine()
        p.BeginErrorReadLine()

Ma funziona solo su un flusso alla volta.
aaa