コンソールアプリケーションの出力をキャプチャーしてプログレスバーを表示する

既存にあるコンソールアプリケーションの出力を読み取り、フォーム上のプログレスバーに処理の進捗状況を表示する。
コンソールアプリケーションの出力としては最初に総処理ステップ数、次に進捗状況に合わせた出力。そして最後に終了を表す文字列が出力されるものとしている。

例)コンソールアプリケーションの出力
Total Steps:10
1/10
2/10
3/10
:
:
10/10
Done!

コンソールアプリケーションはProcessクラスを使って別プロセスで起動する。
ポイントになるのはコンソールアプリケーションからの出力の取得方法。
以下の例ではOutputDataReceivedイベントに処理用のハンドラを用意して処理を行っている。
少々厄介なのはイベントハンドラからフォーム上のコントロールを直接変更しようとするとInvalidOperationExceptionが発生する点。(イベントハンドラがフォームを所有するとスレッドとは別物になるため?)
このため例ではInvokeメソッドを使ってスレッドセーフな方法でフォーム上のコントロールの操作を行っている。

また、アプリケーションを起動した側のスレッドで”WaitForExit()”をコメントアウトしてある。これはコンソールアプリケーションの処理が長時間に渡る場合、その間フォームが待ち状態でフリーズしたようになるのを避けるため。

private void button1_Click(object sender, EventArgs e){Process process = ne Process();ProcessStartInfo pi = new ProcessStartInfo();pi.UseShellExecute = false;
pi.RedirectStandardOutput = true;
pi.CreateNoWindow = true; //ウィンドウを表示しない(コンソールアプリケーションをバックグランドで実行する)pi.WorkingDirectory = @”C:WorkingFolder”; // 作業フォルダpi.FileName = @”ConsoleApp.exe”; // 実行するコンソールアプリケーションprocess.StartInfo = pi;

process.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceivedEventHandler);// イベントハンドラを指定
process.Start();

process.BeginOutputReadLine();
//process.WaitForExit();
// コンソールアプリケーションが終了するまでWindowsのメッセージを処理する

while (process.HasExited != true)
{
System.Threading.Thread.Sleep(100); // ミリ秒で指定
Application.DoEvents();
}
}
// イベントハンドラprivate void OutputDataReceivedEventHandler(object sender, DataReceivedEventArgs output){
if (!String.IsNullOrEmpty(output.Data))
{ if (String.IsNullOrEmpty(output.Data)) return;
if (output.Data.StartsWith(“Total Steps:”)) { string[] lines = output.Data.Split(new char[] { ‘:’ }); int max = int.Parse(lines[1]); SetProgressBarMax(max); } if (output.Data.Contains(“/”)) { string[] lines = output.Data.Split(new char[] { ‘/’ }); int val = int.Parse(lines[0]); SetProgressBar(val); }}}delegate void SetProgressBarMaxCallback(int value);private void SetProgressBarMax(int value){

if (this.progressBar1.InvokeRequired)

{

SetProgressBarMaxCallback d = new SetProgressBarMaxCallback(SetProgressBarMax);

this.Invoke(d, new object[] { value });}
else
{
this.progressBar1.Maximum = value;
}
}

delegate void SetProgressBarCallback(int value);

private void SetProgressBar(int value)

{
if (this.progressBar1.InvokeRequired)
{
SetProgressBarCallback d = new SetProgressBarCallback(SetProgressBar);
this.Invoke(d, new object[] { value });
}
else
{
this.progressBar1.Value = value;
}
}