2010-02-25 12 views
12

Estoy usando VBOXMANAGE para "exportar" una máquina invitada. VBOXManage es una aplicación de consola que puede controlar el comportamiento de la máquina invitada desde el host. Desde el comando de exportación es un proceso largo, devuelve las actualizaciones de proceso de esta manera:Redirección de salida de consola en tiempo real usando el proceso

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

estoy escribiendo una Aplicación C# que invocará VBOXManage usando Process. Aquí está mi código:

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(); 

Esto está bien, excepto que la salida se lee por LINEA. Esto significa que las actualizaciones del proceso " 0% ... 10% ... 20% ... 30% ... 100%" solo se mostrarán DESPUÉS de que se complete el proceso.

¿Hay alguna manera de capturar la salida de la consola en tiempo real?

Gracias!

+0

Tenga en cuenta el nombre de la función: BeginOutput * ReadLine * –

+0

Sí, gracias nobugz por esa maravillosa idea. ;) – Ian

+0

Un par de declaraciones 'With' harían que ese código fuera mucho más fácil para los ojos (y el portapapeles) ...' Con VBOXProc ... Con .StartInfo ... End With ... End With'. – Basic

Respuesta

6

Puede leer directamente desde StanadardOutput/Error para el proceso utilizando todos los métodos estándar de Stream, solo asegúrese de establecer StartInfo.Redirectxxx en 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()); 

Lo anterior hará eco de la salida del proceso hijo a sus aplicaciones Salida estándar.

Puede leer Bloques de un tamaño en particular usando p.StandardOutput.Read (char [], int, int) o lecturas asincrónicas usando p.StadardOutput.BaseStream.BeginRead (...).

Todos los mismos métodos están disponibles para StandardError.

Dormir en el ciclo libera el procesador para otras tareas y permite que algunos datos se acumulen en el bufffer. Si el período de espera es demasiado largo y el búfer se desborda, se perderá parte de la producción del proceso de ejecución. Si el período de reposo es demasiado corto, se gastan muchos ciclos de CPU leyendo y vaciando el buffer.

+0

Hola, el código anterior es poco confiable ... Estoy recibiendo textos truncados. – Ian

+0

Sí, dependiendo de la temporización de las escrituras para la terminación estándar y la finalización del proceso, puede que queden algunos datos en el búfer una vez finalizado el ciclo. Otra lectura después del ciclo recogerá cualquier dato faltante. Principalmente quería señalar que todos los métodos de transmisión están disponibles. En el código de producción, probablemente dormiría o liberaría el procesador en el ciclo y solo leería "ocasionalmente". – ScottS

+1

ReadToEnd() se bloqueará mientras se está ejecutando el proceso. – Herman

5

Esto funcionó para mí

 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() y setMessage() son mis métodos.

+0

solo funcionará si el flujo de salida finaliza. Pero con mi ejemplo, el flujo de salida está en una sola línea y solo termina cuando finaliza el proceso. – Ian

0

Intente redirigir la entrada estándar también y aplique AutoFlush a StandardInput. Siguiente lectura de secuencia 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

Disculpe, por cualquier error, soy brasileño y uso de Google Translate para escribir este texto.

Coincidentemente, también estoy haciendo un programa que funciona con VBoxManage de Virtualbox. En mi caso, quería, entre otras cosas, convertir un disco virtual. También retrasa y el porcentaje con progreso también

Logré hacer esto simplemente creando un proceso de voluntad para ejecutar el programa, y ​​usando un usuario de clases 'Dean North' el otro question que es similar a esto. Es importante utilizar un hilo para ejecutar el VBoxManage, de lo contrario no tiene forma de trabajar el texto obtenido o ver el progreso.

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

Las clases reemplazan la clase del sistema Process. No es necesario realizar ningún cambio en su código, basta con añadir un arquivo.cs con el texto aprobado por el usuario en lugar de Dean NorthProcess p = new Process() uso FixedProcess p = new FixedProcess()

Después de que se trataba de mi código:

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; 
     })); 
    } 
} 

Para mí el evento solo se invoca cuando se cambia el texto, no solo cuando VBoxManage pasa a una nueva línea. A veces el texto era nulo, luego coloca una estructura de verificación como lo hice antes de usar el texto obtenido para los controles.

Cuestiones relacionadas