この記事では、C#から呼び出されたバッチファイルから複数の実行結果を個別に受け取る方法についてご説明します。
【動画】C#から呼び出されたバッチファイルから複数の実行結果を個別に受け取る実際の動き
本題に入る前に、まずは次の動画をご覧ください。
まずはC#からバッチファイルが呼び出されてバッチファイルが実行されます。
バッチファイルが実行されると、その実行結果をC#側が受け取って実行結果格納用の変数に格納されます。
次に、格納された実行結果を1行ずつ分割して配列に格納します。
配列に格納された配列から、何回目に実行されたコマンド結果なのか判定し、今回用意したテキストボックスに出力しています。
コードの流れ
ProcessStartInfoインスタンスには、どのプロセスで実行するのか、どのバッチファイルを実行するのかなどの情報を設定します。
今回は、実行されたコマンドの実行結果を、実行された何回目かごとに用意したテキストボックス(今回は3つ)にそれぞれ出力しています。
バッチファイルとフォームの例
バッチファイル
今回は下のバッチファイルを用意しました。
@echo off echo (1)pingの実行1 ping 192.168.11.0 echo (2)pingの実行2 ping 192.168.11.1 echo (3)pingの実行3 ping 192.168.11.2
バッチファイルには、pingコマンドを実行するコードが3つ記述されています。
バッチファイルを実行すると以下の結果が表示されます。(指定したIPアドレスいずれも応答していない状態であることを知らせるメッセージが返ってきた)
フォーム
今回は下のフォームを用意しました。
実行ボタンをクリックするとバッチファイルが実行されて、設置されたテキストボックスにバッチファイル(今回はpingコマンド)の実行結果が出力されます。
出力イメージは下のとおりです。
先ほどのバッチファイルで実行されたコマンドごとに、実行結果がそれぞれのテキストボックスに出力されています。
C#のコード(例)
App.configのコード
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> <appSettings> <!-- 呼び出すバッチファイルのフルパス --> <add key="batFilePath" value="C:\work\10_勉強\21_C#\0058\0058.bat" /> </appSettings> </configuration>
注目すべきコード
見て頂きたいのは8行目です。
8行目は、バッチファイルのフルパスを「batFilePath」というキーに設定しているコードです。
「batFilePath」のキーの値をフォーム側のコードが参照して使います。
ちなみに、今回呼び出すバッチファイル「C:\work\10_勉強\21_C#\0054\0054.bat」は下の画像にあるバッチファイルです。
フォームのコード
using System; using System.Windows.Forms; using System.Diagnostics; using System.IO; using System.Configuration; namespace wfcs_0058 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { // 各テキストボックスをクリアする textBox1.Text = ""; textBox2.Text = ""; textBox3.Text = ""; // App.configのappSettingsタグ内に記述した「batFilePath」のキーの値を取得して変数「batFilePath」に格納する string batFilePath = ConfigurationManager.AppSettings["batFilePath"]; // ProcessStartInfoインスタンスを生成する ProcessStartInfo psi = new ProcessStartInfo { // 実行するプロセス名(今回はcmd.exe)を設定する FileName = "cmd.exe", // PowerShellの出力結果を標準出力にリダイレクトするように設定する RedirectStandardOutput = true, // シェル機能を使用せずにプロセスを起動する UseShellExecute = false, // 呼び出すバッチファイルのフルパスを取得する Arguments = "/c " + batFilePath }; // Processインスタンスを生成してバッチファイルを実行する Process proc = Process.Start(psi); // バッチファイルの実行結果を読み取るためのStreamReaderを生成する StreamReader reader = proc.StandardOutput; // 読み取ったバッチファイルの実行結果を1行ずつ格納する変数 string line; // どのコマンドの実行結果なのかを識別するための変数を初期kする int cmdNumber = 0; // コマンドの実行結果の行数分処理を繰り返すループ // (コマンドの実行結果の最終行までデータを確認したらループを抜ける) while ((line = reader.ReadLine()) != null) { if (line.Contains("(1)pingの実行1")) { // 行に「(1)pingの実行1」の文字列が含まれている場合 // 1つ目に実行されたコマンドを示す1の値を変数cmdNumberに格納する cmdNumber = 1; } else if (line.Contains("(2)pingの実行2")) { // 行に「(2)pingの実行2」の文字列が含まれている場合 // 2つ目に実行されたコマンドを示す2の値を変数cmdNumberに格納する cmdNumber = 2; } else if (line.Contains("(3)pingの実行3")) { // 行に「(3)pingの実行3」の文字列が含まれている場合 // 3つ目に実行されたコマンドを示す3の値を変数cmdNumberに格納する cmdNumber = 3; } switch (cmdNumber) { case 1: // cmdNumberが1の場合(1つ目に実行されたコマンドの実行結果) // 1つ目に実行されたコマンドの実行結果をTextBox1に出力する textBox1.Text += line + Environment.NewLine; // switchを抜ける break; case 2: // cmdNumberが2の場合(2つ目に実行されたコマンドの実行結果) // 2つ目に実行されたコマンドの実行結果をTextBox2に出力する textBox2.Text += line + Environment.NewLine; // switchを抜ける break; case 3: // cmdNumberが3の場合(3つ目に実行されたコマンドの実行結果) // 3つ目に実行されたコマンドの実行結果をTextBox3に出力する textBox3.Text += line + Environment.NewLine; // switchを抜ける break; } } } } }
注目すべきコード①
最初に見て頂きたいのは23行目です。
// App.configのappSettingsタグ内に記述した「batFilePath」のキーの値を取得して変数「batFilePath」に格納する string batFilePath = ConfigurationManager.AppSettings["batFilePath"];
コードの説明
以上のコードは、App.configのappSettingsタグ内に記述した「batFilePath」のキーの値を取得して変数に格納するコードです。
「batFilePath」のキーの値は変数「batFilePath」に格納します。
注目すべきコード②
次に見て頂きたいのは27行目から40行目です。
// ProcessStartInfoインスタンスを生成する ProcessStartInfo psi = new ProcessStartInfo { // 実行するプロセス名(今回はcmd.exe)を設定する FileName = "cmd.exe", // PowerShellの出力結果を標準出力にリダイレクトするように設定する RedirectStandardOutput = true, // シェル機能を使用せずにプロセスを起動する UseShellExecute = false, // 呼び出すバッチファイルのフルパスを取得する Arguments = "/c " + batFilePath };
コードの説明
以上のコードは、バッチファイルを元にProcessStartInfoインスタンスを生成して、バッチファイルを実行する際に必要な各情報を設定するコードです。
コードの詳細
27行目のコードでは、バッチファイルを元にProcessStartInfoインスタンスを生成します。
このインスタンスを生成することで、バッチファイルを実行したり、実行するバッチファイルの設定を色々と行うことができます。
30行目のコードでは、実行するプロセス名をFileNameプロパティに設定します。
今回はバッチファイルを実行するので、バッチファイルを実行するためのコマンドプロンプトの実行ファイル「cmd.exe」を設定しています。
33行目のコードでは、RedirectStandardOutputプロパティにtrueを設定しています。
RedirectStandardOutputプロパティにtrueを設定することで、バッチファイルの実行結果を直接読み取ることができるようになります。
36行目のコードでは、UseShellExecuteプロパティにfalseしています。
UseShellExecuteプロパティにfalseを設定することで、シェル経由で実行されず、バッチファイルの起動が直接実行されて高速化されます。(パフォーマンスの向上)
39行目のコードでは、Argumentsプロパティに実行するバッチファイルのフルパスを指定します。(/cは、開いたコマンドプロンプトを最後に閉じるオプション)
注目すべきコード③
次に見て頂きたいのは43行目から46行目です。
// Processインスタンスを生成してバッチファイルを実行する Process proc = Process.Start(psi); // バッチファイルの実行結果を読み取るためのStreamReaderを生成する StreamReader reader = proc.StandardOutput;
コードの説明
以上のコードは、Processインスタンスを生成してバッチファイルを実行し、バッチファイルの実行結果を読み取るためのStreamReaderを生成する処理のコードです。
コードの詳細
43行目のコードでは、Processインスタンスを生成してバッチファイルを実行します。
46行目のコードでは、バッチファイルの実行結果を読み取るためのStreamReaderを生成します。
以上の処理により、バッチファイルの実行と実行結果の取得ができました。
注目すべきコード④
次に見て頂きたいのは56行目です。
// コマンドの実行結果の行数分処理を繰り返すループ // (コマンドの実行結果の最終行までデータを確認したらループを抜ける) while ((line = reader.ReadLine()) != null)
コードの説明
以上のコードは、コマンドの実行結果の行数分処理を繰り返すwhile文です。
コマンドの実行結果の最終行までデータを確認したらwhileのループを抜けます。
注目すべきコード④
次に見て頂きたいのは58行目から78行目です。
if (line.Contains("(1)pingの実行1")) { // 行に「(1)pingの実行1」の文字列が含まれている場合 // 1つ目に実行されたコマンドを示す1の値を変数cmdNumberに格納する cmdNumber = 1; } else if (line.Contains("(2)pingの実行2")) { // 行に「(2)pingの実行2」の文字列が含まれている場合 // 2つ目に実行されたコマンドを示す2の値を変数cmdNumberに格納する cmdNumber = 2; } else if (line.Contains("(3)pingの実行3")) { // 行に「(3)pingの実行3」の文字列が含まれている場合 // 3つ目に実行されたコマンドを示す3の値を変数cmdNumberに格納する cmdNumber = 3; }
コードの説明
以上のコードは、取得したバッチファイルの実行結果の中からどのコマンドの実行結果なのかを判定し、識別するための変数に値を設定する処理のコードです。
今回はバッチファイルで3つのコマンドを実行しています。
バッチファイルで記述されたコマンドを複数実行された場合に、C#側はそれぞれのコマンドが何番目に実行されたか特定することはできません。
そこで、それぞれのコマンドが何番目に実行されたかを特定するために、バッチファイル側で「何番目に実行されたコマンド」であることが分かるよう補足情報を追記しておきます。
その補足情報は下の赤枠の1文です。
以上の赤枠の情報をC#側が拾い、コマンドの実行情報が何番目に実行されたコマンドなのかを特定します。
「コマンドの実行情報が何番目に実行されたコマンドなのかを特定」するのが、58行目、65行目、72行目のIF文です。
1番目に実行されたコマンドの場合は58行目で、どのコマンドの実行結果なのかを識別するための変数cmdNumberに1を格納します。
2番目に実行されたコマンドの場合は65行目でcmdNumberに2を、3番目に実行されたコマンドの場合は72行目でcmdNumberに3を格納します。
なお、58行目、65行目、72行目のIF文の「(1)pingの実行1」「(2)pingの実行2」「(3)pingの実行3」の文字列はバッチファイル側と同じにする必要があるので、違いが無いよう気を付けてください。(IF文の条件に合致しない)
注目すべきコード⑤
次に見て頂きたいのは80行目から111行目です。
switch (cmdNumber) { case 1: // cmdNumberが1の場合(1つ目に実行されたコマンドの実行結果) // 1つ目に実行されたコマンドの実行結果をTextBox1に出力する textBox1.Text += line + Environment.NewLine; // switchを抜ける break; case 2: // cmdNumberが2の場合(2つ目に実行されたコマンドの実行結果) // 2つ目に実行されたコマンドの実行結果をTextBox2に出力する textBox2.Text += line + Environment.NewLine; // switchを抜ける break; case 3: // cmdNumberが3の場合(3つ目に実行されたコマンドの実行結果) // 3つ目に実行されたコマンドの実行結果をTextBox3に出力する textBox3.Text += line + Environment.NewLine; // switchを抜ける break; }
コードの説明
以上のコードは、バッチファイルの実行結果の行に「(1)pingの実行1」「(2)pingの実行2」「(3)pingの実行3」の文字列のいずれも含まれていない場合に、コマンドごとにバッチファイルの実行結果をテキストボックスに出力するコードです。
cmdNumberの値が1の場合は87行目で1つ目に実行されたコマンドの実行結果をTextBox1(1つ目のコマンドの実行結果表示用)に出力します。
cmdNumberの値が2の場合は97行目で2つ目に実行されたコマンドの実行結果をTextBox2(2つ目のコマンドの実行結果表示用)に出力します。
cmdNumberの値が3の場合は107行目で3つ目に実行されたコマンドの実行結果をTextBox3(3つ目のコマンドの実行結果表示用)に出力します。
動作確認
「バッチファイルとフォームの例」をご覧ください。
最後に
この記事では、C#から呼び出されたバッチファイルから複数の実行結果を個別に受け取る方法についてご説明しました。
C#から呼び出されたバッチファイルから複数の実行結果を個別に受け取りたい場合は本記事を参考にして頂けたら幸いです。
プログラミングのスキルを習得するなら
プログラミングのスキルを習得したい、今のスキルをもっと高めたい、そう考えているなら「プログラミングスクール」がおすすめです。
プログラミングのスキルの基礎を身につけるなら「TechAcademy」で1週間の無料体験があるので、これで「プログラミングの基礎」を学ぶのにおすすめですよ。