在为 WaitForExit() 提供最长等待时间时,如何可靠地读取 c# 中进程的完整输出?

How can I reliably read the full output of a process in c# when providing a max wait time to WaitForExit()?

考虑以下程序。在这里我开始一个简单的过程并想处理它的输出。我假设 WaitForExit 返回后会出现这种情况,但事实证明,我必须等待整整一秒,直到该输出实际到达我的程序。

static void Main(string[] args)
{
    using var p = new Process();
    p.StartInfo.FileName = "echo";
    p.StartInfo.Arguments = "I apologize for being late";
    p.StartInfo.CreateNoWindow = false;
    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    var stdError = new StringBuilder();
    var stdOutput = new StringBuilder();
    p.ErrorDataReceived += (sender, args) => stdError.AppendLine(args.Data);
    p.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data);
    p.Start();
    p.BeginErrorReadLine();
    p.BeginOutputReadLine();
    // without the int-parameter here, it works
    while (!p.WaitForExit(10000))
        Console.WriteLine("still waiting...");
    string a = stdOutput.ToString();
    string b = stdError.ToString();
    Thread.Sleep(1000);
    string c = stdOutput.ToString();
    string d = stdError.ToString();
    Console.WriteLine("output before sleep: " + a);
    Console.WriteLine("error  before sleep: " + b);
    Console.WriteLine("output after  sleep: " + c);
    Console.WriteLine("error  after  sleep: " + d);
}

输出

output before sleep:
error  before sleep:
output after  sleep: I apologize for being late


error  after  sleep:

我希望 ac 具有完全相同的值。但事实并非如此。我将如何修改此示例,以便我可靠地接收过程的完整输出,但不调用 Thread.Sleep(1000)?

备注:

这里有一个尴尬的实现细节。

通话中

p.WaitForExit();

p.WaitForExit(10000);

当实际的本机进程句柄收到信号时,做一些稍微不同的事情。

内部 p.WaitForExit(); 调用 p.WaitForExit(-1);。 -1 在这里很重要。让我们看看我们有什么(代码被简化/解释以显示本质):

public bool WaitForExit(int milliseconds)
{
    // init stuff removed
    bool flag;
    try
    {
        flag = processWaitHandle.WaitOne(milliseconds, false);
    }
    finally
    {
        // here we see our -1 return
        if (this.output != null && milliseconds == -1)
        {
            this.output.WaitUtilEOF();
        }
    }
    return flag;
}

在上面的代码片段中,您看到 this.output.WaitUtilEOF(); 并且它调用了一个使用队列的内部 AsyncStreamReader。对 WaitUtilEOF(); 的调用基本上是在流上等待引发 EOF 事件。

我找不到其他方法来强制进程 class 进行调用以等待那些 EOF 事件。唯一的选择是不带参数调用 WaitForExit()。然而,在调用 WaitForExit(10000) returned.

之后调用 WaitForExit(); 没有任何惩罚

因此,如果您在第一个 WaitForExit(10000) 上达到了超时,但您确定您宁愿等待更长的时间让 AsyncStreamReader 将它拥有的所有数据交给您,请调用 WaitForExit() 而不带参数让两个 AsyncStreamReader 清空他们的队列,然后 return 控制权交给你。这确实意味着如果您的进程没有结束,您现在将陷入等待,除非您自己杀死子进程,否则永远不会自行解决。