如何在运行时延迟绑定 32 位/64 位库
题
我遇到的问题与所描述的类似,但略有不同 这里 (加载程序集及其依赖项)。
我有一个用于 3D 渲染的 C++ DLL,这是我们卖给客户的。对于 .NET 用户,我们将有一个 CLR 包装器。C++ DLL 可以构建为 32 位和 64 位版本,但我认为这意味着我们需要两个 CLR 包装器,因为 CLR 绑定到特定的 DLL?
假设现在我们的客户有一个 .NET 应用程序,可以是 32 位或 64 位,并且它是一个纯 .NET 应用程序,它让 CLR 通过一组程序集来解决它。问题是应用程序代码如何在运行时动态选择 32 位和 64 位 CLR/DLL 组合?
更具体地说,上述问题的建议答案也适用于此(即创建一个 ResolveEvent 处理程序)?
解决方案
我终于找到了一个似乎有效的答案。
将 32 位和 64 位版本(托管和非托管)编译到单独的文件夹中。然后让 .NET 应用程序在运行时选择从哪个目录加载程序集。
使用 ResolveEvent 的问题是,只有在未找到程序集时才会调用它,因此很容易意外地得到 32 位版本。相反,使用第二个 AppDomain 对象,我们可以在其中更改 ApplicationBase 属性以指向正确的文件夹。所以你最终会得到如下代码:
static void Main(String[] argv)
{
// Create a new AppDomain, but with the base directory set to either the 32-bit or 64-bit
// sub-directories.
AppDomainSetup objADS = new AppDomainSetup();
System.String assemblyDir = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
switch (System.IntPtr.Size)
{
case (4): assemblyDir += "\\win32\\";
break;
case (8): assemblyDir += "\\x64\\";
break;
}
objADS.ApplicationBase = assemblyDir;
// We set the PrivateBinPath to the application directory, so that we can still
// load the platform neutral assemblies from the app directory.
objADS.PrivateBinPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
AppDomain objAD = AppDomain.CreateDomain("", null, objADS);
if (argv.Length > 0)
objAD.ExecuteAssembly(argv[0]);
else
objAD.ExecuteAssembly("MyApplication.exe");
AppDomain.Unload(objAD);
}
您最终会得到 2 个 exe - 您的普通应用程序和第二个选择要加载的位的切换应用程序。注意——我自己不能相信这个细节。根据我最初的指示,我的一位同事推测出了这一点。如果他注册了 StackOverflow,我会将答案分配给他
其他提示
大约一年前我能够做到这一点,但我不再记得所有细节。基本上,您可以使用 IntPtr.Size 来确定要加载哪个 DLL,然后通过 p/Invoke 执行实际的 LoadLibrary。此时,您已经在内存中获得了该模块,并且您应该能够从其中 p/Invoke 函数——相同的模块名称不应再次重新加载。
不过,我认为在我的应用程序中,我实际上让 C++ DLL 将自身注册为 COM 服务器,然后通过生成的 .NET 包装器访问其功能 - 所以我不知道我是否直接测试过 p/Invoking。
不久前我也遇到过类似的场景。我使用的工具包在 64 位环境中表现不佳,并且我无法找到一种方法来动态强制程序集绑定为 32 位。
可以强制您的程序集在 32 位模式下工作,但这需要修补 CLR 标头(框架中有一个工具可以执行此操作),并且如果您的程序集是强命名的,则这不起作用。
恐怕您需要为 32 位和 64 位平台构建和发布两套二进制文件。