17

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!

3
  • Note the function name: BeginOutput ReadLine Commented Feb 25, 2010 at 6:57
  • Yes, thank you nobugz for that wonderful insight. ;) Commented Feb 26, 2010 at 13:44
  • A couple of With Statements would make that code a lot easier on the eyes (and the clipboard)... With VBOXProc ... With .StartInfo ... End With ... End With. Commented Jul 7, 2012 at 0:21

4 Answers 4

10

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Will only work if output stream is terminated. But with my example, output stream is on a single line, and only terminated when process is done.
7

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.

3 Comments

Hi, the above code is somewhat unreliable... I am getting truncated texts.
Yes, depending on the timing of writes to stdout and process termination there may be some data left in the buffer after the loop terminates. Another read after the loop will pick up any missing data. I mainly wanted to point out that all the stream methods are available. In production code I would likely sleep or otherwise release the processor in the loop and only read "occaisionally".
ReadToEnd() will block while process is running.
1

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;

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.