在动态选择处理器架构时,是否可以使用“并排装配清单”分别加载x86或x64程序集?或者,在文件复制部署中是否有其他方法可以完成此操作(例如不使用GAC)?
(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
第三步:主程序分为两个部分。“引导程序”包含可执行文件的主入口点,并在当前应用程序域中注册了自定义程序集解析器:
// 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
第五步:我们现在完成了。我们创建的目录结构应该如下所示:(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时,我们的自定义程序集解析器被附加到当前应用程序域。 .NET一开始加载Program类时,发现对'library'程序集的依赖关系,因此尝试加载它。但是,找不到这样的程序集(因为我们已将其隐藏在platform/*子目录中)。幸运的是,我们的自定义解析器知道我们的诡计,并根据当前平台尝试从适当的platform/*子目录加载程序集。
我的版本类似于@Milan的版本,但有一些重要的更改:
使用AppDomain.CurrentDomain.SetupInformation.ApplicationBase
而不是Path.GetFullPath()
,因为当前目录可能会有所不同,例如在托管场景中,Excel可能会加载您的插件,但当前目录不会设置为您的DLL。
使用Environment.Is64BitProcess
而不是PROCESSOR_ARCHITECTURE
,因为我们不应该依赖于操作系统是什么,而是依赖于此进程如何启动-它可能是在x64 OS上的x86进程。在.NET 4之前,请使用IntPtr.Size == 8
。
在某个主类的静态构造函数中调用此代码,该类将在所有其他类之前加载。
public static class MultiplatformDllLoader
{
private static bool _isEnabled;
public static bool Enable
{
get { return _isEnabled; }
set
{
lock (typeof (MultiplatformDllLoader))
{
if (_isEnabled != value)
{
if (value)
AppDomain.CurrentDomain.AssemblyResolve += Resolver;
else
AppDomain.CurrentDomain.AssemblyResolve -= Resolver;
_isEnabled = value;
}
}
}
}
/// Will attempt to load missing assembly from either x86 or x64 subdir
private static Assembly Resolver(object sender, ResolveEventArgs args)
{
string assemblyName = args.Name.Split(new[] {','}, 2)[0] + ".dll";
string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
Environment.Is64BitProcess ? "x64" : "x86",
assemblyName);
return File.Exists(archSpecificPath)
? Assembly.LoadFile(archSpecificPath)
: null;
}
}
PROCESSOR_ARCHITECTURE
实际上反映的是进程而不是机器;尝试 Start->Run
%SYSTEMROOT%\SysWOW64\cmd /V:ON /q /c "echo This process is: !PROCESSOR_ARCHITECTURE! && pause"
。 - Fowl
PROCESSOR_ARCHITECTURE
是正确的 - 它实际反映了进程而不是机器。 - Fowl