StandardOutput.EndOfStream Hangs

半世苍凉 提交于 2019-11-28 08:28:53

问题


I'm starting a process within my C# application which runs a console application. I've redirected standard input and output, and am able to read a few lines via StandardOutput.ReadLine(). I'm convinced I have the ProcessStartInfo configured correctly.

The console application, when started, outputs a few lines (ending with a "marker" line) and then waits for input. After receiving the input, it again outputs a few lines (ending again with a "marker" line), and so on. My intention is to read lines from it until I receive the "marker" line, at which point I know to send the appropriate input string.

My problem is that, after several iterations, the program hangs. Pausing the debugger tends to place the hang within a call to StandardOutput.EndOfStream. This is the case in the following test code:

while (!mProcess.StandardOutput.EndOfStream) // Program hangs here.
{
    Console.WriteLine(mProcess.StandardOutput.ReadLine());
}

When I'm testing for the "marker" line, I get the same kind of hang if I attempt to access StandardOutput.EndOfStream after reading the line:

string line = "";
while (!isMarker(line))
{
    line = mProcess.StandardOutput.ReadLine();
}
bool eos = mProcess.StandardOutput.EndOfStream; // Program hangs here.

What might I be doing that causes this property to perform so horribly?


回答1:


You cannot use EndOfStream reliably here. The StreamReader.EndOfStream property will call StandardOutput.Read() if it doesn't have any characters buffered. That Read() call will block if the process isn't sending anything to its output pipe and doesn't close it. Which is pretty much guaranteed to happen since it will be waiting for input. EndOfStream won't return true until the process has closed its end of the output pipe and the StreamReader has consumed all its buffered characters. At program termination.

Using BeginOutputReadLine() might be a better way to detect the "marker" line. Beware that the callback happens on another thread. Also note that it shouldn't be necessary to wait for the process to send the marker, anything you write will be buffered until the process is ready to read it. Beware that the buffers are smallish, deadlock is possible.




回答2:


There are numerous paths for which you can create a deadlock when interacting with a process class. Microsoft describes them on the MSDN site here. Here is how I call it. Notice the handling of ErrorDataReceived and OutputDataReceived and the calls to BeginErrorReadLine and BeginOutputReadLine. This eliminates deadlock scenarios by having the parent process read the streams asynchronously. NOTE: RunProcessResponse is my own little wrapper data transfer object.

Public Function RunProcess(ByVal executableFileName As String, ByVal arguments As String, ByVal workingDirectory As System.String) As RunProcessResponse
    Dim process As System.Diagnostics.Process = Nothing
    Dim response As RunProcessResponse

    Try
        process = New System.Diagnostics.Process()
        Dim psInfo As New System.Diagnostics.ProcessStartInfo()
        Dim errorString As System.String = String.Empty
        Dim outputString As System.String = String.Empty


        If Not System.String.IsNullOrEmpty(workingDirectory) Then
            psInfo.WorkingDirectory = workingDirectory
        End If

        psInfo.FileName = executableFileName
        psInfo.Arguments = arguments
        psInfo.WindowStyle = ProcessWindowStyle.Hidden
        psInfo.CreateNoWindow = True
        psInfo.RedirectStandardError = True
        psInfo.RedirectStandardOutput = True
        psInfo.UseShellExecute = False

        AddHandler process.ErrorDataReceived, Sub(sender As Object, args As DataReceivedEventArgs)
                                                  If args.Data IsNot Nothing Then
                                                      errorString &= args.Data & vbCrLf
                                                  End If
                                              End Sub
        AddHandler process.OutputDataReceived, Sub(sender As Object, args As DataReceivedEventArgs)
                                                   If args.Data IsNot Nothing Then
                                                       outputString &= args.Data & vbCrLf
                                                   End If
                                               End Sub

        process.StartInfo = psInfo
        process.Start()

        process.BeginErrorReadLine()
        process.BeginOutputReadLine()
        process.WaitForExit()

        response = New RunProcessResponse(errorString, outputString, process.ExitCode)

        Return response
    Finally
        If process IsNot Nothing Then
            process.Dispose()
        End If
    End Try
End Function



回答3:


Did you wait for the process to finish before reading from it's standard output:

mProcess.WaitForExit();



回答4:


If you know that there is no more standard input after your:

while (!mProcess.StandardOutput.EndOfStream) 

loop, you can close standard input with:

mProcess.StandardInput.Close();

To signal that there is no more input. As long as standard input is open, there is the potential for more input, thus more output; therefore, standard output will never reach EndOfStream.



来源:https://stackoverflow.com/questions/2767496/standardoutput-endofstream-hangs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!