redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

MSIX形式で配布すると、プロジェクトに含めたファイルが見つからない場合の対処方法

WPFプロジェクトに画像ファイルや、実行ファイル等を含めて、プログラムの中から参照したり、実行したりするのは珍しいことではないと思います。

Visual Studio上のソリューションエクスプローラーで対象のファイルをクリックして、プロパティから「出力ディレクトリにコピー」で、「新しい場合はコピーする」や「常にコピーする」を選択してビルドすれば、出力フォルダ(binディレクトリ配下の所定の場所)にコピーされます。

ファイル名(ディレクトリを作成している場合は、ディレクトリ名\ファイル名)を指定するだけで、プログラム上から参照したり、実行したりする事ができます。

Visual Studio上でのデバッグ実行や、出力フォルダにあるアプリケーションを実行した場合は問題ありませんでした。

しかし、それをMSIX形式にパッケージングして配布したら、ファイルが見つかりませんという例外が発生してしまったので、対処方法を調べました。

調査開始

まず、MS公式サイトを確認しましたが、よくわからなかったです。

docs.microsoft.com

次に、しばやん(id:shiba-yan)さんが、実行ファイルのパスについて書いていたので、試すことにしました。

blog.shibayan.jp

加えて、以下のサイトも参考にしながら、取得するパスを追加しました。

techinfoofmicrosofttech.osscons.jp

実際には、以下のコードで試しました。

Logger.Debug($"Assembly.GetExecutingAssembly().Location\n  -> {Assembly.GetExecutingAssembly().Location}");
Logger.Debug($"Environment.GetCommandLineArgs()[0]\n  -> {Environment.GetCommandLineArgs()[0]}");
Logger.Debug($"Environment.CurrentDirectory\n  -> {Environment.CurrentDirectory}");
Logger.Debug($"AppContext.BaseDirectory\n  -> {AppContext.BaseDirectory}");
Logger.Debug($"AppDomain.CurrentDomain.BaseDirectory\n  -> {AppDomain.CurrentDomain.BaseDirectory}");
Logger.Debug($"MainModule.FileName\n  -> {Process.GetCurrentProcess().MainModule?.FileName}");

//階層を下ってディレクトリ、ファイルを全て表示
Logger.Debug("AppDomain.CurrentDomain.BaseDirectory All Entries");
foreach (var entry in Directory.EnumerateFileSystemEntries(AppDomain.CurrentDomain.BaseDirectory,
    "*.*",
    SearchOption.AllDirectories))
{
    Logger.Debug(entry);
}
調査結果

Environment.CurrentDirectoryがC:\WINDOWS\System32を返していて、他はC:\Program Files\WindowsApps配下のアプリケーション個別のパスを返していました。

AppDomain.CurrentDomain.BaseDirectoryで得られたパスの階層を下っていくと、プロジェクトに含めたファイルがありました。

対処方法

ファイル名だけを指定した場合は、Environment.CurrentDirectoryのパス配下していると想定できるので、
AppDomain.CurrentDomain.BaseDirectoryで得られたパスを使用して、絶対パスを作成することで、プロジェクトに含まれたファイルを参照・実行する事が出来ました。

以上