解説

インサイド .NET Framework [改訂版]

第3回 アセンブリのロード

吉松 史彰
2003/07/09

Page1 Page2 Page3 Page4 Page5

アセンブリの配置に関する補足

 最後に、アセンブリの配置に関する補足をしておきたい。ここまでの説明で、もやもやとしていた読者も多いのではないだろうか。というのも、さまざまな文献に「アセンブリはアプリケーションと同じディレクトリに配置しておけばロードできる」と異口同音に書いてあるからだ。

 ところが筆者の説明によれば、アセンブリの配置場所はGACかコードベースの指定しかなく、そこから見つからないと例外になることになっている。「間違ってるんじゃないの?」と思われている読者が多いだろう。結論を先にいえば、アセンブリはアプリケーションと同じディレクトリに配置しておけばいいというものではないので、その説明はあえてここまで先延ばしにしたのだ。

 最初の説明では省略しているのだが、アセンブリの探索の第2段階では、実は次のような検索パスがある。

  1. DEVPATH環境変数に列挙されているディレクトリ
  2. グローバル・アセンブリ・キャッシュ
  3. アセンブリのコードベース
  4. 3.が存在しなかった場合はプローブを行う

 4番目の手順は、探しているアセンブリがGACで見つからず、かつアプリケーションの構成ファイルが存在しない、または構成ファイルはあるものの、その中にいま探しているアセンブリを指すdependentAssembly要素が含まれていない場合に行われる。つまり、構成情報が取得できなかった場合に、CLRがやむを得ず最後の手段として行うのがプローブという手順なのだ。

 仮にuser.exeを実行した結果、utilアセンブリをプローブで探す羽目になった場合、CLRは次のような手順で検索を行う。

  1. アセンブリのアイデンティティのうち、簡易名(この場合はutil)に“.dll”という拡張子を付けて、それをファイル名と見なす。
  2. アプリケーション(この場合はuser.exe)のベース・ディレクトリで、1.で作ったファイル名のファイルを検索する。
  3. 2.で見つからなければ、アプリケーションのベース・ディレクトリの下にあるアセンブリの簡易名(util)と同じ名前のサブディレクトリで、1.で作ったファイル名のファイルを検索する。
  4. 3.で見つからなければ、1.の拡張子を“.exe”に変更して、それをファイル名と見なす。
  5. アプリケーションのベース・ディレクトリで、4.で作ったファイル名のファイルを検索する。
  6. 5.で見つからなければ、アプリケーションのベース・ディレクトリの下にあるアセンブリの簡易名(util)と同じ名前のサブディレクトリで、4.で作ったファイル名のファイルを検索する。

 どこかでファイルが見つかれば、その時点で検索が終了する。手順6まで進んで結局見つからなかった場合は、“System.IO.FileNotFoundException”になる。

 また、構成ファイルを次のように書くことで、手順4と手順6で探すディレクトリを増やすこともできる。

プローブが行われる場合に検索されるディレクトリを追加した構成ファイル

 7行目のprobing要素に注目してほしい。この要素のprivatePath属性には、ディレクトリ名をセミコロンで区切って並べることができる。仮にuser.exeが「C:\Temp」ディレクトリから起動されたとすると、上記の構成ファイルでは次のようにプローブを行うことになる。

C:\Temp\util.DLL
C:\Temp\util\util.DLL
C:\Temp\bin\util.DLL
C:\Temp\bin\util\util.DLL
C:\Temp\bin2\subbin\util.DLL
C:\Temp\bin2\subbin\util\util.DLL
C:\Temp\subdir\util.DLL
C:\Temp\subdir\util\util.DLL
C:\Temp\util.EXE
C:\Temp\util\util.EXE
C:\Temp\bin\util.EXE
C:\Temp\bin\util\util.EXE
C:\Temp\bin2\subbin\util.EXE
C:\Temp\bin2\subbin\util\util.EXE
C:\Temp\subdir\util.EXE
C:\Temp\subdir\util\util.EXE

 一見、構成ファイルなど作らなくてもプローブを使えば正しくアセンブリをロードできるような気がするが、それは気がするだけで間違いだ。もう1度、上記手順1と手順4をよく読んでほしい。プローブで探しているのは、アセンブリの簡易名に拡張子を補って無理やり作り出した名前のファイルだ。つまり、ここではアセンブリの厳密名は完全に無視されてしまう。せっかく厳密名を付けても、プローブでアセンブリを探していると、期待どおりのアセンブリがロードされない可能性があるのだ。

 例えば上記のプローブの例では、開発者が正しいアセンブリを「C:\Temp\util」ディレクトリに配置していたとしても、ユーザーが誤って(あるいは何らかの悪意のあるプログラムが故意に)同じutil.dllという名前のアセンブリを「C:\Temp」に配置してしまったら、「C:\Temp」にある方が常にロードされてしまう。

 もちろん、ファイルがいったんロードされても、そのあとは上述の第3段階のチェックによって、厳密名が一致しているかどうかがチェックされる。ロードされたアセンブリの厳密名が正しくなければ、“System.IO.FileLoadException”例外になる。誤った内容のコードが実行されてしまうわけではない。だが、ユーザーがある名前のファイルをそこに配置しただけで(ファイルを上書きしてしまったわけではないので、ユーザーは気付かないかもしれない)、アプリケーションが動かなくなってしまうのは問題だろう。また、このプローブの手順では、次回に解説するバージョン管理が一切行えない。

 というわけで結論をもう1度繰り返すと、「アセンブリのロードにおいて、プローブに頼ってはならない。従って、アプリケーションと同じディレクトリにアセンブリを配置すればよいという説明は間違いだ」ということになるのだ。これはあくまでも非常手段として用意された手順であり、よい子は真似をしてはならない。

まとめ

 今回は、アセンブリのロードの手順を解説した。Windowsのローダーが従来のCOMやWin32のDLLをロードする方法と、CLRがアセンブリをロードする方法は、実はそれほど違わない。単に検索パスが変わったり、レジストリがGACや構成ファイルに置き換わったりしただけなのだ。ただし、この手順を正しく理解しておかなければ、せっかく開発したアセンブリを正しくロードして実行することができない。テスト段階でアセンブリのロード手順をいろいろ試してみて、そのアセンブリに最適な配置場所を決定すべきだろう。

 実は、アセンブリのロードにおいては、まだ説明を省略している部分がある。それがバージョン管理とセキュリティ機構だ。次回はバージョン管理問題を取り上げて、アセンブリのロード手順をより正確に解説する。End of Article


 INDEX
  解説 インサイド .NET Framework [改訂版]
  第3回 アセンブリのロード
    1.はじめにサンプル・アプリケーション
    2.アセンブリのロード 1〜2段階
    3.GACとコードベース
    4.アセンブリのロード 3〜5段階
  5.アセンブリの配置に関する補足とまとめ
 
インデックス・ページヘ  「解説:インサイド .NET Framework [改訂版]」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間