CLRのロード
mscoree.dllに入っている_CorExeMain(あるいは_CorDllMain)関数の仕事はあまり多くない。このDLLはshim(「詰め木」などの意)と呼ばれていて、実際の仕事はほとんどしない。_CorExeMain関数はすぐにmscorwks.dllかmscorsvr.dllを呼び出し、すべての処理はそこで行われる。どちらのDLLを呼び出すかを選択するのがmscoree.dllの_CorExeMain関数の役割だ。ほとんどの場合はmscorwks.dllがロードされる。マルチプロセッサのシステム上でプログラムが明示的に選択すれば、mscorsvr.dllを使うこともできる。mscorwks.dll/mscorsvr.dllがCLRの実体だといっていいだろう。
mscorwks.dll/mscorsvr.dllは、%Systemroot%\Microsoft.NET\Framework\<バージョン>フォルダからロードされる。つまり、これらのファイル(CLRの本体)は、.NET Frameworkのバージョンごとに別々に存在するわけだ。しかし、これらをロードする役目を持つmscoree.dllは、%Systemroot%\System32に配置される。つまり、.NET Frameworkのバージョンに関係なく、マシンに1つしかないことになる。例えば、Windows Server 2003にはあらかじめ.NET Framework 1.1がインストールされている。そのマシンにあとから.NET Framework 1.0をインストールすると、%Systemroot%\Microsoft.NET\Framework\v1.0.3705フォルダが作成され、バージョン1.0のmscorwks.dll/mscorsvr.dllが新しくそこに配置される。しかし、mscoree.dllはすでにバージョン1.1のものが%Systemroot%\System32に配置されているので、バージョン1.0のものは配置されない。それでは、そのマシンで.NET Framework 1.0向けに開発されたEXEアセンブリが実行されようとしたときには、実際にロードされるCLRは1.0のものなのか、それとも1.1のものなのだろうか。
.NET Framework 1.1がインストールされているマシンで1.0向けのアセンブリが実行される場合、実際にロードされるCLR(mscorwks.dll/mscorsvr.dll)はどちらのバージョンとなるのだろうか?
mscoree.dllは、ロードするCLRのバージョンを特定する仕組みを2種類提供している。1つはアプリケーションがバージョンを明示する方法で、もう1つはCLRを起動するホスト・プロセスがバージョン番号を明示することである
*1
。後者は、今回解説しているデフォルトのロード機能を利用している場合は適用できない。例えば.NET Framework 1.0におけるaspnet_wp.exeがホスト・プロセスの例である(aspnet_wp.exeはIIS 5.0におけるASP.NET用のワーカープロセス。aspnet_wp.exeはCLRをホストし、ASP.NETのWebページを処理する)。後者を実現する場合は、ホスト・プロセスを開発して、CLRをロードするコードを自分で記述しなければならない。これ以降は、前者の仕組みだけを解説する。
*1
環境変数を設定する方法もあるが、あまり実践では使われないため、ここでは解説しない。
アセンブリのメタデータには、そのアセンブリがコンパイルされた.NET Frameworkのバージョンが含まれている。コンパイルしたアセンブリのバイナリ・ファイルを、無理やりメモ帳で開いてみれば、中に「v1.0.3705」や「v1.1.4322」という文字列が見えるだろう。
EXEファイルのアセンブリが起動されたときに、ほかに何も設定をしていない場合は、ここで指定されたバージョンで%Systemroot%\Microsoft.NET\Framework\<バージョン>のパスを置き換えて、mscorwks.dll/mscorsvr.dllをロードするフォルダが決定される。そのため通常は、.NET Framework 1.0向けにコンパイルされたアセンブリを利用する場合は、マシンにも.NET Framework 1.0をインストールしなければならない。同様に、.NET Framework 1.1のコンパイラでコンパイルされたアセンブリを使うには、.NET Framework 1.1をインストールしなければならない。
このように、.NET Framework 1.1向けにコンパイルされたEXEアセンブリが、.NET Framework 1.0しかインストールされていない環境で起動されると、エラーが発生して動作しない。一方、.NET Framework 1.0向けにコンパイルされたEXEアセンブリが、.NET Framework 1.1しかインストールされていない環境で起動された場合には、1.1のCLRがロードされる。この動作は、レジストリの設定によって実現されている。mscoree.dllは、要求されたバージョン(この場合はv1.0.3705)がマシンにインストールされていないことを発見すると、レジストリを開いて
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy\Upgrades
というキーを確認する。.NET Framework 1.1がインストールされている場合、デフォルトではこのキーには次のような値が設定されている。
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy\Upgrades]
"1.1.4322"="1.0.3705-1.1.4322"
つまり、1.0.3705から1.1.4322までのバージョンのCLRが要求された場合に、それを1.1.4322で代用することが許可されているのである。このため.NET Framework 1.1しかインストールされていない環境で.NET Framework 1.0向けにコンパイルされたアセンブリが実行できるようになっている。
EXEアセンブリが起動した後に、さらに別のアセンブリを呼び出した場合はどうなるだろうか。例えば次のコードをコンパイルすると、mscorlibアセンブリとSystem.Dataアセンブリへの参照がメタデータに記述される。
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System"
publicKeyToken="b77a5c561934e089"
culture="neutral" />
<bindingRedirect
oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="1.0.5000.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Configuration.Install"
publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect
oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="1.0.5000.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Data"
publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect
oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="1.0.5000.0" />
</dependentAssembly>
.NET Framework 1.0のアセンブリへの参照は、.NET Framework 1.1のアセンブリへとリダイレクトされる。このため、.NET Framework 1.0向けのアセンブリが、.NET Framework 1.1しかインストールされていない環境で動作する。
もちろんこのような情報は、%Systemroot%\Microsoft.NET\Framework\v1.0.3705フォルダにあるfusion.dllには記述されていない。そのため、上記のDataSetクラスにアクセスするコードを.NET Framework 1.1の環境でコンパイルして、.NET Framework 1.0しかインストールされていない環境で実行しようとすると、エラーになる。.NET Framework 1.1は.NET Framework 1.0と互換性があることにはなっているものの、若干ながら動作が異なる機能や、.NET Framework 1.1で新しく用意された機能がある。これらの機能を利用している.NET Framework 1.1ベースのアプリケーションは、.NET Framework 1.0上では動作しないのは自明であろう。そのため、.NET Framework 1.1向けにコンパイルされたアセンブリは、デフォルトでは.NET Framework 1.0のみの環境では動作しない。
.NET Framework 1.1は1.0と互換性があることにはなっているものの、動作が異なる機能や、1.1で新しく用意された機能がある。これらの機能を利用している1.1ベースのアプリケーションは、1.0上では動作しない。
.NET Framework 1.1向けにコンパイルしたものの、.NET Framework 1.0でも動作することが確認できている場合は、対応可能なCLRのバージョンをアプリケーションの構成ファイルに記述して、古いバージョンの.NET Frameworkしかインストールされていないマシン上で実行できる機能がある。構成ファイルに次のように<startup>要素と<requiredRuntime>要素を記述する。
アセンブリが対応可能なCLRのバージョンを構成ファイルに記述しておけば、古いバージョンの.NET Frameworkしかインストールされていないマシン上でも実行させることができる。
このような構成ファイルがあると、.NET Framework 1.0のmscoree.dllは%Systemroot%\Microsoft.NET\Framework\v1.0.3705というフォルダからmscorwks.dll/mscorsvr.dllをロードしようとする。
ただし、構成ファイルを上記のように記述したときに、.NET Framework 1.1用にコンパイルされたアセンブリから利用できるのは、.NET Framework 1.0のmscorlibアセンブリだけである。従って、このままでは上記のDataSetクラスにアクセスするコードはやはりエラーで実行できない。
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Data"
publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect
oldVersion="0.0.0.0-65535.65535.65535.65535"
newVersion="1.0.3300.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
現在の.NET Frameworkでは、CLRは1つのプロセスに1つしかロードできないため、最初に起動するEXEアセンブリ(またはホスト・プロセス)がロードするCLRのバージョンを決定することになる。CLRのバージョンが決定すると、mscorlibアセンブリのバージョンだけは自動的にCLRと同じバージョンのものに決定される。そのほかのアセンブリ(システムのアセンブリも含む)には、通常通りのバージョン・ポリシーが適用されることになる。
なお、.NET Framework 1.1では、<supportedRuntime>要素が新たに追加されたが、いまのところこの要素の出番はない。この要素は.NET Framework 1.1のmscoree.dllしか解釈できないので、次の表のように、この要素を利用する場面はないからである。
アセンブリ\.NET Framework
1.0のみ
1.1のみ
1.0と1.1を両方インストール
1.0でコンパイル
1.0で動作
1.1で動作
※
1.0で動作
1.1でコンパイル
<requiredRuntime>要素があれば1.0で動作可能。なければ動作しない。
1.1で動作
1.1で動作
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用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える