実行ファイルEXEを出力するディレクトリについて考える

アプリケーションを作成するときのプロジェクトの構造、なかでも実行ファイル EXE を出力するディレクトリの位置について考察してみたいと思います。( 長文になってしまったので、時間のある時に御覧いただいたほうがいいです )

まずは VisualStudio2019 のデフォルト設定で新規にプロジェクトをたちあげます。ソリューション名は "sss" とし、それに従属するプロジェクト名を "aaa" とした場合、Fig. 1 のようなディレクトリ構成になります。

ソリューションのファイル名は sss.sln で、プロジェクトのファイル名は aaa.csproj です。それぞれ同一の名称のディレクトリの中に配置されます。

生成出力される実行ファイルの名称は特にアセンブリ名称を設定しなければ aaa.exe になります。その実行ファイルの形式は AnyCPU 形式で、64bits(x64) のWindows でも 32bits(x86) のWindowsでも、どちらでも動く実行ファイルになります。

Fig. 1

しかし、CPUのビット幅にネィティブ対応した高速な実行ファイルを作りたい場合や、使用する外部ライブラリやドライバが 64bit に限定されている場合、AnyCPU 形式では対応できません。

生成される実行ファイルの形式を x64(64bit) で新たに追加作成するように、構成マネージャで明示しなければなりません。( x64 形式の追加作成については、この記事の後半部で紹介します )

デフォルトの AnyCPU 形式に加えて x64 形式を新たに作成した場合は、Fig. 2 のようなディレクトリ構成になります。

/bin の中に新たに作成した動作プラットフォーム名称の /x64 というディレクトリが作成され、その中に /Debug と /Release が作成されます。

ここで /Debug と /Release のディレクトリ階層が AnyCPU 形式と x64 形式で違うことに違和感を感じてしまいます。

Fig. 2

この違和感について、Fig. 3 を使って説明を試みます。

複雑なアプリケーションの場合は実行ファイル単体だけで動作することはありません。アプリケーションの設定を格納するパラメータファイルと共に運用される場合がほとんどです。ここでは aaa.exe 実行ファイルと共に、aaa.ini パラメータファイルを運用することにしましょう。

Fig. 3

AnyCPU 形式の実行ファイルから見てパラメータファイルは1階層上に配置されており、x64 形式の実行ファイルから見てそれは2階層上に配置されています。

同一のソースコードでパラメータファイルを参照するには #if を使って AnyCPU のときと、x64 のときで別々に条件付きコンパイルをしなくてはなりません。とても面倒です。

		// THIS_CODE_X64 は VisualStudio で定義済みのシンボルにあらず.
		// このシンボルはビルドのオプションであらかじめユーザが定義しておくこと.

		private void menuDebugExec000_Click( object sender, RoutedEventArgs e )
		{

			const String FILENAME_PRM = "aaa.ini";

			String fp_exe = System.Reflection.Assembly.GetExecutingAssembly().Location;
			String dir_exe = System.IO.Path.GetDirectoryName( fp_exe );

			String str_type = "";

			String dir_prm = "";


#if THIS_CODE_X64

			str_type = "x64";

			// 2階層うえのディレクトリを取得する.
			System.IO.DirectoryInfo di0 = new System.IO.DirectoryInfo( dir_exe );
			System.IO.DirectoryInfo di1 = di0.Parent;
			System.IO.DirectoryInfo di2 = di1.Parent;
			dir_prm = di2.FullName;


#else

			str_type = "AnyCPU";

			// 1階層うえのディレクトリを取得する.
			System.IO.DirectoryInfo di0 = new System.IO.DirectoryInfo( dir_exe );
			System.IO.DirectoryInfo di1 = di0.Parent;
			dir_prm = di1.FullName;

#endif

			// パラメータディレクトリと、パラメータファイルを結合して、パスを取得する.
			String filepath_prm = System.IO.Path.Combine( dir_prm, FILENAME_PRM );

			// ---------------------------------------------
			// ここでパラメータファイルのパスが取得できた.
			// パラメータのファイルを内容を読んだり書いたり.
			// いろいろなにかやる.
			// ---------------------------------------------

			// メッセージボックスでパスを表示する.
			String strmsg = String.Format( "{0} : {1}", str_type, filepath_prm );
			MessageBox.Show( strmsg );

		}

