如何正确读取C#进程标准错误流而没有死锁?

时间:2019-06-12 23:48:40

标签: c# .net-core

我真的觉得我正在遵循MSDN的最佳做法。但是我很可能会丢失一些东西,因为我的代码从以下这一行继续:string errorOutput = cmd.StandardError.ReadToEnd();后挂起。我在做什么错了?

        var batchfile = File.OpenWrite("run.bat");
        StreamWriter writer = new StreamWriter(batchfile);
        writer.Write("dotnet run" + '\n');
        //writer.Write("set /p temp=\"Hit enter to continue\"" + '\n');
        writer.Close();

        var cmd = new Process();
        cmd.StartInfo.FileName = batchfile.Name;
        cmd.StartInfo.UseShellExecute = false;
        cmd.StartInfo.RedirectStandardError = true;

        cmd.Start();
        string errorOutput = cmd.StandardError.ReadToEnd();
        cmd.WaitForExit();

        var outputfile = File.OpenWrite("run_errorOut.txt");
        StreamWriter outputWriter = new StreamWriter(outputfile);
        outputWriter.Write(errorOutput);
        outputWriter.Close();

以防万一:目前,此代码正在dotnetcore2.2应用程序(目标框架为'netcoreapp2.2')的xunit测试中运行。

1 个答案:

答案 0 :(得分:3)

所以不久前我遇到了一个非常相似的问题,几乎完全一样,而我发现的是:

  • 在没有任何要读取的块的情况下尝试读取StandardError / Output,直到有需要读取的内容为止(否则,如果没有写入任何内容,则死锁-例如,进程已退出)
  • cmd.HasExited为假,直到已读取StandardError / Output,以便无法用来检测何时完成(然后读取StandardError / Output)

我发现最可靠的方法是使用Process上提供的事件:

var outData = new StringBuilder();
var errData = new StringBuilder();    

cmd.OutputDataReceived += (sender, args) => outData.Append(args.Data ?? string.Empty);
cmd.ErrorDataReceived += (sender, args) => errData.Append(args.Data ?? string.Empty);

然后在cmd.Start() == true之后:

cmd.BeginErrorReadLine();
cmd.BeginOutputReadLine();

开始触发事件。

该过程结束后,您可以在StringBuilders上调用.ToString()以获取数据:

Console.WriteLine(outData.ToString());
Console.WriteLine(errData.ToString());

(请注意,使用.ToString()Console.WriteLine可以隐含)