【问题标题】:Read from process stdout into memory stream asynchronously从进程标准输出异步读取到内存流
【发布时间】:2012-11-30 14:09:47
【问题描述】:

更新: 关键部分是我们使用的流对于 copyto\copytoasync 使用的缓冲区来说太大了,因此我们需要异步处理流,而不是异步运行整个任务部分,比如下面链接的问题

请原谅这里的 VB.net 代码。我也会说 C#,所以请随意用任何一种语言回复。

我一直在关注ProcessStartInfo hanging on "WaitForExit"? Why? 的示例,以尝试解决缓冲区空间不足的问题

之前我们尝试过这段代码:

        Dim buffer As Byte() = New Byte(32767) {}
        Dim file As Byte()
        Using ms = New MemoryStream()
            While True
                Dim read As Integer = Process.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length)

                If read <= 0 Then
                    Exit While
                End If
                ms.Write(buffer, 0, read)
            End While
            file = ms.ToArray()
        End Using

        If Not Process.WaitForExit(timeOut) Then
            Throw New Exception("Html to PDF conversion timed out.")
        End If

现在我已经开始将其从链接问题转换为 aynch 方法,但是在写入内存流而不是字符串构建器时遇到了问题。这是我到目前为止所得到的:

Dim output = new MemoryStream()
Dim errorOutput = new StringBuilder()

Using process = New Process()

    Using outputWaitHandle As New AutoResetEvent(False)
        Using errorWaitHandle As New AutoResetEvent(False)

            process.OutputDataReceived = Function(sender, e)
                If e.Data Is Nothing Then
                    outputWaitHandle.Set()
                Else
                    output.Write(e.Data); //compile error here
                End If

            End Function

        End Using
    End Using

End Using

当然 e.Data 是一个字符串,但不仅如此,我还需要一个缓冲区和一个偏移量……而且不知道在这里提供什么。

欢迎任何建议,谢谢!

【问题讨论】:

    标签: .net asynchronous process stdout memorystream


    【解决方案1】:

    不用OutputDataReceived,你可以直接使用流:

    static async Task<MemoryStream> ReadProcessOutput(ProcessStartInfo psi)
    {
        MemoryStream ms = new MemoryStream();
    
        using (Process p = new Process())
        {
            p.StartInfo = psi;
    
            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
            EventHandler eh = (s, e) => tcs.TrySetResult(0);
    
            p.Exited += eh;
    
            try
            {
                p.EnableRaisingEvents = true;
                p.Start();
    
                await p.StandardError.BaseStream.CopyToAsync(ms);
                await tcs.Task;
            }
            finally
            {
                p.Exited -= eh;
            }
        }
    
        return ms;
    }
    

    【讨论】:

    • 谢谢你......这个答案没有任何问题,只是现在我遇到了同样的问题,但在单独的线程上运行!关键部分是我们使用的流对于 copyto\copytoasync 使用的缓冲区来说太大了,因此我们需要异步处理部分流,而不是异步运行整个任务,就像问题中链接的示例一样。
    • 啊哈,好的,我现在明白了。是的,您可以只保留 ReadAsync() 并分块处理,而不是 CopyToAsync 吗?不清楚是否需要特定的块大小,是否由输入确定,还是无关紧要?
    【解决方案2】:

    您可能希望考虑使用StreamWriter 类,而不仅仅是一个 MemoryStream。这将允许您执行以下操作:

    MemoryStream output = new MemoryStream();
    StreamWriter outputWriter = new StreamWriter(output);
    
    ...[snip]...
    
    outputWriter.Write(e.Data);
    

    【讨论】: