C#

C# 標準出力を自分のプログラムにリダイレクトする

いきなり「標準出力」と言われてもピンとこない方もいるかもしれません。
「標準出力」とはC言語のころからある表現(用語?)でコンソールの画面表示のことと思っていただければよいです。

 

この文字が出ているところが「標準出力」です(同じようなものに「標準エラー出力」というものがあります)
コマンドプロンプトでこの「標準出力」に出てきた文字を別のところ(多くはファイル)に出力することを”リダイレクト”と言います。

コマンドプロントを起動して、コマンドで”dir > aaa.txt”と入力してEnterキーを押すと、普段ならずらずらっと画面で出てくるはずの文字が出なくなり、dirコマンドを実行したフォルダに”aaa.txt”というファイルができています。
その”aaa.txt”のファイルをテキストエディタ(メモ帳など)で開くとそのファイルにdirコマンドの実行結果が出力されます。

 

これだけでも十分「標準出力のリダイレクト」はできてしまうのですが、今回はあえて、C#のプログラムでこの標準出力のリダイレクトを行ってみます。

 

今回は、「Windowsフォームアプリケーション」を作成します。
フォームにTextBoxを2つ(コマンド入力用と結果表示用)とButtonを2つ(実行ボタンと終了ボタン)配置します。

動作としては、実行ボタンを押すと、コマンド入力用の TextBox の内容をコマンドとして実行して、その結果を結果表示用 TextBox に表示する。

というものです。

 

プログラムを作る

画面の用意ができたら、実行ボタンを押したときの処理について説明します。
「標準出力をリダイレクト」するためには「コマンドを実行」する必要があります。

.NET Frameworkでコマンドを実行するには、System.Diagnostics.Processクラスを使います。
Processクラスオブジェクトを生成して、実行するコマンドをセットしてProcess.Start()を呼び出せばコマンドは実行できますが、そのまま実行すると一瞬コマンドプロンプトの画面が表示されたり、実行結果の文字列がとれないということになりますので、コマンドの実行前にやっておくことがあります。

System.Diagnostics.Processクラスオブジェクトを作って実行するコマンドをセットします。

Process p = new Process();
// コマンドプロンプトと同じように実行します
p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
p.StartInfo.Arguments = "/c " + command; // 実行するファイル名(コマンド)

 

コマンドプロンプト(コンソール・ウィンドウ)が表示されないようにします。
次行にある”UseShellExecute = false;”ですが、これも実行しないと、標準出力のリダイレクト時にInvalidOperationException例外が発生してしまいます。

p.StartInfo.CreateNoWindow = true; // コンソール・ウィンドウは開かない
p.StartInfo.UseShellExecute = false; // シェル機能を使用しない

 

’StartInfo.RedirectStandardOutput = true;’とすると、「標準出力」をリダイレクトできます。

p.StartInfo.RedirectStandardOutput = true;

 

ここまでやったら、コマンドを実行します

p.Start();

 

・・・と、これだけではコマンドの実行結果はC#のプログラムのほうで扱うことができません。

実行結果を回収します。

txtResult.Text = p.StandardOutput.ReadToEnd();

 

ここでは回収した実行結果をそのまま結果表示用テキストボックスに表示します。

最後に、’p.WaitForExit();’でコマンドが実行終了するのを待ちます。

p.WaitForExit();

 

主なコードは下記のようになります。

using System.Diagnostics;   // 'Process'クラスの名前空間

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // フォームを表示するときの処理
        this.Text = Application.ProductName;
        txtCommand.Text = string.Empty;
        txtResult.Text = string.Empty;
    }

    private void btnExit_Click(object sender, EventArgs e)
    {
        // [Exit]ボタンを押したときの処理
        this.Close();
    }

    private void btnExecute_Click(object sender, EventArgs e)
    {
        // コマンドを実行します。
        // 結果表示用テキストボックスの内容をクリアします
        txtResult.Text = string.Empty;

        string command = txtCommand.Text;
        if (command == string.Empty)
        {
            // コマンド文字列が空では実行できないのでなにもせずに終了
            return;
        }

        btnExit.Enabled = false;
        btnExecute.Enabled = false;

        Process p = new Process();
        // コマンドプロンプトと同じように実行します
        p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
        p.StartInfo.Arguments = "/c " + command; // 実行するファイル名(コマンド)

        p.StartInfo.CreateNoWindow = false;   // コンソール・ウィンドウは開かない
        p.StartInfo.UseShellExecute = false; // シェル機能を使用しない
        p.StartInfo.RedirectStandardOutput = true;   // <-- これが「標準出力」リダイレクト
        p.Start();  // コマンドを実行します

        // 標準出力に出力しようとした内容を取得して
        // 結果表示用テキストボックスに表示します
        txtResult.Text = p.StandardOutput.ReadToEnd();
        p.WaitForExit();

        btnExit.Enabled = true;
        btnExecute.Enabled = true;
    }
}

 

今回のこのプログラムでは「コマンドプロンプト風」に1つのコマンドを実行できますが、複数のコマンドを順次実行する(フォルダを移動してdirコマンドを実行する)などという動作はできません。
理由は、このコードの場合、Processオブジェクトが1つのコマンドプロンプトと同じようなものですので、「実行ボタンを押すたびに情報が初期化されている」ためです。

 

タイトルとURLをコピーしました