如何在运行时后期绑定32bit / 64位库
我遇到了一个与此处描述的问题类似(但与之稍有不同)的问题(加载程序集及其依赖项)。
我有一个用于3D渲染的C ++ DLL,这是我们向客户销售的产品。对于.NET用户,我们将在其周围有一个CLR包装器。 C ++ DLL可以同时构建32位和64位版本,但是我认为这意味着我们需要两个CLR包装器,因为CLR绑定到特定的DLL吗?
现在说我们的客户有一个.NET应用程序,它可以是32位或者64位,并且它是一个纯净的.NET应用程序,它使CLR可以从一组程序集中进行处理。问题是应用程序代码如何在运行时动态地在32位和64位CLR / DLL组合之间进行选择?
更具体地说,对上述问题的建议答案是否也适用于此(即创建ResolveEvent处理程序)?
解决方案
回答
不久前,我遇到了类似的情况。我使用的工具包在64位环境中表现不佳,并且我无法找到一种方法来动态地强制程序集将其绑定为32位。
可以强制程序集以32位模式工作,但这需要修补CLR标头(在Framework中有一个工具可以对此进行修补),并且如果程序集是强命名的,则无法解决。
恐怕我们需要为32位和64位平台构建和发布两组二进制文件。
回答
我大约一年前就可以做到这一点,但我不再记得所有细节。基本上,可以使用IntPtr.Size确定要加载的DLL,然后通过p / Invoke执行实际的LoadLibrary。到那时,我们已经将该模块存储在内存中,并且应该应该只能从其内部p / Invoke函数-相同的模块名称不应再次重新加载。
不过,我认为,在我的应用程序中,我实际上已经将C ++ DLL自身注册为COM服务器,然后通过生成的.NET包装器访问其功能-因此,我不知道是否直接测试了p / Invoking。
回答
我终于有了一个似乎可行的答案。
将托管和非托管的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时,我会为他分配答案