-
Notifications
You must be signed in to change notification settings - Fork 0
MS_GettingApplicationPaths
- 戻る(各種パスを取得する方法)
調査したところ、
が推奨方法の既定値であるもよう。
補足(この結論は現在も妥当): 原文の結論は正しく、
AppDomain.CurrentDomain.BaseDirectoryが最も安全な既定である。【なぜこれが安全か】 ・カレント ディレクトリの影響を受けない ・単一ファイル発行でも「展開先」ではなく期待する場所を返す(後述) ・[アプリケーション ドメイン](MS_ApplicationDomain) が .NET Core で廃止されても、このプロパティは残っているなお、.NET 6 以降では
AppContext.BaseDirectoryが推奨される
(実体は同じ値を返すが、AppDomainに依存しない名前空間にある)。var dir = AppContext.BaseDirectory; // 現在はこちらが素直
System 名前空間のライブラリを使用するのが一般的。
アプリケーションと同じディレクトリにあるファイルを検索する場合に使用する。
string path = System.AppDomain.CurrentDomain.BaseDirectory;環境変数の current directory を取得する場合に使用する。
- アプリケーションの実行中に変化する可能性のある値。
- OpenFileDialog でファイルを選択されたディレクトリに変更される可能性がある。
string path = System.Environment.CurrentDirectory;補足(
OpenFileDialogの件は現在も要注意): 原文が挙げている
この落とし穴は、既定の挙動として現在も存在する。var dlg = new OpenFileDialog(); dlg.RestoreDirectory = true; // ← これを true にすると変わらない dlg.ShowDialog();
RestoreDirectoryの既定はfalseなので、
ファイルを選ぶたびにカレント ディレクトリが移動する。
カレント ディレクトリに依存した相対パスを使っていると、
ある操作の後だけ動かなくなるという再現しにくい不具合になる。結論: カレント ディレクトリを当てにしない。
相対パスはAppContext.BaseDirectoryを基点に組み立てる。var configPath = Path.Combine(AppContext.BaseDirectory, "config", "app.json");
System.Environmentと同じ、
環境変数の current directory を取得する場合に使用する。
string path = System.IO.Directory.GetCurrentDirectory();Assembly クラスの Location プロパティで Assembly のパスを取得できる。
GetEntryAssembly メソッドでは、EXE などのエントリポイントとなるアセンブリを取得する。
string path = System.Reflection.Assembly.GetEntryAssembly().Location;移行メモ(誤記): 原文の
string path = System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetEntryAssembly().Location;
は変数宣言が二重になっており、コンパイルできないため、
前後の例に合わせて修正した。
また、本文の「EntryAssembly メソッド」も
GetEntryAssemblyメソッドの脱字と判断した。
GetExecutingAssembly メソッドでは、
実行中のコードを含む EXE や DLL のアセンブリを取得する。
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;GetCallingAssembly メソッドでは、
現在実行中のメソッドを呼び出したコードを含む EXE や DLL のアセンブリを取得する。
string path = System.Reflection.Assembly.GetCallingAssembly().Location;補足(3 つの使い分け): アセンブリ が複数ある
(EXE + 複数の DLL)場合、返る値が異なる。MyApp.exe が MyLib.dll の Foo() を呼び、 その中でパスを取得した場合 GetEntryAssembly() → MyApp.exe (入口) GetExecutingAssembly() → MyLib.dll (今いる場所) GetCallingAssembly() → MyApp.exe (呼び出し元)
用途 使うもの アプリの設定ファイルを探す AppContext.BaseDirectory(推奨)ライブラリ自身に付属するファイルを探す GetExecutingAssembly().Location「誰に呼ばれたか」でふるまいを変える GetCallingAssembly()(避けるべき)
GetCallingAssemblyは使わない方がよい。
インライン化やasyncの書き換えによって期待通りに動かないことがある
(async/await のステート マシン化の影響)。
補足(
Assembly.Locationは単一ファイル発行で空になる/最新化): これは
.NET 5 以降で最も踏みやすい破壊的変更である。
発行形態 Assembly.Location通常の発行 パスが返る 単一ファイル発行( PublishSingleFile)空文字列 Native AOT 空文字列 単一ファイル発行では、アセンブリはメモリ上に展開されるため 「ファイルとしての場所」が存在しない → Location は "" を返す(例外ではない点が厄介) → Path.GetDirectoryName("") が例外になる、 あるいは意図しないパスになる対処は
AppContext.BaseDirectoryを使うことである
(こちらは単一ファイルでも実行ファイルの場所を返す)。// ✗ 単一ファイル発行で壊れる var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // ○ 常に動く var dir = AppContext.BaseDirectory; // 実行ファイル自身のパスが欲しい場合(.NET 6+) var exe = Environment.ProcessPath;
Environment.ProcessPath(.NET 6 で追加)は、
後述のApplication.ExecutablePathの
クロスプラットフォーム版にあたる。なお、単一ファイル発行時に
Locationを使っているコードは
**ビルド時に警告(IL3000)**が出るため、
警告を有効にしておくと検出できる。<PublishSingleFile>true</PublishSingleFile> <EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
Windows.Forms 用
string path = Application.ExecutablePath;string path = Application.StartupPath;補足(Windows Forms 以外では使えない): この 2 つは
System.Windows.Formsに属するため、
コンソール / Web / ライブラリからは使えない
(参照を追加すれば呼べるが、設計として不適切)。
返す値 Application.ExecutablePath実行ファイルの「フル パス」(ファイル名を含む) Application.StartupPath実行ファイルの「ディレクトリ」 クロスプラットフォームな代替は次の通り。
Environment.ProcessPath // ExecutablePath 相当(.NET 6+) AppContext.BaseDirectory // StartupPath 相当
補足(用途別のまとめ): 実務での選択指針を整理しておく。
欲しいもの 推奨 アプリと同梱したファイル(設定、テンプレート) AppContext.BaseDirectory実行ファイルそのもののパス Environment.ProcessPath(.NET 6+)ユーザーごとの設定の保存先 Environment.GetFolderPath(SpecialFolder.ApplicationData)一時ファイル Path.GetTempPath()ログの出力先 設定で指定させる(.NET Core config) Web のパス Webサイトのパスを取得する方法 重要: アプリの配置先に書き込まない。
Program Files配下は書き込み権限が無く、
コンテナでは読み取り専用のことが多い。
書き込むものはApplicationDataか、
設定で指定された場所に置く。var appData = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyCompany", "MyApp"); Directory.CreateDirectory(appData);なお、ASP.NET Core では
IHostEnvironmentを
注入して使うのが定石である(ASP.NET Core における DI)。public MyService(IHostEnvironment env) { var root = env.ContentRootPath; // アプリのルート }
-
c# - Should I use AppDomain.CurrentDomain.BaseDirectory or System.Environment.CurrentDirectory? - Stack Overflow
https://stackoverflow.com/questions/674857/should-i-use-appdomain-currentdomain-basedirectory-or-system-environment-current -
c# - アプリケーションのフォルダパスを取得する最善の方法 - .net | CODE Q&A [日本語]
https://code.i-harness.com/ja/q/5c2ef4
- カレントディレクトリ(現在の作業ディレクトリ)を取得、設定する
https://dobon.net/vb/dotnet/file/currentdirectory.html - 自分のアプリケーションの実行ファイルのパスを取得する、
VB6のApp.Pathと同じ事を行うには?
https://dobon.net/vb/dotnet/vb6/apppath.html
- 現在実行しているDLLのパスを取得する。(GetExecutingAssembly)
http://d.hatena.ne.jp/tekk/20110307/1299513821 - アプリケーションの実行パスを取得する。どの方法が適切か検討してみた。
http://d.hatena.ne.jp/tekk/20110222/1298371975
- AppContext.BaseDirectory プロパティ
https://learn.microsoft.com/ja-jp/dotnet/api/system.appcontext.basedirectory - Environment.ProcessPath プロパティ
https://learn.microsoft.com/ja-jp/dotnet/api/system.environment.processpath - 単一ファイル アプリでの Assembly.Location の動作変更
https://learn.microsoft.com/ja-jp/dotnet/core/compatibility/core-libraries/5.0/assembly-location-empty-string
Tags: 移行, .NET開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。