AnyCPU としてコンパイルされた実行可能ファイルからプラットフォーム固有のアセンブリをロードできる単純なソリューションを作成しました。使用される技術は次のように要約できます。
- デフォルトの .NET アセンブリ ロード メカニズム (「Fusion」エンジン) が、プラットフォーム固有のアセンブリの x86 または x64 バージョンを検出できないことを確認します。
- メイン アプリケーションがプラットフォーム固有のアセンブリの読み込みを試みる前に、現在の AppDomain にカスタム アセンブリ リゾルバーをインストールします。
- メイン アプリケーションがプラットフォーム固有のアセンブリを必要とする場合、Fusion エンジンは (ステップ 1 のため) あきらめ、カスタム リゾルバーを呼び出します (ステップ 2 のため)。カスタム リゾルバーでは、現在のプラットフォームを特定し、ディレクトリ ベースのルックアップを使用して適切な DLL をロードします。
この手法を実演するために、コマンドライン ベースの短いチュートリアルを添付します。結果のバイナリを Windows XP x86 でテストし、次に Vista SP1 x64 でテストしました (展開と同じように、バイナリをコピーして)。
注 1 : 「csc.exe」は C-sharp コンパイラです。このチュートリアルでは、それがパスにあることを前提としています (私のテストでは "C:\WINDOWS\Microsoft.NET\Framework\v3.5\csc.exe" を使用していました)
注 2 : テスト用の一時フォルダーを作成し、現在の作業ディレクトリがこの場所に設定されているコマンド ライン (または PowerShell) を実行することをお勧めします。
(cmd.exe)
C:
mkdir \TEMP\CrossPlatformTest
cd \TEMP\CrossPlatformTest
ステップ 1 : プラットフォーム固有のアセンブリは、単純な C# クラス ライブラリによって表されます。
// file 'library.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Library
{
public static class Worker
{
public static void Run()
{
System.Console.WriteLine("Worker is running");
System.Console.WriteLine("(Enter to continue)");
System.Console.ReadLine();
}
}
}
ステップ 2 : 簡単なコマンドライン コマンドを使用して、プラットフォーム固有のアセンブリをコンパイルします。
(cmd.exe from Note 2)
mkdir platform\x86
csc /out:platform\x86\library.dll /target:library /platform:x86 library.cs
mkdir platform\amd64
csc /out:platform\amd64\library.dll /target:library /platform:x64 library.cs
ステップ 3 : メイン プログラムは 2 つの部分に分割されます。「Bootstrapper」には、実行可能ファイルのメイン エントリ ポイントが含まれており、現在の appdomain にカスタム アセンブリ リゾルバーを登録します。
// file 'bootstrapper.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Program
{
public static class Bootstrapper
{
public static void Main()
{
System.AppDomain.CurrentDomain.AssemblyResolve += CustomResolve;
App.Run();
}
private static System.Reflection.Assembly CustomResolve(
object sender,
System.ResolveEventArgs args)
{
if (args.Name.StartsWith("library"))
{
string fileName = System.IO.Path.GetFullPath(
"platform\\"
+ System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
+ "\\library.dll");
System.Console.WriteLine(fileName);
if (System.IO.File.Exists(fileName))
{
return System.Reflection.Assembly.LoadFile(fileName);
}
}
return null;
}
}
}
「プログラム」は、アプリケーションの「実際の」実装です (App.Run は Bootstrapper.Main の最後で呼び出されたことに注意してください)。
// file 'program.cs' in C:\TEMP\CrossPlatformTest
namespace Cross.Platform.Program
{
public static class App
{
public static void Run()
{
Cross.Platform.Library.Worker.Run();
}
}
}
ステップ 4 : コマンド ラインでメイン アプリケーションをコンパイルします。
(cmd.exe from Note 2)
csc /reference:platform\x86\library.dll /out:program.exe program.cs bootstrapper.cs
ステップ 5 : これで完了です。作成したディレクトリの構造は次のようになります。
(C:\TEMP\CrossPlatformTest, root dir)
platform (dir)
amd64 (dir)
library.dll
x86 (dir)
library.dll
program.exe
*.cs (source files)
32 ビット プラットフォームで program.exe を実行すると、platform\x86\library.dll が読み込まれます。64 ビット プラットフォームで program.exe を実行すると、platform\amd64\library.dll が読み込まれます。Worker.Run メソッドの最後に Console.ReadLine() を追加したことに注意してください。これにより、タスク マネージャー/プロセス エクスプローラーを使用して読み込まれた DLL を調査したり、Visual Studio/Windows Debugger を使用してプロセスにアタッチして、コールスタックなど
program.exe が実行されると、カスタム アセンブリ リゾルバーが現在の appdomain にアタッチされます。.NET が Program クラスのロードを開始するとすぐに、'library' アセンブリへの依存関係が検出されるため、それをロードしようとします。ただし、そのようなアセンブリは見つかりません (platform/* サブディレクトリに隠しているため)。幸いなことに、私たちのカスタム リゾルバーは私たちの策略を知っており、現在のプラットフォームに基づいて、適切な platform/* サブディレクトリからアセンブリを読み込もうとします。