もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
以前の記事で、コンソールアプリの標準出力を受け取る方法を調べた。
今回は、それを非同期で出来るというのを見たので、やり方をメモしておく。
サンプルコード
実験用に、標準出力と標準エラー出力に、100msおきに出力を行う子プロセスと、 それを呼び出して出力を受け取る親プロセスを作ってみた。
親プロセス(要点)
子プロセスを呼び出して、そこから出力される標準出力、標準エラー出力を受け取る。
using System.Diagnostics; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { var si = new ProcessStartInfo() { // 実行するコマンド(プロセス名) FileName = @"TestProcess.exe", // 渡す引数 Arguments = "a.txt", // ウインドウ無しならtrue CreateNoWindow = true, // 標準出力をこのアプリで受け取るよう指定 RedirectStandardOutput = true, // 標準エラー出力をこのアプリで受け取るよう指定 RedirectStandardError = true, UseShellExecute = false, }; using (var proc = new Process()) { proc.EnableRaisingEvents = true; proc.StartInfo = si; proc.OutputDataReceived += (sender, ev) => { if (ev.Data is not null) Debug.WriteLine($"標準出力 {ev.Data}"); else Debug.WriteLine($"標準出力 ev.Data がnull"); }; proc.ErrorDataReceived += (sender, ev) => { if (ev.Data is not null) Debug.WriteLine($"標準エラー出力 {ev.Data}"); else Debug.WriteLine($"標準エラー出力 ev.Data がnull"); }; proc.Exited += (sender, ev) => { Debug.WriteLine($"終了イベント到来"); }; // プロセス起動 proc.Start(); // 非同期出力読出し開始 proc.BeginErrorReadLine(); proc.BeginOutputReadLine(); // 終了まで(同期的に)待つ proc.WaitForExit(); } Debug.WriteLine("プログラム終了"); } } }
子プロセス(親から呼ばれるプロセス(TestProcess.exe))
100msおきに標準出力に数字を出力し、 その後100msおきに標準エラー出力に数字を出力するプログラム。
namespace TestProcess { internal class Program { static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine(i.ToString()); Thread.Sleep(100); } for (int i = 0; i < 5; i++) { Console.Error.WriteLine(i.ToString()); Thread.Sleep(100); } } } }
実行結果
標準出力 0 標準出力 1 標準出力 2 標準出力 3 標準出力 4 標準エラー出力 0 標準エラー出力 1 標準エラー出力 2 標準エラー出力 3 標準エラー出力 4 標準出力 ev.Data がnull 標準エラー出力 ev.Data がnull 終了イベント到来 プログラム終了
※終了時、最後にnullが送られてくる?実験用子プロセスのプログラムの作りが悪いだけかも?
参考
EnableRaisingEvents フラグ
プロセスが終了したときに、Exited イベントを発生させるかどうかのフラグ。
非同期外部プロセス起動で標準出力を受け取る際の注意点
このぺージのサンプルでは、CancellationTokenのCancel待ちをして、プロセス終了を待っていたが、今回はProcessをproc.WaitForExit();
する形にしてみた。試した限り、どちらでも同じ挙動になっていた。