2010-02-25 12 views
12

Sto utilizzando VBOXMANAGE per "esportare" un computer guest. VBOXManage è un'applicazione console in grado di controllare il comportamento della macchina guest dall'host. Dal momento che il comando di esportazione è un processo lungo, restituisce gli aggiornamenti di processo in questo modo:Reindirizzamento dell'output della console in realtime utilizzando il processo

0% ... 10% ... 20% ... 30% ... 100%

sto scrivendo un Applicazione C# che invocherà VBOXManage usando Process. Ecco il mio codice:

Process VBOXProc = new Process(); 

VBOXProc.StartInfo.FileName = VBOXMANAGE; 
VBOXProc.StartInfo.Arguments = Arguments; 
VBOXProc.StartInfo.UseShellExecute = false; 
VBOXProc.StartInfo.CreateNoWindow = true; 
VBOXProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
VBOXProc.StartInfo.RedirectStandardError = true; 
VBOXProc.StartInfo.RedirectStandardOutput = true; 

VBOXProc.OutputDataReceived += new DataReceivedEventHandler(VBOXProc_OutputDataReceived); 
VBOXProc.ErrorDataReceived += new DataReceivedEventHandler(VBOXProc_ErrorDataReceived); 

VBOXProc.EnableRaisingEvents = true; 

VBOXProc.Start(); 
VBOXProc.BeginOutputReadLine(); 
VBOXProc.BeginErrorReadLine(); 

VBOXProc.WaitForExit(); 

Questo va bene, tranne che l'uscita viene letta per LINE. Ciò significa che il processo aggiorna " 0% ... 10% ... 20% ... 30% ... 100%" mostrerà solo DOPO che il processo effettivo è terminato.

C'è un modo per acquisire l'output della console in tempo reale?

Grazie!

+0

Nota il nome della funzione: BeginOutput * ReadLine * –

+0

Sì, grazie nobugz per quella meravigliosa intuizione. ;) – Ian

+0

Un paio di istruzioni 'With' renderebbero questo codice molto più semplice per gli occhi (e gli appunti) ...' Con VBOXProc ... Con .StartInfo ... End With ... End With'. – Basic

risposta

6

È possibile leggere direttamente da StanadardOutput/Error per il processo utilizzando tutti i metodi standard di Stream, accertarsi di impostare StartInfo.Redirectxxx su true.

var p = new Process() 
p.StartInfo.UseShellExecute = false; //not sure if this is absolutely required 
p.StartInfo.RedirectStandardOuput = true; 
.... 

do 
{ 
    Thread.Sleep(nnn); 
    Console.Out.Write(p.StandardOutput.ReadToEnd()); 
} 
while (!p.HasExited); 
//catch any leftovers in redirected stdout 
Console.Out.Write(p.StandardOutput.ReadToEnd()); 

Quanto sopra sarà eco l'uscita del processo figlio per le applicazioni standard out.

È possibile leggere blocchi di una dimensione specifica utilizzando p.StandardOutput.Read (char [], int, int) o letture asincrone utilizzando p.StadardOutput.BaseStream.BeginRead (...).

Tutti gli stessi metodi sono disponibili per StandardError.

Dormire nel ciclo libera il processore per altre attività e consente l'accumulo di alcuni dati nel buffer. Se il periodo di sospensione è troppo lungo e il buffer trabocca, alcuni output del processo in esecuzione andranno persi. Se il periodo di sospensione è troppo breve, vengono trascorsi molti cicli della CPU e il buffer vuoto viene letto.

+0

Ciao, il codice sopra è in qualche modo inaffidabile ... Sto ricevendo dei testi troncati. – Ian

+0

Sì, a seconda della tempistica delle scritture su stdout e sulla fine del processo, potrebbero esserci alcuni dati rimasti nel buffer dopo che il ciclo è terminato. Un'altra lettura dopo il ciclo raccoglierà eventuali dati mancanti. Principalmente volevo sottolineare che tutti i metodi di streaming sono disponibili. Nel codice di produzione probabilmente dormirò o altrimenti rilascerò il processore nel ciclo e leggerò solo "occaisionalmente". – ScottS

+1

ReadToEnd() si blocca mentre il processo è in esecuzione. – Herman

5

