我们有两个版本的托管C ++程序集,一个用于x86,一个用于x64。该程序集由为AnyCPU编译的.net应用程序调用。我们正在通过文件副本安装来部署我们的代码,并且希望继续这样做。
当应用程序动态选择其处理器体系结构时,是否可以使用Side-by- Side程序集清单分别加载x86或x64程序集?还是有其他方法可以在文件副本部署中完成此任务(例如,不使用GAC)?
我创建了一个简单的解决方案,能够从编译为AnyCPU的可执行文件中加载特定于平台的程序集。使用的技术可以总结如下:
为了演示该技术,我将附带一个简短的基于命令行的教程。我在Windows XP x86和Vista SP1 x64上测试了生成的二进制文件(通过复制二进制文件,就像您的部署一样)。
注1 :“ csc.exe”是C尖锐的编译器。本教程假定它在您的路径中(我的测试使用的是“ 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 :主程序分为两部分。“ 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; } } }
“程序”是应用程序的“实际”实现(请注意,在Bootstrapper.Main的末尾调用了App.Run):
// 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;否则,将运行DLL。如果您在64位平台上运行program.exe,将加载platform \ amd64 \ library.dll。请注意,我在Worker.Run方法的末尾添加了Console.ReadLine(),以便您可以使用任务管理器/进程资源管理器来调查已加载的DLL,或者可以使用Visual Studio / Windows调试器将其附加到进程中以查看调用堆栈等
运行program.exe时,我们的自定义程序集解析器将附加到当前的appdomain。.NET一旦开始加载Program类,它就会看到对“库”程序集的依赖,因此它将尝试加载它。但是,找不到这样的程序集(因为我们已将其隐藏在platform / 子目录中)。幸运的是,我们的自定义解析器知道我们的诡计,并基于当前平台尝试从适当的platform / 子目录加载程序集。