StandardOutput.ReadToEnd()挂起

Ela*_*ron 45 c# stream hang redirectstandardoutput

我有一个经常使用外部程序并读取其输出的程序.它使用你通常的进程重定向输出很好地工作,但是当我尝试读取它时,由于某种原因,一个特定的参数会挂起,没有错误消息 - 没有例外,当它到达该行时它只是'停止'.我当然使用集中式函数来调用和读取程序的输出,这是:

public string ADBShell(string adbInput)
{
    try
    {
        //Create Empty values
        string result = string.Empty;
        string error = string.Empty;
        string output = string.Empty;
        System.Diagnostics.ProcessStartInfo procStartInfo 
            = new System.Diagnostics.ProcessStartInfo(toolPath + "adb.exe");

        procStartInfo.Arguments = adbInput;
        procStartInfo.RedirectStandardOutput = true;
        procStartInfo.RedirectStandardError = true;
        procStartInfo.UseShellExecute = false;
        procStartInfo.CreateNoWindow = true;
        procStartInfo.WorkingDirectory = toolPath;
        System.Diagnostics.Process proc = new System.Diagnostics.Process();
        proc.StartInfo = procStartInfo;
        proc.Start();
        // Get the output into a string
        proc.WaitForExit();
        result = proc.StandardOutput.ReadToEnd();
        error = proc.StandardError.ReadToEnd();  //Some ADB outputs use this
        if (result.Length > 1)
        {
            output += result;
        }
        if (error.Length > 1)
        {
            output += error;
        }
        Return output;
    }
    catch (Exception objException)
    {
        throw objException;
    }
}
Run Code Online (Sandbox Code Playgroud)

挂起的行是result = proc.StandardOutput.ReadToEnd();,但不是每次都是,只有在发送特定参数("start-server")时才会挂起.所有其他参数都可以正常工作 - 它读取值并返回它.挂起的方式也很奇怪.它不会冻结或给出错误或任何东西,它只是停止处理.好像它是一个'return'命令,除了它甚至没有返回到调用函数,它只是停止所有接口仍然启动并运行.以前有人经历过吗?任何人都知道我应该尝试什么?我假设它在流本身内是意想不到的东西,但有没有办法可以处理/忽略它以便它无论如何都能读取它?

Fed*_*dor 52

建议的解决方案BeginOutputReadLine()是一种很好的方法,但在这种情况下,它是不适用的,因为进程(当然使用WaitForExit())退出的时间早于异步输出完全完成.

所以,我试图同步实现它,并发现解决方案是使用类中的Peek()方法StreamReader.我添加了检查Peek() > -1以确保它不是在MSDN文章中所描述的流的结尾,最后它工作并停止挂起!

这是代码:

var process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WorkingDirectory = @"C:\test\";
process.StartInfo.FileName = "test.exe";
process.StartInfo.Arguments = "your arguments here";

process.Start();
var output = new List<string>();

while (process.StandardOutput.Peek() > -1)
{
    output.Add(process.StandardOutput.ReadLine());
}

while (process.StandardError.Peek() > -1)
{
    output.Add(process.StandardError.ReadLine());
}
process.WaitForExit();
Run Code Online (Sandbox Code Playgroud)

  • 我解决了这个问题,但我实现了其他人从另一个问题回答的问题,这里是我使用的答案的链接:http://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why (6认同)
  • 我刚刚实现了这个更改,我的进程仍然挂在 process.StandardError.ReadLine()... (4认同)
  • 不如检查“process.StandardOutput.EndOfStream”怎么样?使用“process.StandardOutput.Peek() &gt; -1”,仅显示多行输出的第一行 (2认同)
  • @ganders,当使用“Peek”时,流编写器实现中存在一个已知的[bug](/sf/ask/1697668591/) ()` 在空输出上它将永远挂起 (2认同)

Dan*_*rth 17

问题是您在流和流ReadToEnd上都使用同步方法.这可能会导致您遇到的潜在僵局.这甚至在MSDN中描述.那里描述了解决方案.基本上,它是:使用异步版本来读取流的数据:StandardOutputStandardErrorBeginOutputReadLineStandardOutput

p.BeginOutputReadLine();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Run Code Online (Sandbox Code Playgroud)

使用BeginOutputReadLine实现异步读取请参见挂载在"WaitForExit"上的ProcessStartInfo?为什么?

  • 感谢回复.我担心这不起作用,它一旦挂起'StandardError.ReadToEnd();'就会挂起.我甚至尝试过使用'BeginErrorReadLine();' 但那也挂了.DID唯一可行的是向'WaitForExit'添加超时.由于这个挂起的特定参数几乎立即提供输出,我在大约3秒时将其计时,一切正常.它不是很优雅,但它有效.再次感谢您的帮助. (2认同)

小智 5

怎么样的:

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

process.OutputDataReceived += (sender, args) =>
                               {
                                    var outputData = args.Data;
                                    // ...
                                };
process.ErrorDataReceived += (sender, args) =>
                            {
                                var errorData = args.Data;
                                // ...
                            };
process.WaitForExit();
Run Code Online (Sandbox Code Playgroud)