questo ha lavorato per me

 process.StartInfo.CreateNoWindow = true; 
     process.StartInfo.ErrorDialog = false; 
     process.StartInfo.RedirectStandardError = true; 
     process.StartInfo.RedirectStandardOutput = true; 
     process.StartInfo.UseShellExecute = false; 

     process.ErrorDataReceived += (sendingProcess, errorLine) => error.AppendLine(errorLine.Data); 
     process.OutputDataReceived += (sendingProcess, dataLine) => SetMessage(dataLine.Data); 

     process.Start(); 
     process.BeginErrorReadLine(); 
     process.BeginOutputReadLine(); 

     process.WaitForExit(); 

error.AppendLine() e setMessage() sono i miei metodi.

+0

Funzionerà solo se il flusso di output è terminato. Ma con il mio esempio, il flusso di output è su una singola riga e termina solo quando il processo è terminato. – Ian

0

Provare a reindirizzare anche l'input standard e applicare AutoFlush a StandardInput. Successivo leggi stream usando StreamReader.

Process proces; 
ProcessStartInfo psi = new ProcessStartInfo(); 
psi.FileName = "test.exe"; 
psi.UseShellExecute = false; 
psi.CreateNoWindow = true; 
psi.RedirectStandardOutput = true; 
psi.RedirectStandardInput = true; 

proces = Process.Start(psi); 
proces.StandardInput.AutoFlush = true; 
0

spiacenti alcun errore, io sono brasiliano e per utilizzando Google Translate per scrivere questo testo.

Per coincidenza, sto anche facendo un programma che funziona con VBoxManage di Virtualbox. Nel mio caso volevo, tra le altre cose, convertire un disco virtuale. Anche ritardi e la percentuale con progresso anche

Sono riuscito a fare proprio questo creando un processo di volontà per eseguire il programma, e utilizzando un 'Dean North' classi utente l'altro question che è simile a questo. È importante utilizzare un thread per eseguire VBoxManage, altrimenti non è possibile utilizzare il testo ottenuto o visualizzare lo stato di avanzamento.

O texto é muito grande pra eu adicionar quatro espaços antes de cada linha e repassar.

Le classi sostituiscono la classe di sistema Process. Hai bisogno di non apportare alcuna modifica al codice, basta aggiungere un arquivo.cs con il testo passato dall'utente Dean North invece di Process p = new Process() uso FixedProcess p = new FixedProcess()

Dopo che era il mio codice:

private void BotaoParaTestes_Click(object sender, EventArgs e) 
{ 
    string linha = @"clonehd " + 
     "\"Z:\\Máquinas Virtuais\\Teste.vdi\" " + 
     "\"C:\\Temp\\teste.vdi\" " + 
     "--format VDI --variant Standard"; 

    Thread tarefa = new Thread(Executar); 
    tarefa.Start(linha); 
} 
private void Executar(object Linha) 
{ 
    FixedProcess fp = new FixedProcess(); 
    fp.StartInfo.FileName = ItensEstaticos.VBox; 
    fp.StartInfo.Arguments = Linha.ToString(); 
    fp.StartInfo.CreateNoWindow = true; 
    fp.StartInfo.ErrorDialog = false; 
    fp.StartInfo.RedirectStandardError = true; 
    fp.StartInfo.RedirectStandardOutput = true; 
    fp.StartInfo.UseShellExecute = false; 
    fp.ErrorDataReceived += (sendingProcess, errorLine) => Escrita(errorLine.Data); 
    fp.OutputDataReceived += (sendingProcess, dataLine) => Escrita(dataLine.Data); 
    fp.Start(); 
    fp.BeginErrorReadLine(); 
    fp.BeginOutputReadLine(); 

    fp.WaitForExit(); 
} 
private void Escrita(string Texto) 
{ 
    if (!string.IsNullOrEmpty(Texto)) 
    { 
     BeginInvoke(new MethodInvoker(delegate 
     { 
      this.Texto.Text += Texto; 
     })); 
    } 
} 

Per me l'evento viene chiamato solo quando il testo viene modificato, non solo quando VBoxManage passa a una nuova riga. A volte il testo era nullo, quindi posto una struttura di controllo come ho fatto prima di usare il testo ottenuto per i controlli.

Problemi correlati