I am using VBOXMANAGE to "export" a guest machine. VBOXManage is a Console application that can control the guest machine's behavior from the host. Since the export command is a long process, it returns process updates like so:
0%...10%...20%...30%...100%
I am writing a C# application that will invoke VBOXManage using Process. Here's my code:
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();
This is fine, except that the output is being read per LINE. This means that the process updates " 0%...10%...20%...30%...100%" will only show AFTER the actual process is done.
Is there a way to capture the console output in realtime?
Thanks!
This worked for 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()
and SetMessage()
are the methods I used.
You can read directly from the StanadardOutput/Error for the process using all the standard Stream methods, just be sure to set the StartInfo.Redirectxxx to 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());
The above will echo the output of the child process to your applications Standard Out.
You can read Blocks of a particular size using p.StandardOutput.Read(char[], int, int) or asynchronous reads using p.StadardOutput.BaseStream.BeginRead(...).
All the same methods are available for StandardError.
Sleeping in the loop frees up the processor for other tasks and allows some data to accumulate in the bufffer. If the sleep period is too long and the buffer overflows some output from the executing process will be lost. If the sleep period is too short a lot of CPU cycles are spent reading and empty buffer.
Try to redirect standard input too and apply AutoFlush to StandardInput. Next read stream using 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;
Sorry any mistake, I am Brazilian and to using Google Translate to write this text.
Coincidentally, I also'm doing a program that works with VBoxManage of Virtualbox. In my case I wanted, among other things, convert a virtual disk. Also it delays and the percentage with progress also
I managed to do just this by creating a process of will to run the program, and using a user classes 'Dean North` the other question that is similar to this. It is important to use a thread to run the VBoxManage, otherwise has no way to work the obtained text or view the progress.
O texto é muito grande pra eu adicionar quatro espaços antes de cada linha e repassar.
The classes replace the Process
system class. Need not make any changes to your code, just add a arquivo.cs with the text passed by the user Dean North
instead of Process p = new Process()
use FixedProcess p = new FixedProcess ()
After that it was my code:
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;
}));
}
}
For me the event is only called when the text is changed, not only when the VBoxManage goes to a new line. Sometimes the text was null, then place a check structure as I did before using the text obtained for controls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With