我有以下代码:

info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents

我知道我正在启动的进程的输出约为7MB。在Windows控制台中运行它可以正常工作。不幸的是,它以编程方式无限期地卡在WaitForExit上。还要注意,对于较小的输出(如3KB),此代码不会挂起。

ProcessStartInfo中的内部StandardOutput是否可能无法缓冲7MB?如果是这样,我应该怎么做呢?如果没有,我在做什么错?

最佳答案

问题是,如果您重定向StandardOutput和/或StandardError,则内部缓冲区可能已满。无论您使用什么顺序,都可能出现问题:

  • 如果您在读取StandardOutput之前等待进程退出,则该进程可能会阻止尝试对其进行写入,因此该进程永远不会结束。
  • 如果您使用ReadToEnd从StandardOutput进行读取,则如果该进程从不关闭StandardOutput(例如,它永不终止,或者被阻止写入StandardError),则该进程可能会阻塞。

  • 解决方案是使用异步读取来确保缓冲区未满。为了避免任何死锁并收集StandardOutputStandardError的所有输出,您可以执行以下操作:

    编辑:请参阅下面的答案,以了解如果发生超时,如何避免 ObjectDisposedException
    using (Process process = new Process())
    {
        process.StartInfo.FileName = filename;
        process.StartInfo.Arguments = arguments;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
    
        StringBuilder output = new StringBuilder();
        StringBuilder error = new StringBuilder();
    
        using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
        using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
        {
            process.OutputDataReceived += (sender, e) => {
                if (e.Data == null)
                {
                    outputWaitHandle.Set();
                }
                else
                {
                    output.AppendLine(e.Data);
                }
            };
            process.ErrorDataReceived += (sender, e) =>
            {
                if (e.Data == null)
                {
                    errorWaitHandle.Set();
                }
                else
                {
                    error.AppendLine(e.Data);
                }
            };
    
            process.Start();
    
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
    
            if (process.WaitForExit(timeout) &&
                outputWaitHandle.WaitOne(timeout) &&
                errorWaitHandle.WaitOne(timeout))
            {
                // Process completed. Check process.ExitCode here.
            }
            else
            {
                // Timed out.
            }
        }
    }
    

    09-27 03:31