The System.Diagnostics.Process class provides the options to retrieve all output from an external process after it has completed, or to retrieve events containing the output as the execution occurs. The option to receive asynchronous output events is useful in scenarios where you need to provide user feedback or log the output when timeouts occur.

When receiving asynchronous output events, you must ensure that the external process has completed when you want to make sure that you get all the output. However, waiting for HasExited to be true is not enough. In order to ensure that all processing has completed, including the handling of asynchronous output events, you must call Process.WaitForExit(). You need to call the overload taking no arguments, even if you previously called Process.WaitForExit(Int32) overload. The Process.WaitForExit(Int32) docs state:

To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter after receiving a true from this overload.

Below is a simple example that uses Process.WaitForExit(Int32) to enforce timeouts and Process.WaitForExit() to flush the output events when the process completes.

static void DoExternalThing()
{
    var proc = new Process
    {
        EnableRaisingEvents = true,
        StartInfo = new ProcessStartInfo
        {
            FileName = "foo.exe",
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
        },
    };
    proc.ErrorDataReceived += proc_ErrorDataReceived;
    proc.OutputDataReceived += proc_OutputDataReceived;

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();

    // Timeout after 10 seconds
    if (!proc.WaitForExit(10000))
    {
        Console.WriteLine("Operation timed out");
        return;
    }

    // Flush async events
    proc.WaitForExit();
    Console.WriteLine("Operation complete.");
}