在为 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:
我希望 a
和 c
具有完全相同的值。但事实并非如此。我将如何修改此示例,以便我可靠地接收过程的完整输出,但不调用 Thread.Sleep(1000)
?
备注:
- 我想要 stdOut 和 stdErr 的可靠完整输出
- 当我改用
p.WaitForExit()
时 p.WaitForExit(10000)
似乎一切正常
- 当对两个流使用
p.StandardOutput.ReadToEnd()
时,它似乎有效。但是我被告知 by the official documentation,这会导致死锁
- 当使用
p.StandardError.ReadToEnd()
并为输出使用异步解决方案时,输出仍然迟到。
- 这不是 Process WaitForExit not waiting 的副本,因为对他们来说
p.WaitForExit()
没有任何参数已经不起作用。而且他们对输出根本不感兴趣。
这里有一个尴尬的实现细节。
通话中
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 控制权交给你。这确实意味着如果您的进程没有结束,您现在将陷入等待,除非您自己杀死子进程,否则永远不会自行解决。
考虑以下程序。在这里我开始一个简单的过程并想处理它的输出。我假设 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:
我希望 a
和 c
具有完全相同的值。但事实并非如此。我将如何修改此示例,以便我可靠地接收过程的完整输出,但不调用 Thread.Sleep(1000)
?
备注:
- 我想要 stdOut 和 stdErr 的可靠完整输出
- 当我改用
p.WaitForExit()
时p.WaitForExit(10000)
似乎一切正常 - 当对两个流使用
p.StandardOutput.ReadToEnd()
时,它似乎有效。但是我被告知 by the official documentation,这会导致死锁 - 当使用
p.StandardError.ReadToEnd()
并为输出使用异步解决方案时,输出仍然迟到。 - 这不是 Process WaitForExit not waiting 的副本,因为对他们来说
p.WaitForExit()
没有任何参数已经不起作用。而且他们对输出根本不感兴趣。
这里有一个尴尬的实现细节。
通话中
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 控制权交给你。这确实意味着如果您的进程没有结束,您现在将陷入等待,除非您自己杀死子进程,否则永远不会自行解决。