目标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位或64位本机DLL,具体取决于环境

以下是我在许多项目中使用的解决方案:

  • 使用"面向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类,它们可以帮助您获得所需内容。