アセンブリとは?

前置き

Win32やMFCの世界から.NETへ入門すると、まず最初に目にするのが「アセンブリ」や「マニフェスト」といった用語です。何でいまさらアセンブラなん? とか思ってしまいますが、だまされてはいけません。そもそもassembleは「組み立てる」という動詞。アセンブリ(assembly)は「集合体」みたいな意味で使ってるんだと思います。マニフェストはまぁ、かつての流行語ですね。

で、アセンブリって何? という話しです。私の理解を書きますが、間違っている可能性も大いにあります。

本文

MS曰く、アセンブリとは

  • 物理的なコードのグループ分けの基本単位
  • 機能の論理的単位を構成する型とリソースの集まり
  • .NET Frameworkのビルドブロック
  • 配置、バージョン管理、再利用、アクティブ化のスコープの指定、およびセキュリティアクセス許可の基本単位

Windowsにとってのexeやdllに相当するもの

レガシー(非.NET)と.NETに分けて考えてみます。

レガシーの世界では、OSとしてのWindowsが、exeやdllをメモリにロードして実行します。objやスタティックライブラリ(lib)の状態では、メモリにロードできません。

一方、.NETの世界では、(やや乱暴な言い方ですが)CLRがOSに相当します。で、このとき、レガシーでのexeやdllに相当するものを、.NETではアセンブリと呼んでいるんだと思います。

アセンブリとは、CLRがメモリにロードして実行できるファイルのこと。で、9割がた合ってるんじゃないでしょうか。

アセンブリはファイルなのか?

ややこしいことに、アセンブリとファイルは1対1の関係になっているとは限りません。1対多の可能性があります。つまり、1つのアセンブリが、複数のファイルで構成される場合があります。これをマルチファイルアセンブリと呼びます(1対1の場合はシングルファイルアセンブリ)。

複数のファイルに分かれうるということは、アセンブリの中に何らかの構造があるということですね。確かにあります。アセンブリは以下の情報で構成されます。

  • 1つのマニフェスト
  • 1つ以上のコードモジュール
  • 0、または1つ以上のリソース

マニフェストは、アセンブリ名やバージョン、アセンブリを構成するモジュールの一覧といったメタ情報です。コードモジュール(あるいは単にモジュール)が、CIL(MSIL)で書かれたプログラム本体です。リソースはイメージとか音とか、あのてのやつです。

Visual Studioでアプリを作ると、マニフェストとコードモジュールが1つずつ(と任意のリソースが)含まれたシングルファイルアセンブリになります。

一方、マルチファイルアセンブリを作るには、コマンドラインツールを使う必要があります。まずコンパイラに適切なオプションを指定することにより、マルチファイルアセンブリ用にビルドされたコードモジュール(拡張子はnetmodule)を作ります。次に、1つ、または複数のnetmoduleファイルをアセンブリリンカ(al.exe)でリンクしてアセンブリファイル(exe)を作ります。「なんだ、1つのexeになるんじゃん」と早とちりしてはいけません。このexeにはマニフェストだけが含まれています。このexeと、リンク時に指定したnetmoduleをまとめて1つのアセンブリが成立しています。

マルチファイルアセンブリの利点として、例えば、ネットからダウンロードして実行するときに、重たいコードモジュールやリソースファイルのダウンロードを、必要なタイミングまで遅らせることができることなどが挙げられます。

DLL Hell

MSによるアセンブリの定義に「バージョン管理の基本単位」というのがありました。これは、DLL Hellと呼ばれる状況を回避する上で重要です。

DLL Hellとは、共有モジュールのバージョン間に互換性が無いことによって引き起こされるカオスのことです。例を挙げてみましょう。

  • あなたは、A社のアプリMoon 6.0を使っている
  • Moon 6.0をインストールすると、共有ライブラリcommon.dll(バージョン1.0)がC:\Program Files\Common Files\A-Corp\にコピーされる
  • Moon 6.0はcommon.dll無しでは動かない
  • そこへ、同じくA社の新発売のアプリTitan 12.0をインストールする
  • Titan 12.0に必要なcommon.dll(バージョン9.0)が上書きコピーされる
  • common.dllのバージョン1.0と9.0の間には互換性が無い
  • Moon 6.0は動かなくなってしまった

DLLに限らず、この手の問題には良く遭遇しますね。

で、マイクロソフトは、サイドバイサイドという仕組みを使って、これに対処しています。詳しいことは分かりませんが、この仕組みを使うと、同じアセンブリの複数のバージョンが共存できるようです。上の例で言えば、後からインストールしたcommon.dllが、古いcommon.dllを上書きしなくて済むということです。

dllとexeから成るアプリを開発していると、たまに「アプリケーションのサイドバイサイド構成が正しくないため、アプリケーションを開始できません」みたいなエラーが出ることがありますが、これは、exeをビルドしたときに参照していたdllと互換性のあるバージョンのdllが、実行時に見つからない場合に起きます。

このように、アセンブリのバージョンは、.NETがアプリを安全に実行する上で重要な役割を果たしているわけです。

ちなみにアセンブリのバージョンは、4つの32bit値から成り、1.0.0.1のように表記されます。VC.NETならAssemblyInfo.cppの中で定義します。

  • AssemblyInfo.cpp
//
// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を
// 既定値にすることができます:
[assembly:AssemblyVersionAttribute("1.0.*")];

最初の2つで互換性の有無が判断されます。例えば、common.dllの2.1.1.1と2.2.0.0と2.2.3.4がインストールされており、Moon.exeをビルドするときにcommon.dllの2.2.0.0を使っていた場合、Moon.exeを実行すると、2.2.3.4(互換性のある最新バージョン)がロードされるらしいです。

Last modified:2011/11/24 20:28:08
Keyword(s):
References:[.NETアプリ開発]
This page is frozen.