.NET Native とコンパイル
.NET Framework を対象にした Windows デスクトップ アプリケーションは、特定のプログラミング言語で記述され、中間言語 (IL) にコンパイルされます。 実行時は、メソッドが初めて実行される直前に、ジャストインタイム (JIT) コンパイラによって、IL がローカル コンピューターのネイティブ コードにコンパイルされます。 それとは対照的に、.NET ネイティブ ツール チェーンでは、コンパイル時にソース コードをネイティブ コードに変換します。 この記事では、.NET ネイティブと、.NET Framework アプリで利用可能な他のコンパイル テクノロジとを比較します。また、.NET ネイティブによるネイティブ コードの生成方法についての実用的な概要を示します。この概要は、.NET ネイティブでコンパイルされたコードで発生した例外が JIT コンパイル コードで発生しない理由を理解するのに役立ちます。
ネイティブ バイナリの生成
.NET Framework を対象とし、かつ .NET ネイティブ ツール チェーンを使用してコンパイルされていないアプリケーションは、次のようなアプリケーション アセンブリで構成されます。
アセンブリ、その依存関係、それに含まれる型、そのメンバーを記述したメタデータ。 メタデータは、リフレクションと遅延バインディング アクセスのために使用され、場合によってはコンパイラとビルド ツールによって使用されることもあります。
実装コード。 これは、中間言語 (IL) オペコードで構成されます。 実行時には、JIT (Just-In-Time) コンパイラが IL コードを対象のプラットフォームのネイティブ コードに変換します。
アプリでは、メイン アプリケーション アセンブリに加え、次のものが存在している必要があります。
アプリに必要な他のすべてクラス ライブラリまたはサード パーティ製のアセンブリ。 これらのアセンブリには、アプリと同様に、アセンブリ、その型、およびそのメンバーを記述したメタデータが含まれるほか、すべての型のメンバーを実装する IL が含まれます。
.NET Framework クラス ライブラリ。 これは、.NET Framework のインストール時にローカル システムにインストールされるアセンブリのコレクションです。 .NET Framework クラス ライブラリに含まれるアセンブリには、メタデータと実装コードの完全なセットが含まれます。
共通言語ランタイム。 これは、アセンブリの読み込み、メモリ管理とガベージ コレクション、例外処理、JIT コンパイル、リモート処理、および相互運用機能などのサービスを実行するダイナミック リンク ライブラリのコレクションです。 クラス ライブラリと同様に、このランタイムは .NET Framework のインストールの一部としてローカル システムにインストールされます。
アプリを正常に実行するためには、共通言語ランタイム全体だけでなく、アプリケーション固有のアセンブリ、サード パーティ製のアセンブリ、およびシステム アセンブリに含まれるすべての型のメタデータと IL が存在している必要があることに注意してください。
ジャストインタイム コンパイル
.NET ネイティブ ツール チェーンへの入力は、C# または Visual Basic コンパイラでビルドされた UWP アプリです。 したがって、.NET ネイティブ ツール チェーンの実行が開始されるのは、言語コンパイラが UWP アプリのコンパイルを終了した時点です。
ヒント
.NET ネイティブへの入力は、マネージド アセンブリ向けに記述された IL とメタデータであるため、ビルド前またはビルド後のイベントを使用するか、または MSBuild プロジェクト ファイルを変更することにより、カスタム コードの生成やその他のカスタム操作を実行する余地があります。
ただし、IL を変更する種類のツール、つまり .NET ツール チェーンがアプリの IL を解析することを妨げるツールはサポートされません。 難読化ツールは、この種類のツールとして代表的なものです。
アプリを IL からネイティブ コードに変換する過程で、.NET ネイティブ ツール チェーンは次のような操作を実行します。
特定のコード パスについて、リフレクションやメタデータに依存するコードを、静的なネイティブ コードで置き換えます。 たとえば、値型が ValueType.Equals メソッドをオーバーライドしない場合、等しいかどうかの既定のテストは、リフレクションを使用して値型のフィールドを表す FieldInfo オブジェクトを取得することによって行われるため、2 つのインスタンスのフィールド値の比較になります。 ネイティブ コードにコンパイルするとき、.NET ネイティブ ツール チェーンは、このリフレクションのコードとメタデータをフィールド値の静的な比較に置き換えます。
可能であれば、すべてのメタデータを削除しようとします。
アプリにより実際に呼び出される実装コードのみを、最終的なアプリ アセンブリに組み込みます。 これによる効果は、特にサード パーティ製ライブラリや、.NET Framework クラス ライブラリの場合に発揮されます。 この操作の結果として、アプリケーションはサード パーティ製のライブラリにも完全な .NET Framework クラス ライブラリにも依存しなくなり、サード パーティ製のライブラリおよび .NET Framework クラス ライブラリに含まれるコードがアプリに対してローカルになります。
完全な CLR を、主としてガベージ コレクターを含む、リファクタリングされたランタイムに置き換えます。 リファクタリングされたランタイムは、アプリに対してローカルで、サイズがわずか数百キロバイトの mrt100_app.dll という名前のライブラリに含まれます。 これが可能になるのは、静的リンクを使用することにより、共通言語ランタイムによって実行されるサービスの多くが不要になるためです。
Note
.NET ネイティブでは、標準の共通言語ランタイムと同じガベージ コレクターが使用されます。 .NET ネイティブのガベージ コレクターでは、既定でバックグラウンド ガベージ コレクションが既定で有効になります。 ガベージ コレクションの詳細については、「ガベージ コレクションの基礎」を参照してください。
重要
.NET ネイティブは、アプリケーション全体をネイティブ アプリケーションにコンパイルします。 ネイティブ コードに対するクラス ライブラリを含む 1 つのアセンブリをコンパイルして、マネージド コードから独立して呼び出せるようにすることはできません。
.NET ネイティブ ツール チェーンによって生成される結果のアプリは、プロジェクト ディレクトリの Debug または Release ディレクトリ内の ilc.out という名前のディレクトリに出力されます。 これは次のようなファイルで構成されます。
<appName>.exe。<appName>.dll 内にある特殊な
Main
エクスポートに制御を移すためのみの、スタブ実行可能ファイルです。<appName>.dll。すべてのアプリケーション コードに加え、.NET Framework クラス ライブラリや、依存関係が存在するサードパーティ ライブラリからのコードが含まれている Windows ダイナミック リンク ライブラリです。 さらに、たとえば Windows と相互運用するために必要なコードや、アプリ内のオブジェクトをシリアル化するために必要なコードなどのサポート コードも格納しています。
mrt100_app.dll。リファクタリングしたランタイムであり、ガベージ コレクションなどのランタイム サービスを提供します。
すべての依存関係は、アプリの APPX マニフェストによってキャプチャされます。 appx パッケージに直接バンドルされるアプリケーションの exe、dll、および mrt100_app.dll に加えて、さらに次の 2 つのファイルが含まれます。
msvcr140_app.dll。mrt100_app.dll によって使用される C ランタイム (CRT) ライブラリです。 これはパッケージ内のフレームワーク参照によって組み込まれます。
mrt100.dll。 mrt100_app.dll のパフォーマンスを向上させる関数が含まれるライブラリですが、このファイルが存在しなくても mrt100_app.dll は機能します。 これが存在する場合は、ローカル コンピューターの system32 ディレクトリから読み込まれます。
.NET ネイティブ ツール チェーンはアプリが実際に呼び出すことがわかっている実装コードのみをアプリにリンクするため、次のシナリオで必要なメタデータや実装コードはアプリに組み込まれないことがあります。
リフレクション。
動的呼び出しまたは遅延バインディング呼び出し。
シリアル化と逆シリアル化。
COM 相互運用。
必要なメタデータや実装コードが実行時に存在しない場合は、.NET ネイティブ ランタイムが例外をスローします。 これらの例外を回避し、必要なメタデータと実装コードが .NET ネイティブ ツール チェーンによって組み込まれたことを確認するには、ランタイム ディレクティブ ファイルという XML ファイルを使用します。このファイルでは、実行時に利用可能であることが必要なメタデータまたは実装コードを含むプログラム要素を指定し、それらのプログラム要素にランタイム ポリシーを割り当てます。 .NET ネイティブ ツール チェーンによってコンパイルされた UWP プロジェクトに追加される、既定のランタイム ディレクティブ ファイルを次に示します。
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="*Application*" Dynamic="Required All" />
</Application>
</Directives>
これにより、アプリ パッケージ内のすべてのアセンブリに含まれるすべての型と、それに含まれるすべてのメンバーのリフレクションと動的呼び出しが可能になります。 ただし、.NET Framework クラス ライブラリ アセンブリに含まれる型のリフレクションと動的なアクティブ化は可能になりません。 多くの場合、これで十分です。