[C#] コンソールアプリの標準入出力を奪ってゴニョゴニョする

## やりたいこと
既存の**コンソールアプリケーション**について、その標準入出力を非同期で制御してゴニョゴニョしてみる！

ただし、完全なる自由は手に入らない。
深く制御したい場合は、ソースを直接いじってライブラリ化しよう。

要は、**「コンソールアプリの機能を自前のアプリケーションに使いたい、でもソースコードいじる程でもない」**って時にやること！

## 環境
Windows10、Visual Studio 2017



## すること
例えばFormsアプリケーションにおいて、XXX.exeの標準出力を監視しつつ標準入力してみる
ちなみに、実際に作ったツールは https://github.com/sensuikan1973/Edax_AutoLearning_Tool です

## コード

``` C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Sample_Asynchronous
{
    public partial class Form1 : Form
    {
        // 同ディレクトリにあるXXX.exeのパスを取得
        string XXX_Path = Path.GetDirectoryName(Application.ExecutablePath) + "XXX.exe";

        // イベントハンドラ
        public delegate void MyEventHandler(object sender, DataReceivedEventArgs e);
        public event MyEventHandler myEvent = null;

        // 外部プロセス(XXX.exe)を宣言
        Process xxx_process = null;

         /**
         * @brief コンストラクタ
         *        XXX.exeを起動する
         */
        public Form1()
        {
            //イベントハンドラを作成
            myEvent = new MyEventHandler(event_DataReceived);

            xxx_process = new Process();
            // パス指定
            xxx_process.StartInfo.FileName = XXX_Path;

            // 非同期処理のために、ShellExecuteを使わない設定にする
            // BeginOutputReadLine()を利用するための条件
            xxx_process.StartInfo.UseShellExecute        = false;

            // 非同期読込での完了イベントとなるイベントハンドラを設定
            // BeginOutputReadLine()を利用するための条件
            xxx_process.OutputDataReceived += new DataReceivedEventHandler(process_DataReceived);
            
            // 標準入出力をリダイレクト
            xxx_process.StartInfo.RedirectStandardOutput = true;
            xxx_process.StartInfo.RedirectStandardInput  = true;

            // XXX.exeのコンソールは邪魔なので開かない
            xxx_process.StartInfo.CreateNoWindow         = true; 
            
            // ついにプロセス起動！
            xxx_process.Start();

            // 標準出力の非同期読込を開始
            xxx_process.BeginOutputReadLine();
        }

        /**
         * @brief 非同期で出力を読み込む
         *        メインスレッドにアクセスする場合は、Invokeメソッドを利用し、スレッドの同期をとる必要がある
         */
        void process_DataReceived(object sender, DataReceivedEventArgs e)
        {
            this.Invoke(myEvent, new object[2] { sender, e });
        }

        /**
         * @brief XXX.exeの標準出力を見て、その内容に応じて処理をしてみる     
         */
        void event_DataReceived(object sender, DataReceivedEventArgs e)
        {
          // --------------------
          // e.Dataの中身を見て、色々処理できる！
          // --------------------

         // 例えば、"星"という文字が出力に含まれていたら、"Starだね"という文字を標準入力してみる
          if (e.Data.Contains("星"))
          {
              xxx_process.StandardInput.WriteLine("Starだね");
          }
        }

        /**
         * @brief Formロード時
         */
        private void Form1_Load(object sender, EventArgs e)
        {
            // Formが閉じられた時のイベントを登録
            this.FormClosed  += new FormClosedEventHandler(Form1_Closed);
        }

        /**
         * @brief Formが閉じられた時の処理
         *        外部プロセス(xxx.exe)をkillする
         */
        private void Form1_Closed(object sender, FormClosedEventArgs e)
        {
            Properties.Settings.Default.Save();
            try
            {
                if (xxx_process != null)
                {
                    xxx_process.Kill();
                    xxx_process.Close();
                    xxx_process.Dispose();
                }
            }
            catch (InvalidOperationException exc) { }
        }

    }
}
```