普通にC#でアプリケーションを作るとAnyCPUのアセンブリができる。これは64ビット環境では64ビットで動作し、32ビットの場合は32ビットで動作する。
C++やC++/CLIで作ったライブラリはAnyCPUにすることができない。そしてWindowsでは64ビットアプリは64ビットのライブラリしかロードできない。32ビットも同じ。
AnyCPUのアプリをつくるときにこの問題をどう解決するかのはなし。ここではC++/CLIでライブラリをつくり、それをC#から利用することを考える。
C++/CLIで32ビットと64ビットのライブラリをつくる
作り方は省略。MyLib.x86.dllとMyLib.x64.dllができたとする。
C#でMyLib.x86.dllを参照する
参照するときに、プロジェクトの参照をするのではなく、ファイルを直接参照する。参照のプロパティのCopy LocalをFalseにする。こうすることでビルド時にライブラリがコピーされない。
C#のビルドイベントでMyLib.*をターゲットディレクトリにコピーする
この時のコピー先のパスはランタイムが自動で見つけられない場所にする。自動で見つけると違うdllをロードして例外が発生する。
以下はビルド後のコマンドの例、アプリのフォルダ配下のplatformにコピーしている。
1 2 3 |
mkdir "$(TargetDir)platform" copy "C:\Linkout\DNAssembly.NET4\MyLib.x86.*" "$(TargetDir)platform\" copy "C:\Linkout\DNAssembly.NET4\MyLib.x64.*" "$(TargetDir)platform\" |
この状態でアプリを起動してもライブラリが見つからない、のエラーが出る。
staticコンストラクタでカスタムローダを設定する
staticコンストラクタはstatic main()よりも早く動く。ここでライブラリが見つからないときのイベントハンドラを設定し、ハンドラで適切なライブラリを読み込む。以下はC#でデフォルトでできるクラスProgramに記述する例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private static System.Reflection.Assembly CustomResolve( object sender, System.ResolveEventArgs args) { if (args.Name.StartsWith("MyLib.x86")) { string filename = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "platform", string.Format("MyLib.{0}.dll", Environment.Is64BitProcess ? "x64" : "x86")); return System.Reflection.Assembly.LoadFile(filename); } return null; } static Program() { System.AppDomain.CurrentDomain.AssemblyResolve += CustomResolve; } |
もともとx86の方を参照しているので、ランタイムはこのファイルがないと通知してくる。ここで環境(Environment.Is64BitProcess)に合わせてライブラリを読み込む。
参照
Using Side-by-Side assemblies to load the x64 or x32 version of a DLL