非同期で標準出力(コンソールアプリケーション)の内容をプログラムで受け取る - C#

こちらの記事では、コンソールアプリケーションからの標準出力をプログラムで取得する方法を紹介しました。今回は、コンソールアプリケーションの実行時間が長く、コンソールアプリケーションの標準出力を非同期で受け取る例を紹介します。

プログラム例

コード:呼び出し側WinFormアプリケーション

FormMain.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace ASyncCmdExec
{
  public partial class Form1 : Form
  {
    public delegate void MyEventHandler(object sender, DataReceivedEventArgs e);  
    public event MyEventHandler myEvent = null;
    Process process = null;

    //private ReceivedEventHandler
    
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      myEvent = new MyEventHandler(event_DataReceived); 

      process = new Process();
      string apppath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
      apppath = System.IO.Path.GetDirectoryName(apppath);
      apppath = System.IO.Path.GetDirectoryName(apppath);
      apppath = System.IO.Path.GetDirectoryName(apppath);

      process.StartInfo.FileName = apppath + @"\ExecConsole\bin\Debug\ExecConsole.exe";
      
      process.StartInfo.UseShellExecute = false;
      process.StartInfo.RedirectStandardOutput = true; // 標準出力をリダイレクト
      process.OutputDataReceived += new DataReceivedEventHandler(process_DataReceived);

      process.Start();
      process.BeginOutputReadLine();

    }

    void event_DataReceived(object sender, DataReceivedEventArgs e)
    {
      textBox1.Text += e.Data + "\r\n";
    }

    void process_DataReceived(object sender, DataReceivedEventArgs e)
    {
      //Console.WriteLine(e.Data);  
      this.Invoke(myEvent, new object[2]{sender, e});
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
      try {
        if (process != null) {
          process.Kill();
          process.Close();
          process.Dispose();
        }
      }
      catch (InvalidOperationException exc) {
      }
    }
  }
}

コード:呼び出される側のコンソールアプリケーション

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExecConsole
{
  class Program
  {
    static void Main(string[] args)
    {
      int i = 0;
      while (true) {
        Console.WriteLine("number: {0:d}", i);
        System.Threading.Thread.Sleep(1000);
        i++;
      }
    }
  }
}

解説

呼び出される側のコンソールアプリケーションは実行されると、カウンタを1ずつ増加させてカウンタの内容を標準出力(Console.WriteLine)で出力します。ループ内でSystem.Threading.Thread.Sleep(1000)を呼び出しているため、カウントアップは1秒に1回する動作になっています。

呼び出し側のWinformアプリケーションでは、下記コード
process = new Process();
で呼び出すアプリケーションのProcessを作成します。

FileNameプロパティに呼び出すアプリケーションのファイル名を設定します。
process.StartInfo.FileName = apppath + @"\ExecConsole\bin\Debug\ExecConsole.exe";

ShellExecuteを使わない設定にします。これはBeginOutputReadLine()を利用するための条件となります。
process.StartInfo.UseShellExecute = false;

標準出力をリダイレクトするためRedirectStandardOutputプロパティをtrueに設定します。これもBeginOutputReadLine()を利用するための条件となります。
process.StartInfo.RedirectStandardOutput = true;

非同期読み込みでの完了イベントとなるイベントハンドラを設定します。BeginOutputReadLine()を利用するための条件となります。
process.OutputDataReceived += new DataReceivedEventHandler(process_DataReceived);

プロセスを開始し、BeginOutputReadLine()メソッドで非同期読み込みを開始します。
process.Start();
process.BeginOutputReadLine();

非同期読み込みのメソッドは下記の記述です。非同期読み込みメソッドはメインスレッドではないスレッドが呼び出しますので、メインフォームのコントロールにアクセスする場合はInvokeメソッドを利用して、スレッドの同期をとる必要があります。
void process_DataReceived(object sender, DataReceivedEventArgs e)
{
  //Console.WriteLine(e.Data);  
  this.Invoke(myEvent, new object[2]{sender, e});
}

Invokeメソッドの引数のmyEventはアプリケーションの開始時に下記コードによりイベントハンドラとして作成します。
myEvent = new MyEventHandler(event_DataReceived);

実行結果


テキストボックス内にコンソールアプリケーションの出力内容が表示されます。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2018-02-26
作成日: 2010-11-15
iPentec all rights reserverd.