将Process StandardOutput连接到另一个Process StandardInput

时间:2015-11-19 20:02:40

标签: c# .net

我试图在多个Process对象之间创建一个管道。

我可以运行单个Process并捕获其StandardOutput,但是当我尝试连接多个Process对象时,第二个似乎没有从第一个StandardInput接收任何数据。

在这段代码中,我使用cat,它将其参数的内容打印到stdout,或者只是将stdin复制到stdout。

使用一个过程可以正常工作:

// Works
private async void launchButtonClicked(object sender, EventArgs e)
{
    var outputBuffer = new MemoryStream();

    var tasks = new List<Task>();

    Process process1;

    {
        process1 = new Process();

        process1.StartInfo.FileName = "cat.exe";
        process1.StartInfo.Arguments = "K:\\temp\\streams.txt";
        process1.StartInfo.UseShellExecute = false;
        process1.StartInfo.RedirectStandardOutput = true;
        process1.StartInfo.RedirectStandardInput = false;
        process1.StartInfo.CreateNoWindow = true;
        process1.EnableRaisingEvents = true;
        process1.Start();
    }

    tasks.Add(process1.StandardOutput.BaseStream.CopyToAsync(outputBuffer));

    await Task.WhenAll(tasks);

    // OK: This prints the contents of the file
    Console.WriteLine("Final output: {0}", UTF8Encoding.UTF8.GetString(outputBuffer.GetBuffer()));

}

但是当我添加第二个进程并尝试将Process1的StandardOutput复制到Process2的StandardInput时,我从来没有从Process2获得任何输出并且await永远不会完成:

// Doesn't work
private async void launchButtonClicked(object sender, EventArgs e)
{
    var outputBuffer = new MemoryStream();

    var tasks = new List<Task>();

    Process process1, process2;

    {
        process1 = new Process();

        process1.StartInfo.FileName = "cat.exe";
        process1.StartInfo.Arguments = "K:\\temp\\streams.txt";
        process1.StartInfo.UseShellExecute = false;
        process1.StartInfo.RedirectStandardOutput = true;
        process1.StartInfo.RedirectStandardInput = false;
        process1.StartInfo.CreateNoWindow = true;
        process1.Start();
    }

    {
        process2 = new Process();

        process2.StartInfo.FileName = "cat.exe";
        process2.StartInfo.Arguments = "";
        process2.StartInfo.UseShellExecute = false;
        process2.StartInfo.RedirectStandardOutput = true;
        process2.StartInfo.RedirectStandardInput = true;
        process2.StartInfo.CreateNoWindow = true;
        process2.Start();
    }

    tasks.Add(process1.StandardOutput.BaseStream.CopyToAsync(process2.StandardInput.BaseStream));
    tasks.Add(process2.StandardOutput.BaseStream.CopyToAsync(outputBuffer));

    await Task.WhenAll(tasks); // Never returns!

    Console.WriteLine("Final output: {0}", UTF8Encoding.UTF8.GetString(outputBuffer.GetBuffer()));

}

通过命令行执行此类操作可以正常工作:

C:\>cat.exe K:\temp\streams.txt | cat.exe
... contents of file ...

C:\>

我尝试将Exited事件处理程序添加到这些Process对象。 Process1退出正常,但Process2永不退出。

我也尝试了其他一些命令(比如sed),但Process2似乎仍然没有做任何事情。

我正在使用溪流&#39; BaseStream属性,因为我最终将使用二进制数据。 (https://stackoverflow.com/a/4535927/339378)这也意味着我无法使用OutputDataReceived,它会返回一个字符串(无论如何都会更复杂)。

谢谢!

1 个答案:

答案 0 :(得分:3)

显然,CopyToAsync在完成时不会关闭输出流。这对我的MemoryStream来说无关紧要,但显然我需要关闭一个进程的StandardInput,否则它将无法完成。

我制作了这个方法:

private static async Task CopyThenClose(Stream from, Stream to)
{
    await from.CopyToAsync(to);
    to.Close();
}

取代了对CopyToAsync的电话;例如:

tasks.Add(CopyThenClose(process1.StandardOutput.BaseStream, process2.StandardInput.BaseStream));