I am using .NET and C# to start a process and read it's output asynchronously. My problem is that there seems to be a delay before the output is read by my program. If I run the executable on the command line, there is output immediately when it starts running. But when I run it using my code the ReadOutput event handler isn't called until the Process exits. I want to use this to provide a real-time view of the process's output, so I don't want to wait (several minutes) until the process exits.
Here's some relevant code:
MyProcess = new Process();
MyProcess.StartInfo.FileName = command;
MyProcess.StartInfo.Arguments = args;
MyProcess.StartInfo.UseShellExecute = false;
MyProcess.StartInfo.RedirectStandardOutput = true;
MyProcess.StartInfo.RedirectStandardError = true;
MyProcess.StartInfo.RedirectStandardInput = true;
MyProcess.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
MyProcess.ErrorDataReceived += new DataReceivedEventHandler(ReadOutput);
if (!MyProcess.Start())
{
throw new Exception("Process could not be started");
}
try
{
MyProcess.BeginOutputReadLine();
MyProcess.BeginErrorReadLine();
}
catch (Exception ex)
{
throw new Exception("Unable to begin asynchronous reading from process";
}
And here's my event handler:
private void ReadOutput(object sendingProcess, DataReceivedEventArgs outLine)
{
OutputBuilder.AppendLine(outLine.Data);
Console.WriteLine(outLine.Data);
Console.Out.Flush();
}
This is the way I do it (C# 3) as per my comment using the lambda syntax.
/// <summary>
/// Collects standard output text from the launched program.
/// </summary>
private static readonly StringBuilder outputText = new StringBuilder();
/// <summary>
/// Collects standard error text from the launched program.
/// </summary>
private static readonly StringBuilder errorText = new StringBuilder();
/// <summary>
/// The program's entry point.
/// </summary>
/// <param name="args">The command-line arguments.</param>
/// <returns>The exit code.</returns>
private static int Main(string[] args)
{
using (var process = Process.Start(new ProcessStartInfo(
"program.exe",
args)
{
CreateNoWindow = true,
ErrorDialog = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false
}))
{
process.OutputDataReceived += (sendingProcess, outLine) =>
outputText.AppendLine(outLine.Data);
process.ErrorDataReceived += (sendingProcess, errorLine) =>
errorText.AppendLine(errorLine.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine(errorText.ToString());
Console.WriteLine(outputText.ToString());
return process.ExitCode;
}
The problem in your approach is probably that the process only finishes the output of a line when it exits. There's no way to control when the asynchronous event handlers fire.
In a console application, your best bet is to periodically check for new output and read and display it synchronously:
while (!p.HasExited)
{
if (!p.StandardOutput.EndOfStream)
{
errorBuilder.Append(p.StandardError.ReadToEnd());
outputBuilder.Append(p.StandardOutput.ReadToEnd());
Console.Write(p.StandardOutput);
}
else
{
Thread.Sleep(200);
}
}
In a UI project, you'd use Timer or DispatcherTimer for WinForms and WPF, respectively, to call the contents of the loop and update the UI.
Note that I don't flush Console.Out, as Console.Write() and Console.WriteLine() cause this automatically.
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