Windows
C#

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


すること

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

ただし、完全なる自由は手に入らない。

深く制御したい場合は、ソースを直接いじってフレームワーク化などをする必要がある。

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


環境

Windows10、Visual Studio 2017


すること

例えばFormsアプリケーションにおいて、XXX.exeの標準出力を監視しつつ標準入力してみる


コード


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) { }
}

}
}