ショートファイル名で実行された場合でも*.exe.configを読み込む
前回の記事で紹介したように、Windows OSの環境で.NET Frameworkアプリケーションを実行するとき、エントリーポイントとなったアセンブリの名称がショートファイル名(8.3形式)で認識されるケースがあります。
この場合、アプリケーション構成ファイル *.exe.config は読み込まれず、ConfigurationManager.AppSettings
やConfigurationManager.ConnectionStrings
が返すコレクションは要素を含まない空のコレクションになってしまいます。
問題への対策にはConfigurationManager.OpenMappedExeConfiguration(...)
メソッドを利用します:
string mayShortName = Assembly.GetEntryAssembly().Location; string longName = Path.GetFullPath(mayShortName); ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap { ExeConfigFilename = longName + ".config" }; Configuration conf = ConfigurationManager.OpenMappedExeConfiguration (fileMap, ConfigurationUserLevel.None);
このコードはPath.GetFullPath(...)
メソッドがショートファイル名のパスをロングファイル名のパスに変換してくれるのを利用して、アプリケーション構成ファイルのパスを導出して読み込みを行います。
アプリケーション構成ファイルのロードには上位のファイルからの継承やローミングの有無など複雑な決まりごとがあるようで、それらすべてを考えた場合にこれが完全な対案となっているかどうかはわかりませんが・・・。
しかしAppSettings
プロパティなどが返すNameValueCollection
と異なり、このメソッドが返すのはConfiguration
です。明らかに使い勝手が悪いです。ここからIDictionary<string, string>
形式で設定情報を引き出すには次のようなコードを書くことになるでしょう:
IDictionary<string,string> appSettings = conf.AppSettings.Settings .Cast<KeyValueConfigurationElement>() .ToDictionary(e => e.Key, e => e.Value); IDictionary<string,string> connStrings = conf.ConnectionStrings.ConnectionStrings .Cast<ConnectionStringSettings>() .ToDictionary(s => s.Name, s => s.ConnectionString);
やたらと手間がかかりますが、とにかくこれでアプリケーションがショートファイル名で実行された場合でも*.exe.configを読み込むことができます。
JP1/AJS2は基本的にショートファイル名で実行ファイルを取り扱う
先日の記事 JP1/AJSから実行すると*.exe.configが読み取れないケースがある で取り上げた事象の原因がわかりました。分かってみればなんのことはありません。「それは仕様です」ということでした。
JP1/AJS2はPCジョブの[詳細定義]ダイアログボックス*1の[実行ファイル名]欄に指定されたパスを基本的にショートファイル名(別名:8.3形式)に変換した上で実行するのです。必ずショートファイル名が利用されるというのではないのですが、ロングファイル名が利用されるケースのほうが少ないのです。
これについてはHITACHIが公開しているリファレンスのあるページに次のような記載がありました*2:
ジョブの実行ファイルの名称は,通常,NTFS(NT File System)およびFAT(File Allocation Table)ボリューム上のファイル用に生成されたショートファイル名に変換されます。ジョブの実行ファイルをショートファイル名に変換しないでロングファイル名で実行したい場合は,次に示す手順でオプションを有効にしてください。
なお,キューレスジョブの場合は,ショートファイル名に変換しないでロングファイル名のまま実行されます。
このページでは「環境設定パラメーターIsExecFindExecutableに『1』を設定」することでこの挙動を抑制する方法が紹介されているのですが、それでもページ末尾には次のようにあります:
次の条件が重なるジョブを実行した場合は,環境設定パラメーターIsExecFindExecutableに「1」を設定していても,実行ファイルをショートファイル名に変換して実行します。キューレスジョブの場合でも,この条件によって実行ファイルをショートファイル名に変換して実行します。
- [実行ファイル名]に指定したファイル名が相対パス指定で,ファイル名の拡張子が「.exe」,「.bat」,「.cmd」,「.com」または「.pif」である
- 1のファイルパスをジョブ定義の[ワークパス]で指定している
ジョブをロングファイル名で実行したい場合は,次のどちらかの操作を行ってください。
おまけに、これは厳密に言って「仕様」なのか不明ですが、[ワークパス]に何も入力していない状態 かつ [実行ファイル名]に絶対パスが指定されている場合でも、コマンドライン引数が[パラメーター]欄に独立して記述され、[実行ファイル名]には文字通り純粋な実行ファイル名が記述された状態である場合(≠実行ファイルの後ろに半角スペース区切りでコマンドライン引数が指定されている場合)、結局実行ファイル名はショートファイル名に変換されてしまうようなのです。
反対に[実行ファイル名]に「実行ファイルの絶対パス+半角スペース+コマンドライン引数」という記述をしていれば、実行ファイル名はロングファイル名として処理されました。
JP1/AJSから実行すると*.exe.configが読み取れないケースがある
JP1/AJSから.NET Frameworkのコンソールアプリを実行したときに起こることがある事象についてです。今のところ、なぜこれが起こるのかわかっていません。したがってこれが.NET Frameworkの問題なのか、当該アプリ固有の問題なのか、それともJP1/AJSの問題なのか、あるいはそれ以外なのかはわかりません。そういうわけで、どうしたら事象を解消できるのかも分かっていません。 その後、原因がわかりました。JP1/AJS2の仕様でした(2017/06/23追記)。
事象
JP1/AJSのPCジョブで.NET Frameworkのコンソールアプリの*.exeを実行するとき、*.exe.configが読み取れない。
読み取りにあたって何か例外がスローされるわけではない。ファイルがそもそも存在していない扱いとなり、ConfigurationManager.AppSettings
プロパティが返すNameValueCollection
の内容が空の状態となる。
環境情報
OS | Windows Server 2008 R2 |
AJS | JP1/AJS3 Agentサービスを利用。ただし別サーバで稼働するManagerサービスはJP1/AJS2。Agent/Managerのバージョン差異については、当該環境構築したおりHITACHIのサポートより問題ない旨の回答を得ている。 |
.NET | .NET Framework v4.5.2 |
表面的な原因
この事象が発生しているとき、Assembly.GetEntryAssembly().Location
プロパティが返すアセンブリのパスが「*.exeの配置されたディレクトリのパス+ "\" + *.exeのベースネーム("*"部分)の先頭6文字の大文字表記 + "~1.exe"」という値になっている。
つまりもし*.exeが "C:\path\to\SampleApp.exe" である場合、上述のプロパティが返す値は "C:\path\to\SAMPLE~1.exe" となる。
このため本来のアプリケーション構成ファイル "SampleApp.exe.config" の内容はそのままに、名称を "SAMPLE~1.exe.config" としたものを用意しておくと、この内容が読み込まれて、ConfigurationManager.AppSettings
プロパティが返すコレクションのエントリーとしてプログラム側から参照できる。
根本的な原因
今のところ不明。 前述の通り、この記事を書いたあと、原因がわかりました。JP1/AJS2の仕様でした。JP1/AJS2は基本的にPCジョブの実行ファイル名をショートファイル名に変換してしまうのです(2017/06/23追記)。
同様の構成(環境情報のセクションを参照のこと)で稼働するアプリでこの事象を発現しているものは今のところ見つかっていないため、PCジョブの設定もしくは*.exe自体に原因がある可能性が高いが、突き止められていない。