トップ回答者
Process 終了と 出力リダイレクトのタイミングが合いません

質問
-
プログラム中で HTML ファイルを XML に成形するために、Process を使って 変換プログラム (HTML Tidy) を呼び出すコードを書いてみましたが、なぜか 入出力のリダイレクトが うまくいきません。
(コードの一部)
convProc.Start();
convProc.BeginErrorReadLine();
convProc.BeginOutputReadLine();
convProc.StandardInput.Write(convSourceString);
convProc.StandardInput.Close();if(!convProc.WaitForExit(30000))
throw(new SystemException("プロセスがタイム アウトしました。"));print("----END----");
問題は、WaitForExit は通過しているのに 非同期出力が終了せず、プロセスが閉じた後も DataReceivedEventHandler が呼び出されているようなんです。
実際に Output/ErrorDataReceived イベントでテキスト出力すると、こういう感じです。
Output.
Output.
Output.
...(中略)...
Output.
Output.
Output.
----END----
Error.
Error.
Output.
Output.
Error.
…どうやら StandardOutput と StandardError の切り替え時に Exit してしまっているようなんですけれど…;
Process.Close()、Refresh()、StreamReader.Close() などを入れて 色々試してみましたが、どうしても 非同期出力の終了とプロセスの終了を 合わせられません。
何かやり方があるんでしょうか…?
回答
-
StandardOutputとStandardErrorが読み終わるのをManualResetEventをつかって待機してみる
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { if(args.Length==0) {//引数が無い時は標準入力から読み込んで、送り返す string read=System.Console.In.ReadToEnd(); System.Console.WriteLine(read); System.Console.WriteLine(read); System.Console.Write(read); } else {//引数があるときは引数なしで自分と同じ実行ファイルを実行する try { System.Threading.ManualResetEvent[] waits = { errorClosed , outputClosed }; for (int loop = 0; loop < 100; loop++) { Console.WriteLine(string.Format("************** {0} **************" , loop)); errorClosed.Reset(); outputClosed.Reset(); System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(); info.UseShellExecute = false; info.RedirectStandardError = true; info.RedirectStandardInput = true; info.RedirectStandardOutput = true; info.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location; using (System.Diagnostics.Process process = new System.Diagnostics.Process()) { process.StartInfo = info; process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived); process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.StandardInput.Write(loop); process.StandardInput.Close(); if (!process.WaitForExit(10000)) { throw new TimeoutException(); } process.Close(); //OutputとErrorが閉じられるまで待機 if (!System.Threading.ManualResetEvent.WaitAll(waits , 10000)) { throw new TimeoutException(); } Console.Out.WriteLine("END"); } } } catch(TimeoutException) { Console.Error.Write("TimeOut"); } } } static System.Threading.ManualResetEvent errorClosed= new System.Threading.ManualResetEvent(false); static System.Threading.ManualResetEvent outputClosed = new System.Threading.ManualResetEvent(false); static void process_ErrorDataReceived(object sender , System.Diagnostics.DataReceivedEventArgs e) { if (String.IsNullOrEmpty(e.Data)) { Console.Out.WriteLine("CLOSE ErrorStream"); errorClosed.Set(); } else { Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write(e.Data); Console.ResetColor(); } } static void process_OutputDataReceived(object sender , System.Diagnostics.DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) { Console.WriteLine("CLOSE OutputStream"); outputClosed.Set(); } else { Console.Out.WriteLine("OUT:" + e.Data); } } } }
- 回答としてマーク にふてぃ 2010年3月26日 15:02
すべての返信
-
StandardOutputとStandardErrorが読み終わるのをManualResetEventをつかって待機してみる
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { if(args.Length==0) {//引数が無い時は標準入力から読み込んで、送り返す string read=System.Console.In.ReadToEnd(); System.Console.WriteLine(read); System.Console.WriteLine(read); System.Console.Write(read); } else {//引数があるときは引数なしで自分と同じ実行ファイルを実行する try { System.Threading.ManualResetEvent[] waits = { errorClosed , outputClosed }; for (int loop = 0; loop < 100; loop++) { Console.WriteLine(string.Format("************** {0} **************" , loop)); errorClosed.Reset(); outputClosed.Reset(); System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo(); info.UseShellExecute = false; info.RedirectStandardError = true; info.RedirectStandardInput = true; info.RedirectStandardOutput = true; info.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location; using (System.Diagnostics.Process process = new System.Diagnostics.Process()) { process.StartInfo = info; process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_OutputDataReceived); process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(process_ErrorDataReceived); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.StandardInput.Write(loop); process.StandardInput.Close(); if (!process.WaitForExit(10000)) { throw new TimeoutException(); } process.Close(); //OutputとErrorが閉じられるまで待機 if (!System.Threading.ManualResetEvent.WaitAll(waits , 10000)) { throw new TimeoutException(); } Console.Out.WriteLine("END"); } } } catch(TimeoutException) { Console.Error.Write("TimeOut"); } } } static System.Threading.ManualResetEvent errorClosed= new System.Threading.ManualResetEvent(false); static System.Threading.ManualResetEvent outputClosed = new System.Threading.ManualResetEvent(false); static void process_ErrorDataReceived(object sender , System.Diagnostics.DataReceivedEventArgs e) { if (String.IsNullOrEmpty(e.Data)) { Console.Out.WriteLine("CLOSE ErrorStream"); errorClosed.Set(); } else { Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write(e.Data); Console.ResetColor(); } } static void process_OutputDataReceived(object sender , System.Diagnostics.DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) { Console.WriteLine("CLOSE OutputStream"); outputClosed.Set(); } else { Console.Out.WriteLine("OUT:" + e.Data); } } } }
- 回答としてマーク にふてぃ 2010年3月26日 15:02