我有以下代码:

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?如果是,我该怎么办?如果不是,我做错了什么?


当前回答

我也有同样的问题,但原因不同。但是在Windows 8下会发生,而在Windows 7下不会。下面这行似乎是导致这个问题的原因。

pProcess.StartInfo.UseShellExecute = False

解决方案是不禁用UseShellExecute。我现在收到一个Shell弹出窗口,这是不需要的,但比程序等待什么特别的事情发生要好得多。所以我添加了如下的解决方法:

pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden

现在唯一让我困扰的是为什么在Windows 8下会出现这种情况。

其他回答

未处理的ObjectDisposedException问题发生在进程超时时。在这种情况下,条件的其他部分:

if (process.WaitForExit(timeout) 
    && outputWaitHandle.WaitOne(timeout) 
    && errorWaitHandle.WaitOne(timeout))

不执行。我用以下方法解决了这个问题:

using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
{
    using (Process process = new Process())
    {
        // preparing ProcessStartInfo

        try
        {
            process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        outputBuilder.AppendLine(e.Data);
                    }
                };
            process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        errorBuilder.AppendLine(e.Data);
                    }
                };

            process.Start();

            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            if (process.WaitForExit(timeout))
            {
                exitCode = process.ExitCode;
            }
            else
            {
                // timed out
            }

            output = outputBuilder.ToString();
        }
        finally
        {
            outputWaitHandle.WaitOne(timeout);
            errorWaitHandle.WaitOne(timeout);
        }
    }
}

我们也有这个问题(或一个变体)。

试试下面的方法:

1)为p.WaitForExit(nnnn)添加超时;其中NNNN以毫秒为单位。

2)将ReadToEnd调用放在WaitForExit调用之前。这是我们看到MS推荐的。

罗布接了电话,帮我省了几个小时的时间。在等待之前读取输出/错误缓冲区:

// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

我也有同样的问题,但原因不同。但是在Windows 8下会发生,而在Windows 7下不会。下面这行似乎是导致这个问题的原因。

pProcess.StartInfo.UseShellExecute = False

解决方案是不禁用UseShellExecute。我现在收到一个Shell弹出窗口,这是不需要的,但比程序等待什么特别的事情发生要好得多。所以我添加了如下的解决方法:

pProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden

现在唯一让我困扰的是为什么在Windows 8下会出现这种情况。

让我们将这里发布的示例代码称为重定向程序,将另一个程序称为重定向程序。如果是我,我可能会写一个测试重定向程序,可以用来复制这个问题。

于是我照做了。对于测试数据,我使用ECMA-334 c#语言规范v PDF;大约5MB。下面是其中的重要部分。

StreamReader stream = null;
try { stream = new StreamReader(Path); }
catch (Exception ex)
{
    Console.Error.WriteLine("Input open error: " + ex.Message);
    return;
}
Console.SetIn(stream);
int datasize = 0;
try
{
    string record = Console.ReadLine();
    while (record != null)
    {
        datasize += record.Length + 2;
        record = Console.ReadLine();
        Console.WriteLine(record);
    }
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Error: {ex.Message}");
    return;
}

datasize值与实际文件大小不匹配,但这无关紧要。目前还不清楚PDF文件是否总是在行尾同时使用CR和LF,但这并不重要。您可以使用任何其他大型文本文件进行测试。

使用这种方法,当我写入大量数据时,示例重定向器代码挂起,而当我写入少量数据时则不会挂起。

我试图以某种方式跟踪该代码的执行,但我做不到。我注释掉了重定向程序中禁用为重定向程序创建控制台的行,以尝试获得单独的控制台窗口,但我不能。

然后我发现了如何在新窗口、父窗口或没有窗口中启动控制台应用程序。所以很明显,当一个控制台程序在没有ShellExecute的情况下启动另一个控制台程序时,我们不能(很容易)拥有一个单独的控制台,因为ShellExecute不支持重定向,我们必须共享一个控制台,即使我们没有为其他进程指定窗口。

我假设,如果重定向程序在某个地方填满了缓冲区,那么它必须等待数据被读取,如果此时重定向程序没有读取数据,那么它就是死锁。

解决方案是不使用ReadToEnd,而是在写入数据时读取数据,但没有必要使用异步读取。解决方法很简单。下面是5 MB的PDF文件。

ProcessStartInfo info = new ProcessStartInfo(TheProgram);
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
string record = p.StandardOutput.ReadLine();
while (record != null)
{
    Console.WriteLine(record);
    record = p.StandardOutput.ReadLine();
}
p.WaitForExit();

另一种可能是使用GUI程序进行重定向。上面的代码在WPF应用程序中工作,除非进行了明显的修改。