StandardOutput.ReadToEnd() hangs [duplicate]

2019-01-06 13:36发布

This question already has an answer here:

I have a program that frequently uses an external program and reads its outputs. It works pretty well using your usual process redirect output, but one specific argument for some reason hangs when I try to read it, no error message - no exception, it just 'stops' when it reaches that line. I of course use a centralized function to call and read output from the program, which is this:

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;
    }
}

The line that hangs is result = proc.StandardOutput.ReadToEnd();, but again, not every time, only when sent a specific argument ("start-server"). All other arguments work just fine - it reads the value and returns it. It's also strange the way it hangs. It doesn't freeze or give an error or anything, it just stops processing. As if it was a 'return' command, except it doesn't even return to the calling function, it just stops everything with the interface still up and running. Anyone experienced this before? Anyone have any idea what I should try? I'm assuming it's something unexpected within the stream itself, but is there a way I can handle/ignore this so that it reads it anyway?

9条回答
唯我独甜
2楼-- · 2019-01-06 14:13

first

     // Start the child process.
     Process p = new Process();
     // Redirect the output stream of the child process.
     p.StartInfo.UseShellExecute = false;
     p.StartInfo.RedirectStandardOutput = true;
     p.StartInfo.FileName = "Write500Lines.exe";
     p.Start();
     // Do not wait for the child process to exit before
     // reading to the end of its redirected stream.
     // p.WaitForExit();
     // Read the output stream first and then wait.
     string output = p.StandardOutput.ReadToEnd();
     p.WaitForExit();

second

 // Do not perform a synchronous read to the end of both 
 // redirected streams.
 // string output = p.StandardOutput.ReadToEnd();
 // string error = p.StandardError.ReadToEnd();
 // p.WaitForExit();
 // Use asynchronous read operations on at least one of the streams.
 p.BeginOutputReadLine();
 string error = p.StandardError.ReadToEnd();
 p.WaitForExit();

This is from MSDN

查看更多
别忘想泡老子
3楼-- · 2019-01-06 14:14

I had the same deadlock problem. This code snippet worked for me.

        ProcessStartInfo startInfo = new ProcessStartInfo("cmd")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        };

        Process process = new Process();
        process.StartInfo = startInfo;
        process.Start();
        process.StandardInput.WriteLine("echo hi");
        process.StandardInput.WriteLine("exit");
        var output = process.StandardOutput.ReadToEnd();
        process.Dispose();
查看更多
何必那么认真
4楼-- · 2019-01-06 14:17

Something that is elegant and worked for me is:

Process nslookup = new Process()
{
   StartInfo = new ProcessStartInfo("nslookup")
   {
      RedirectStandardInput = true,
      RedirectStandardOutput = true,
      UseShellExecute = false,
      CreateNoWindow = true,
      WindowStyle = ProcessWindowStyle.Hidden
   }
};

nslookup.Start();
nslookup.StandardInput.WriteLine("set type=srv");
nslookup.StandardInput.WriteLine("_ldap._tcp.domain.local"); 

nslookup.StandardInput.Flush();
nslookup.StandardInput.Close();

string output = nslookup.StandardOutput.ReadToEnd();

nslookup.WaitForExit();
nslookup.Close();

This answer I found here and the trick is using Flush() and Close() on standard input.

查看更多
Juvenile、少年°
5楼-- · 2019-01-06 14:19

What about something like:

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

process.OutputDataReceived += (sender, args) =>
                               {
                                    var outputData = args.Data;
                                    // ...
                                };
process.ErrorDataReceived += (sender, args) =>
                            {
                                var errorData = args.Data;
                                // ...
                            };
process.WaitForExit();
查看更多
仙女界的扛把子
6楼-- · 2019-01-06 14:24

Proposed solutions with BeginOutputReadLine() are a good way but in situations such as that, it is not applicable, because process (certainly with using WaitForExit()) exits earlier than async output finished completely.

So, I tried to implement it synchronously and found that the solution is in using Peek() method from StreamReader class. I added check for Peek() > -1 to sure that it is not the end of the stream as in MSDN article described and finally it works and stop hanging!

Here is the code:

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();
查看更多
够拽才男人
7楼-- · 2019-01-06 14:30

The accepted answer's solution didn't work for me. I had to use tasks in order to avoid the deadlock:

//Code to start process here

String outputResult = GetStreamOutput(process.StandardOutput);
String errorResult = GetStreamOutput(process.StandardError);

process.WaitForExit();

With a GetStreamOutput function as follows:

private string GetStreamOutput(StreamReader stream)
{
   //Read output in separate task to avoid deadlocks
   var outputReadTask = Task.Run(() => stream.ReadToEnd());

   return outputReadTask.Result;
}
查看更多
登录 后发表回答