目标32位或64位本机DLL,具体取决于环境
本文关键字:取决于 环境 DLL 32位 64位 本机 目标 | 更新日期: 2023-09-27 17:59:15
我有一个本地DLL,它有32位和64位版本(x86)。我想创建一个包装器,它可以在两种体系结构(任何CPU)上工作,并根据当前环境加载正确版本的DLL(运行时为32位或64位!)。这个过程应该自动发生,这样我的DLL的用户就不需要针对特定的体系结构。
关于如何做到这一点,有什么最佳实践吗?有什么例子可以指导我吗?
我找到了一个可能的解决方案,它为每个体系结构使用托管代理,然后使用Assembly.Resolve
事件来加载正确的版本。然而,这需要我除了2个非托管库之外还有3个托管程序集,这似乎有点过头了。
还有其他解决方案吗?
以下是我在许多项目中使用的解决方案:
- 使用"面向32位的名称"命名32位程序集。对于示例MyAssembly.Native.x86.dll
- 使用"面向64位的名称"命名64位程序集。例如MyAssembly.Native.x64.dll
- 将托管程序集编译为"Any Cpu"
- 以相同的路径运送所有东西
以下是我如何声明p/Invoke方法:
[DllImport("MyAssembly.Native.x86.dll", EntryPoint = "MyTest")]
private static extern void MyTest86(MyType myArg);
[DllImport("MyAssembly.Native.x64.dll", EntryPoint = "MyTest")]
private static extern void MyTest64(MyType myArg);
这里是相应的"MyTest"函数,这是我将一直使用的函数(其他函数只是用于正确的bitness绑定)。它与其他P/Invoke签名相同:
public static void MyTest(MyType myArg)
{
if (IntPtr.Size == 8)
{
MyTest64(myArg);
return;
}
MyTest86(myArg);
}
优点是:
- 可以在同一路径中运送所有二进制文件(DLL、EXE…)
- 支持具有相同文件布局的32位和64位进程以及操作系统
- 您不必使用Win32 api来更改dll加载路径
不便之处在于:
- 对于1个"real"方法,将有3个方法声明
- 由于bitness测试,您将失去一些CPU周期
- 根据上下文的不同,有时无法更改本机DLL的名称,因此无法执行此操作
我这样做的方法是在调用库的任何p/invokes之前p/invoke对LoadLibrary
的调用。
- 使用执行程序集的位来确定要加载非托管DLL的哪个版本
- 然后调用
LoadLibrary
来加载它,将完整路径传递到DLL - 然后,当您调用p/invokes时,正确的DLL已经加载到进程中,p/invokers绑定到它
这依赖于32位和64位都具有相同名称的非托管DLL。如果不是这样,那么你就有麻烦了。在这种情况下,您可能需要通过p/调用GetProcAddress
来显式绑定到DLL。这一点都不好玩。或者你实现了西蒙在回答中描述的那种脚手架。
看看Microsoft.WinAny.Helper和It'aDynamicNativeLibrary类,它们可以帮助您获得所需内容。