前回 SDIエディターウィンドウの「ドンガラ」を作ったので、今度はMSBuild(注 )を実行できるようにします。
注 :Visual Studioなどでは様々なバージョンのMSBuildがxamlコードで使えるのかもしれませんが、今回は話を簡単にするため、
現在使用中のC#コンパイラー(C#5のcsc.exe)と同じ場所にあるMSBuildを使うことにします。(この話は最終回にしましょう。)
前回のコードで、
MessageBox.Show("ゴメンナサイ。まだできていません。", "工事中", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);」
にしていた、
//「ファイルを開く」処理
//「保存」処理
//「設定」処理
//「スイッチ」処理
//「実行」処理
//「使い方」処理
というメニューを選択したり、ツールバーボタンを押したりという「ウィンドウズの割込み機能で」で呼び出される関数の内容、及びスイッチ文を入力するダイアログクラス(SwitchDlg)もコメント と解説: で説明 します。
先ずはメニューやツールバーボタンを押した際に呼び出される割込み処理関数の実装から。
//「ファイルを開く」処理
private void OnOpen_Click(object sender, EventArgs e)
{
//先ず*.msbファイルか、ビルド用ファイルかの確認を行う
DialogResult dr = MessageBox.Show("MSBuilder用ファイル(*.msb)を開きますか?(「はい」)\r\nそれともビルド用ファイル(*.csproj | sln)ですか?(「いいえ」)", "ファイル種類確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
//msbファイルの場合
if(dr == DialogResult.Yes)
{
OpenFileDialog ofDlg = new OpenFileDialog();
//ファイルフィルターの指定
ofDlg.FileName = "*.msb"; //拡張子
ofDlg.Filter = "msbファイル(*.msb)|*.msb"; //解説:表示用のフィルターの設定です。
ofDlg.RestoreDirectory = true; //初期ディレクトリへ復帰
ofDlg.CheckPathExists = true; //ファイルパスの存在チェック
ofDlg.InitialDirectory = "."; // デフォルトのフォルダーの指定
ofDlg.Title = "ファイルを開く"; //ダイアログのタイトルを指定する
if(ofDlg.ShowDialog() == DialogResult.Cancel) //ダイアログを表示する
{
MessageBox.Show("キャンセルされました。", "キャンセル", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
//MSBDataFileを記録し、TargetFileとSwitchを読み込み、設定する
MSBDataFile = ofDlg.FileName;
string InFile = File.ReadAllText(MSBDataFile, Encoding.GetEncoding("shift-jis"));
//解説:Win32のGetPrivateProfileStringで扱うiniファイルの様にしてみました。
//解説:ファイルに次の様に書かれていることを想定しています。
//解説:"TargetFile=(文字列データ-ファイルパス)"
//解説:"MSBSwitch=(文字列データスイッチ文)"
//解説:同様にiniファイルの様にした「保存」も参照して下さい。
string[] tokens = Regex.Split(InFile, @"\r\n|="); //改行または'='をセパレーターとして分割する
if(tokens[0] == "TargetFile" && tokens[2] == "MSBSwitch") //出力形式が合う場合
{
//slnまたはprojファイル名を記録
tssl[1].Text = TargetFile = tokens[1];
tssl[1].ToolTipText = TargetFile; //ToolTip設定
//slnまたはprojファイル名を表示
txtBox.Text = File.ReadAllText(TargetFile, Encoding.GetEncoding("shift-jis"));
MSBSwitch = tokens[3];
}
else
{
MessageBox.Show("msbファイル形式ではありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
//オブジェクトを破棄する
ofDlg.Dispose();
}
//ビルド用ファイルの場合
else
{
//プロジェクト(*.csproj)ファイルかソリューションファイル(*.sln)か確認する
dr = MessageBox.Show("csprojファイルを開きますか?(「はい」)\r\nそれともslnファイルですか?(「いいえ」)\r\n或いは中止しますか?(「キャンセル」)", "ファイル種類確認", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
OpenFileDialog ofDlg = new OpenFileDialog();
switch(dr)
{
case DialogResult.Yes:
ofDlg.FileName = "*.csproj";//拡張子
ofDlg.FilterIndex = 1; //ファイルフィルターインデックス
break;
case DialogResult.No:
ofDlg.FileName = "*.sln"; //拡張子
ofDlg.FilterIndex = 2; //ファイルフィルターインデックス
break;
default:
return;
}
//ファイルフィルターの指定
ofDlg.Filter = "csprojファイル(*.csproj)|*.csproj|slnファイル(*.sln)|*.sln";
ofDlg.RestoreDirectory = true; //初期ディレクトリへ復帰
ofDlg.CheckPathExists = true; //ファイルパスの存在チェック
ofDlg.InitialDirectory = "."; // デフォルトのフォルダーの指定
ofDlg.Title = "ファイルを開く"; //ダイアログのタイトルを指定する
if(ofDlg.ShowDialog() == DialogResult.Cancel) //ダイアログを表示する
{
MessageBox.Show("キャンセルされました。", "キャンセル", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
//slnまたはprojファイル名を記録
tssl[1].Text = TargetFile = ofDlg.FileName;
tssl[1].ToolTipText = TargetFile; //ToolTip設定
//slnまたはprojファイルを表示し、新しいファイルなので初期化する
txtBox.Text = File.ReadAllText(ofDlg.FileName, Encoding.GetEncoding("shift-jis"));
MSBSwitch = ""; //MSBSwitchを初期化する
MSBDataFile = ""; //MSBDataFileを初期化する
MessageBox.Show("現在のビルド情報は「保存」しなければ失われてしまいます。\r\n必ず「保存」してください。", "注意", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
// オブジェクトを破棄する
ofDlg.Dispose();
}
}
//「保存」処理
private void OnSave_Click(object sender, EventArgs e)
{
//MSBDataFileにTargetFileとSwitchを保存する
if(MSBDataFile != "") //解説:既に*.msbファイルが設定されている場合
{
using(FileStream fs = new FileStream(MSBDataFile, FileMode.OpenOrCreate))
{
using(StreamWriter file = new StreamWriter(fs, Encoding.GetEncoding("shift-jis")))
{
// ファイルを書き込む
//解説:"TargetFile=(文字列データ-ファイルパス)"
//解説:"MSBSwitch=(文字列データスイッチ文)"
file.Write("TargetFile=");
file.WriteLine(TargetFile); //ビルド用ファイル
file.Write("MSBSwitch=");
file.WriteLine(MSBSwitch); //ビルド用スイッチ
}
//ファイルクローズ
fs.Close();
fs.Dispose();
}
}
//MSBDataFileが""であれば、名前を付けてTargetFileとSwitchを保存する
else
{
SaveFileDialog sfDlg = new SaveFileDialog();
//ファイルフィルターの指定
sfDlg.AddExtension = true; //拡張子自動付加
sfDlg.DefaultExt = "msb";
sfDlg.Filter = "msbファイル(*.msb)|*.msb";
sfDlg.FilterIndex = 1;
sfDlg.RestoreDirectory = true; //初期ディレクトリへ復帰
sfDlg.CheckPathExists = true; //ファイルパスの存在チェック
sfDlg.InitialDirectory = "."; // デフォルトのフォルダーの指定
sfDlg.Title = "MSBデータファイルを保存"; //ダイアログのタイトルを指定する
sfDlg.OverwritePrompt = true; //上書き確認
if(sfDlg.ShowDialog() == DialogResult.Cancel) //ダイアログを表示する
{
MessageBox.Show("キャンセルされました。", "キャンセル", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
MSBDataFile = sfDlg.FileName;
using(FileStream fs = new FileStream(MSBDataFile, FileMode.OpenOrCreate))
{
using(StreamWriter file = new StreamWriter(fs, Encoding.GetEncoding("shift-jis")))
{
//ファイルを書き込む
//解説:"TargetFile=(文字列データ-ファイルパス)"
//解説:"MSBSwitch=(文字列データスイッチ文)"
file.Write("TargetFile=");
file.WriteLine(TargetFile); //ビルド用ファイル
file.Write("MSBSwitch=");
file.WriteLine(MSBSwitch); //ビルド用スイッチ
}
//ファイルクローズ
fs.Close();
fs.Dispose();
}
}
}
}
//「設定」処理
private void OnSetUp_Click(object sender, EventArgs e)
{
//64bit版MSBuildまたは32bit版に設定する
string msg = "現在のMSBuildへのパスは、\r\n\"" + MSBPath +"\"\r\nとなっています。\r\n64bitにしますか?(「はい」)\r\nそれとも32bitですか?(「いいえ」))";
DialogResult dr = MessageBox.Show(msg, "Framework種類確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if(dr == DialogResult.Yes)
MSBPath = @"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\"; //解説:64bit版のファイルパスです。
else
MSBPath = @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\"; //解説:32bit版のファイルパスです。
}
//「スイッチ」処理
private void OnSwitch_Click(object sender, EventArgs e)
{
SwitchDlg SwtDlg = new SwitchDlg(MSBSwitch); //スイッチダイアログを呼び出す
SwtDlg.ShowDialog();
MSBSwitch = SwtDlg.Switch; //スイッチ文を引き取る
SwtDlg.Dispose();
}
//「実行」処理
private void OnExec_Click(object sender, EventArgs e)
{
string msg = TargetFile + "\r\nをビルドします。\r\n", stdoutput, stderr;
if(MSBSwitch == "")
msg += "指定されたスイッチはありません。\r\n宜しいですか?";
else
msg += "指定されたスイッチは\r\n" + MSBSwitch + "\r\nです。\r\n宜しいですか?";
DialogResult dr = MessageBox.Show(msg, "MSBuildのスイッチ確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question); //解説:ビルド用ファイルとスイッチ文を表示して実行可否を確認します。
if(dr == DialogResult.Yes)
{
//コマンドライン引数を指定してMSBuildを起動する
ProcessStartInfo startInfo = new ProcessStartInfo()
{ FileName = MSBPath + "msbuild.exe", //解説:あらかじめ設定したファイルパスにファイル名を付加します。
Arguments = "\"" + TargetFile + "\" " + MSBSwitch, //解説:スイッチ文を引数として追加します。
UseShellExecute = false, //解説:コンソールは表示しません。
RedirectStandardOutput = true, //解説:MSBuildからの出力を取得します。
RedirectStandardError = true //解説:MSBuildがエラーとなった時のエラー出力を取得します。
};
using(Process process = Process.Start(startInfo) ) //解説:try~catch~finallyと同じです。
{
stdoutput = process.StandardOutput.ReadToEnd(); //結果出力
stderr = process.StandardError.ReadToEnd(); //エラー出力(あれば)
process.WaitForExit(); //終了待機
}
//MSBuildからの出力結果とエラーを表示
txtBox.Text = "【出力結果】\r\n" + stdoutput + "\r\n【エラー】\r\n" + stderr;
}
else
txtBox.Text = "ビルドは中止されました。";
}
//「使い方」処理
private void OnHowtoUse_Click(object sender, EventArgs e)
{
Help.ShowHelp (txtBox, @".\MSBuilderHelp.chm"); //ヘルプ表示(*.chmファイル)-解説:Microsoft HTML ヘルプ (hh.exe) でchmファイルを表示します。
}
そして、SwitchDlgは以下の通り。
/////////////////////
//Switch ダイアログ
/////////////////////
class SwitchDlg : Form
{
//メンバー変数
public string Switch; //データ受け渡し用メンバー変数( 解説:なので public ))
//メンバーコントロール
Button btnOK, btnHelp, btnAdd;
ComboBox cmbBox;
TextBox txtBox;
public SwitchDlg(string swt = "")
{
//ダイアログの属性設定( 解説:サイズを変えられないダイアログとしています。)
this.Text = "MSBuildスイッチ設定";
this.ClientSize = new Size(320, 240);
this.MaximizeBox = false; // 最大化ボタン
this.MinimizeBox = false; // 最小化ボタン
this.ShowInTaskbar = false; //タスクバー上表示
this.FormBorderStyle = FormBorderStyle.FixedDialog; // 境界のスタイル
this.StartPosition = FormStartPosition.CenterParent; // 親フォームの中央に配置
//コントロールの属性設定
//スイッチ文選択用コンボボックス
cmbBox = new ComboBox();
cmbBox.DropDownStyle = ComboBoxStyle.DropDown;
cmbBox.DropDownWidth = 176;
cmbBox.DropDownHeight = 176;
cmbBox.Size = new Size(180, 20);
cmbBox.Location = new Point(10, 10);
string[] Items = new string [] {"/consoleloggerparameters:", //解説:ドロップダウンリストを作ります。
"/distributedlogger:",
"/distributedFileLogger",
"/detailedsummary",
"/fileLogger",
"/fileloggerparameters",
"/help",
"/ignoreprojectextensions:",
"/logger:",
"/maxcpucount",
"/noautoresponse",
"/noconsolelogger",
"/nodeReuse:",
"/nologo",
"/preprocess:",
"/property:",
"/target:",
"/toolsversion:",
"/validate",
"/verbosity:",
"/version"};
foreach(string item in Items)
{
cmbBox.Items.Add(item);
}
this.Controls.Add(cmbBox);
//スイッチ文表示用テキストボックス
txtBox = new TextBox();
txtBox.Multiline = true;
txtBox.WordWrap = true;
txtBox.ScrollBars = ScrollBars.Vertical;
txtBox.Size = new Size(this.ClientSize.Width - 20, 120);
txtBox.Location = new Point(10, cmbBox.Height + 20);
txtBox.Text = swt;
this.Controls.Add(txtBox);
//終了ボタン
btnOK = new Button();
btnOK.Location = new Point(ClientSize.Width - btnOK.Width - 10, ClientSize.Height - btnOK.Height - 10);
btnOK.Text = "終了";
btnOK.Click += new EventHandler(OnOK_Click);
this.Controls.Add(btnOK);
//Helpボタン
btnHelp = new Button();
btnHelp.Location = new Point(ClientSize.Width - btnHelp.Width - 10, ClientSize.Height - btnHelp.Height - btnOK.Height - 20);
btnHelp.Text = "Help";
btnHelp.Click += new EventHandler(OnHelp_Click);
this.Controls.Add(btnHelp);
//追加ボタン
btnAdd = new Button();
btnAdd.Location = new Point(ClientSize.Width - btnAdd.Width - 10, 10);
btnAdd.Text = "追加";
btnAdd.Click += new EventHandler(OnAdd_Click);
this.Controls.Add(btnAdd);
}
private void OnOK_Click(object sender, EventArgs e)
{
Switch = txtBox.Text; //データ引き渡し用メンバー変数に結果を代入
Close();
}
private void OnHelp_Click(object sender, EventArgs e)
{
//ヘルプ表示(URLにすると既定のブラウザーで開く- 解説:↑のHelp.ShowHelpとは異なる動作ですね。)
Help.ShowHelp (btnHelp, "https://learn.microsoft.com/ja-jp/visualstudio/msbuild/msbuild-command-line-reference?view=vs-2022#switches");
}
private void OnAdd_Click(object sender, EventArgs e)
{
txtBox.Text += cmbBox.Text + " "; //スイッチ文を追加してゆく(削除、編集はテキストボックスで行う)
}
}
如何だったでしょうか?
このプログラムで注意してほしい点は、
(1)「実行」処理 で使う(外部の実行ファイル(exeファイル等)を実行するProcessStartInfo と Process.Start メソッドの使い方
(2)ブラウザーやHTMLヘルプを使う Help.ShowHelp の使い方
(3)puyblic メンバー変数を使った自作ダイアログと呼び出しプログラムとのデータの受け渡し
でしょうか?
ご参考となれば幸いです。なお、次回にこのMSBuilderで遭遇したトラブルを書いて終了とさせていただきます。