今回は、.NET の アプリケーションドメイン(AppDomain)について整理します。
AppDomain は、.NET ランタイム上でコードを実行するための論理的な分離単位です。
.NET Framework では、1 つのプロセス内に複数のアプリケーションドメインを作り、アセンブリの読み込みや実行を分離するために使われていました。
現在の .NET(.NET Core / .NET 5 以降)では、昔のように自由に複数の AppDomain を作ってアンロードする設計は中心ではありません。
それでも AppDomain.CurrentDomain を通して、現在のアプリケーションドメイン情報や読み込み済みアセンブリを調べることはできます。
AppDomain とは何か
AppDomain は、.NET アプリケーションが実行される論理的な境界です。
Windows のプロセスは OS レベルの分離単位ですが、AppDomain は CLR(Common Language Runtime)内の分離単位です。
つまり、ざっくり言うと次のような関係になります。
- プロセス: OS が管理する実行単位
- AppDomain: CLR が管理する .NET コードの実行境界
- アセンブリ: AppDomain に読み込まれて実行される DLL / EXE
.NET Framework では、1 つのプロセス内に複数の AppDomain を作ることで、プラグインや外部コードをある程度分離して実行できました。
また、アセンブリをアンロードしたい場合は、個別のアセンブリではなく、そのアセンブリを読み込んだ AppDomain ごとアンロードする、という考え方が使われていました。
ただし、現在の .NET では AppDomain による分離・アンロードは限定的です。
プラグインのアンロードや動的ロードを扱う場合は、System.Runtime.Loader.AssemblyLoadContext を使うのが現代的な選択肢です。
それでも、AppDomain は「今の .NET アプリがどの実行環境で動いているか」「どのアセンブリが読み込まれているか」を確認する入口として役立ちます。
System.AppDomain クラス
System.AppDomain クラスは、アプリケーションドメインを表す型です。
主に次のような情報や操作を提供します。
CurrentDomain
- 現在実行中のコードが属しているアプリケーションドメインを取得します。通常、最初に触るのはこのプロパティです。
FriendlyName
- アプリケーションドメインの表示名を取得します。コンソールアプリでは実行ファイル名に近い名前になることがあります。
BaseDirectory
- アプリケーションの基準ディレクトリを取得します。設定ファイルや相対パス解決の確認に使えます。
GetAssemblies()
- 現在のアプリケーションドメインに読み込まれているアセンブリ一覧を取得します。診断、ログ出力、プラグイン調査などに便利です。
SetupInformation
- アプリケーションドメインの構成情報を取得します。ただし、現在の .NET では利用できる情報や意味が .NET Framework 時代と異なる場合があります。
既定のアプリケーションドメインを操作する
.NET アプリケーションが起動すると、基本的には既定のアプリケーションドメイン上でコードが実行されます。
まずは AppDomain.CurrentDomain を使って、現在のドメイン情報を確認してみます。
using System;
AppDomain defaultDomain = AppDomain.CurrentDomain;
Console.WriteLine("現在のアプリケーションドメイン情報");
Console.WriteLine($"FriendlyName: {defaultDomain.FriendlyName}");
Console.WriteLine($"BaseDirectory: {defaultDomain.BaseDirectory}");
Console.WriteLine($"IsFullyTrusted: {defaultDomain.IsFullyTrusted}");
Console.WriteLine($"TargetFrameworkName: {defaultDomain.SetupInformation.TargetFrameworkName}");
実行すると、現在のアプリケーションがどのドメイン上で動いているかを確認できます。
FriendlyName は、人間が識別しやすい名前です。
BaseDirectory は、アプリケーションの基準パスです。
IsFullyTrusted は、完全信頼で実行されているかを示します。
.NET Framework の古いコードでは、AppDomain.CreateDomain() で新しいドメインを作り、そこへコードを読み込む例がよく登場します。
しかし、現在の .NET ではこの方向の API は制限されており、同じ設計をそのまま持ち込むのはおすすめしません。
読み込み済みアセンブリを列挙する
AppDomain.GetAssemblies() を使うと、現在のアプリケーションドメインに読み込まれているアセンブリを列挙できます。
using System;
using System.Linq;
using System.Reflection;
AppDomain currentDomain = AppDomain.CurrentDomain;
Assembly[] assemblies = currentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies.OrderBy(a => a.GetName().Name))
{
AssemblyName name = assembly.GetName();
Console.WriteLine($"Name: {name.Name}");
Console.WriteLine($"Version: {name.Version}");
Console.WriteLine($"FullName: {assembly.FullName}");
Console.WriteLine($"Location: {GetLocationOrEmpty(assembly)}");
Console.WriteLine();
}
static string GetLocationOrEmpty(Assembly assembly)
{
try
{
return string.IsNullOrEmpty(assembly.Location)
? "(dynamic or in-memory assembly)"
: assembly.Location;
}
catch (NotSupportedException)
{
return "(location is not supported)";
}
}
このコードでは、読み込まれているアセンブリの名前、バージョン、完全名、ファイルパスを表示しています。
注意点として、すべてのアセンブリが実ファイルの場所を持っているとは限りません。
動的に生成されたアセンブリや、メモリ上に読み込まれたアセンブリでは Location が空文字になったり、取得できなかったりする場合があります。
AppDomain と AssemblyLoadContext の違い
現代の .NET では、アセンブリの動的読み込みやアンロードを考えるとき、AppDomain よりも AssemblyLoadContext が重要です。
AppDomain は、現在の実行環境や読み込み済みアセンブリを確認するための入口として使えます。
一方で、プラグインの読み込み、バージョンの異なるアセンブリの分離、不要になったアセンブリのアンロードなどは、AssemblyLoadContext の領域です。
今回の範囲では、まず次の理解で十分です。
AppDomain: CLR の実行境界を表す古典的な概念。現在のドメイン情報や読み込み済みアセンブリ確認に使えるAssemblyLoadContext: 現代の .NET でアセンブリ読み込み・分離・アンロードを扱う仕組み
まとめ
AppDomain は、.NET アプリケーションが動作する論理的な実行領域です。
.NET Framework では分離やアンロードのために重要な役割を持っていましたが、現在の .NET ではその役割の多くを AssemblyLoadContext が担っています。
それでも、AppDomain.CurrentDomain と GetAssemblies() は今でも有用です。
現在のアプリケーションドメインの情報を確認したり、読み込み済みアセンブリを列挙したりすることで、実行環境の理解や診断に役立ちます。