5

我正在我的 C# 应用程序中启动一个运行控制台应用程序的进程。我已经重定向了标准输入和输出,并且能够通过 StandardOutput.ReadLine() 读取几行。我确信我正确配置了 ProcessStartInfo。

控制台应用程序在启动时会输出几行(以“标记”行结尾),然后等待输入。接收到输入后,它再次输出几行(再次以“标记”行结束),依此类推。我的意图是从中读取行,直到收到“标记”行,此时我知道要发送适当的输入字符串。

我的问题是,经过几次迭代,程序挂起。暂停调试器往往会将挂起置于对 StandardOutput.EndOfStream 的调用中。下面的测试代码就是这种情况:

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

当我测试“标记”行时,如果我在阅读该行后尝试访问 StandardOutput.EndOfStream,我会遇到同样的问题:

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

我可能在做什么导致这个属性表现得如此糟糕?

4

4 回答 4

10

您不能在这里可靠地使用 EndOfStream。如果没有缓冲任何字符,StreamReader.EndOfStream 属性将调用 StandardOutput.Read()。如果进程没有向其输出管道发送任何内容并且没有关闭它,则该 Read() 调用将阻塞。这几乎可以保证发生,因为它将等待输入。在进程关闭输出管道的末端并且 StreamReader 已消耗其所有缓冲字符之前,EndOfStream 不会返回 true。在程序终止时。

使用 BeginOutputReadLine() 可能是检测“标记”线的更好方法。请注意回调发生在另一个线程上。另请注意,不必等待进程发送标记,您写入的任何内容都将被缓冲,直到进程准备好读取它。请注意缓冲区很小,可能会出现死锁。

于 2010-05-04T17:42:17.147 回答
1

在与流程类交互时,有许多路径可以创建死锁。Microsoft 在此处的 MSDN 站点上对其进行了描述。我是这样称呼它的。注意 ErrorDataReceived 和 OutputDataReceived 的处理以及对 BeginErrorReadLine 和 BeginOutputReadLine 的调用。这通过让父进程异步读取流来消除死锁情况。注意:RunProcessResponse 是我自己的小包装数据传输对象。

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
于 2011-02-18T15:09:06.930 回答
0

在从标准输出中读取之前,您是否等待该过程完成:

mProcess.WaitForExit();
于 2010-05-04T17:29:01.360 回答
0

如果您知道在您执行以下操作后没有更多标准输入:

while (!mProcess.StandardOutput.EndOfStream) 

循环,您可以使用以下命令关闭标准输入:

mProcess.StandardInput.Close();

表示没有更多的输入。只要标准输入是开放的,就有可能有更多的输入,从而有更多的输出;因此,标准输出永远不会到达 EndOfStream。

于 2016-07-10T20:48:12.913 回答