【.Net】アプリのパスを取得する方法っていくつもあるけど何か違うの?【C#】

.Net アプリ パス…で検索すると、だいたい以下の4つのメソッドとプロパティが見つかります。
どの記事を見ても、どれを使っても良い…みたいなことが書いてあります。

  • Application.StartupPath
  • Assembly.GetExecutingAssembly().Location
  • AppDomain.CurrentDomain.BaseDirectory
  • Environment.CurrentDirectory

でも、ふと疑問がわきます。

「どれを使っても同じ結果を得られるなら、なんで4通りもの方法が用意されているんだろう?」

同じものを4つに分ける意味はありません。
本当に「同じ」なら、1つあれば良いはずです。

実はこれ、作成するアプリや目的によって使い分ける必要があります。

本稿のサンプルコードは以下の環境で動作確認しています。

.Net 7
C# 11
Visual Studio 2022
Windows 11 Home

Application.StartupPath

string path = System.Windows.Forms.Application.StartupPath;

このメソッドは Windows フォームアプリ用です。
WPF アプリとコンソールアプリでは使えません。

.Net Framework では System.Windows.Forms を参照に追加すれば行けたようなのですが、.Net 7 では使えなくなっています。
そもそも、使用目的が違うライブラリを参照するべきではありません。


※クリック/タップすると拡大表示できます。

Form1.Designer.cs

/* 略 */
partial class Form1 {
	/* 略 */
	private void InitializeComponent() {
		/* 略 */

		// アプリのパスをタイトルバーに表示
		string path = System.Windows.Forms.Application.StartupPath;
		this.Text = path;
	}
	/* 略 */
}
Assembly.GetExecutingAssembly().Location

string path = System.IO.Path.GetDirectryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

どのタイプのアプリでも使用できます。
リフレクション用なので、リフレクションやシリアライズの処理中に使うと、コードの流れが分かりやすくなると思います。

Location はアプリの実行ファイルを含めた完全パスを返すので、フォルダまでのパスが欲しい場合は、System.IO.Path.GetDirectoryName() を使う必要があり、少々面倒です。

Console.WriteLine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));

公式に注釈で以下の記載があります。

パフォーマンス上の理由から、このメソッド(GetExecutingAssembly)は、デザイン時に現在実行されているアセンブリがわからない場合にのみ呼び出す必要があります。 

Location プロパティの注釈は以下。

.NET 5 以降のバージョンでは、バンドルされたアセンブリの場合、返される値は空の文字列です。
.NET Frameworkのみ: 読み込まれたファイルがシャドウ コピーされた場合、その場所はシャドウ コピー後のファイルの場所になります。 
パフォーマンス上の理由から、…

「このメソッド重いから、本当に必要な状況でのみ使ってね!」ということです。

バンドルされたアセンブリの場合、返される値は空の文字列

アプリの実行ファイルでは起きないと思いますが、チート対策などで実行するコードを DLL に切り替えていて、その DLL がアプリの実行ファイルに含まれる(バンドルされている)というような場合、バンドルされている DLL 側で Location を参照すると空文字が返って来るようです。

そんな作りのアプリがどれだけあるのか?…が、疑問なのと、DLL 側で Location プロパティを使うのか?…が、疑問ですが、雑にまとめると、Location は空文字を返すことがある…ということになります。

System.Reflection を参照しなければ、その分、アプリのサイズを抑えられるので、使わないで済むなら使わない方が良いです(バンドルする場合の話)。

AppDomain.CurrentDomain.BaseDirectory

string path = System.AppDomain.CurrentDomain.BaseDirectry;

アプリを実行している環境を参照する方法として、最も適しているのはこちらになります。

公式に以下の記載があります。

アプリケーション ドメインとは、アプリケーションが実行される分離された環境です。
  • どのタイプのアプリでも使える。
  • ディレクトリのパスを返してくれる。
  • Assembly.GetExecutingAssembly() メソッドのようにパフォーマンスについての言及がない(BaseDirectry のパフォーマンスは GetExecutingAssembly() と比べて問題視するほどではない)。
  • System.Runtime.dll に含まれるので(.Net を使うなら必須のアセンブリなので)、アプリのサイズを肥大化させることがない。
Environment.CurrentDirectory

string path = System.Environment.CurrentDirectry;

カレントディレクトリを知りたいときに使います。
カレントディレクトリとアプリのパスは違うので、これも使用目的が違います。

カレントディレクトリとアプリのパスが違う状況は、ショートカット/コマンドプロンプト/ターミナル…などを使ってアプリを起動したときに起きます。


※クリック/タップすると拡大表示できます。

↑の画像の ConsoleApp1.exe は Environment.CurrentDirectory の値を Console.WriteLine で出力するだけのコンソールアプリです。
コマンドプロンプトやターミナルの左側にあるパス(↑の画像の C:\> や C:\ConsoleApp1\bin\Debug\net7.0>)がカレントディレクトリです。

アプリのパスは C:\ConsoleApp1\bin\Debug\net7.0 ですが、カレントディレクトリが C:\> のときにアプリを起動すると、Current Directory: C:\ と出力されているのが分かります。
カレントディレクトリをアプリのパスと一致させるには、cd C:\ConsoleApp1\bin\Debug\net7.0 でカレントディレクトリを変更してからアプリを起動する必要がある…ということが↑の画像から分かります。

状況によって得られるパスが変わってしまうので、アプリのパスを調べる用途には向いていません。

.Net でカレントディレクトリを取得する方法は他にもあります。
そちらも全く同じ機能ではなく、使い分けが必要です。
本稿のテーマではないので割愛します。

他に参考にしたページ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です