2015-11-19 2 views
2

Я пытаюсь создать конвейер между несколькими объектами Process.Подключить процесс StandardOutput к другому стандарту ProcessInput

Я могу запустить один процесс и захватить его стандартный вывод, но когда я пытаюсь подключить несколько объектов Процесса, второй, похоже, не получает никаких данных из первого стандартного ввода.

В этом коде я использую cat, который либо печатает содержимое своих аргументов в стандартном режиме, либо просто копирует 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())); 

} 

Но когда я добавить второй процесс и попытаться скопировать StandardOutput process1 на StandardInput Process2, я никогда не получить какой-либо выход из 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 обработчики событий. Process1 выходит из строя, но Process2 никогда не выходит.

Я также пробовал некоторые другие команды (например, sed), но Process2 все еще ничего не делает.

Я использую свойства базового потока streams, потому что в конечном итоге я буду работать с двоичными данными. (https://stackoverflow.com/a/4535927/339378) Это также означает, что я не могу использовать OutputDataReceived, который возвращает строку (и это было бы все равно сложнее).

Спасибо!

+0

вы можете настроить свой вход и выход процесса перед запуском начала – gashach

+0

@gashach Вы не можете использовать StandardOutput/Input перед началом процесса или вы получите исключение: «StandardOut не был перенаправлен или процесс еще не начался ». – mrb

+0

нормально, как насчет использования стандартного свойства на process2? – gashach

ответ

2

По-видимому, CopyToAsync не закрывает выходной поток по завершении. Это не имело значения для моего MemoryStream, но, очевидно, мне нужно закрыть стандартную процедуру процесса или она никогда не будет завершена.

Я сделал этот метод:

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)); 
Смежные вопросы