この面倒な問題を解決するために、AnyCPU 形式のときは /AnyCPU というディレクトリの中に /Debug と /Relese が内包されるようにしてやれば、#if による条件つきコンパイルはしなくてもよくなります。この状態を Fig. 4 に示します。

Fig. 4

そうすれば Fig. 5 に示すように、実行ファイルと共に運用するパラメータファイルは、AnyCPU 形式であっても x64 形式であっても、必ず2階層上に配置しておけばよいことになります。

Fig. 5

しかし、まだ私にはこのディレクトリ構成でも違和感があります。

この例で示したソースファイルは MainWindow.xaml.cs だけですが、実際には /aaa というディレクトリの中に多数のソースコードが配置されており、そういった乱雑なファイル群の中にある /bin の中に実行ファイルが生成出力されるという状況に違和感を感じるのです。

そこで、ソースファイルの含まれているディレクトリと、実行ファイルが生成出力されるディレクトリを分離することを検討します。ソースファイルは /aaa の中に配置し、実行ファイルが生成出力されるディレクトリは /out とします。これが Fig. 6 に示す状態です。

Fig. 6

このような状態にしておけば、別のPCに実行ファイルを配置するときなど、ソースコードが多数配置されているディレクトリの中まで覗かなくても /out だけをサッと持ち出せばよいことになります。

もちろん /out は /sss の外に作成してもいいのですが、/sss ディレクトリを持ち運べば /out も同時に持ち運んでいるという安心感があるので、私は Fig. 6 のような状態までにとどめています。


さて、以上の考え方をふまえて、実行ファイルの生成出力先を設定する方法を紹介します。

あらかじめメニューバーの [ビルド]...[構成マネージャー]で、x64 形式の実行ファイルも生成するように設定してください。「アクティブソリューションプラットフォーム」に新規作成があるので、そこで x64 を作ってください。その後、「プラットフォーム」にも新規作成があるのでそこでも同様に x64 を作ってください。

構成マネージャーでうまく設定できれば AnyCPU 形式の Debug ビルドと Release ビルドの実行ファイル、x64 形式の Debug ビルドと Release ビルドの実行ファイルが生成出力されることになります。

つぎに本当に構成マネージャで設定できたか確認します。メニューバーの [ビルド]...[バッチビルド] を実行します。そうすると下記のバッチビルド実行ダイアログが表示されます。AnyCPU と x64 で、Debug と Release の、2×2の4通りがあることを確認してください。

ここで [ビルド] や [リビルド] のボタンを押したくなりますが、まだガマンです、押してはいけません。

次に、ソリューションエクスプローラの Properties をダブルクリックしてビルドオプション画面を表示させてください。

注目すべきは、「構成」と「プラットフォーム」です。われわれが書き込む場所は「出力パス」です。

  • Debug, AnyCPU のとき ..¥out¥AnyCPU¥Debug¥
  • Release, AnyCPU のとき ..¥out¥AnyCPU¥Release¥
  • Debug, x64 のとき ..¥out¥x64¥Debug¥
  • Release, x64 のとき ..¥out¥x64¥Release¥

と書き込んでください。..¥ というのは、aaa.vcproj の一つ上のディレクトリ階層という意味です。

( ドットドット円です、ドットは2個です )

さて、もう一度バッチビルドのダイアログに戻りましょう。ビルドのチェックボックスにすべてチェックを入れてください。ようやく [ビルド] または [リビルド] のボタンを押すことができます。私のおすすめは [リビルド] のボタンです。

さぁ [リビルド] ボタンを押して4通りのビルドをイッキに開始しましょう!

VisualStudio メニュー [表示]...[出力] で、出力ウインドウを表示させて、ビルド結果を確認しましょう。下記のように4通りとも成功していればOKです。

このページで紹介した手順をふめば、私が理想と考えている Fig. 6 のディレクトリ構成になっているはずです。

出力形式は x64 以外に x86 や ARM などもあります。それらも同様の考え方で同じ階層に実行ファイルを出力するようにすれば何かと都合がいいと思います。

/out/AnyCPU/Debug/aaa.exe
/out/AnyCPU/Release/aaa.exe

/out/x64/Debug/aaa.exe
/out/x64/Release/aaa.exe

/out/x86/Debug/aaa.exe
/out/x86/Release/aaa.exe

/out/ARM/Debug/aaa.exe
/out/ARM/Release/aaa.exe

こういう感じです。

以上、長文になりましたが、最後までお読みいただきありがとうございました。