目标 32 位或 64 位本机 DLL,具体取决于环境

aKz*_*enT 4 .net c# dll pinvoke

我有一个 32 位和 64 位版本 (x86) 的本机 DLL。我想创建一个适用于两种架构(任何 CPU)的包装器,并根据当前环境(32 位或 64 位,运行时!)加载正确版本的 DLL。这个过程应该自动发生,所以我的 DLL 的用户不需要针对特定​​的体系结构。

有没有关于如何做到这一点的最佳实践?任何可以指导我的例子?

我找到了一种可能的解决方案,它为每个架构使用托管代理,然后使用该Assembly.Resolve事件加载正确的版本。但是,这要求我除了 2 个非托管库之外还有 3 个托管程序集,这似乎有点过头了。

还有其他解决方案吗?

Dav*_*nan 7

我这样做的方法是在LoadLibrary对库调用任何 p/invoke 之前先 p/invoke 调用。

  • 使用执行程序集的位数来确定要加载的非托管 DLL 的版本。
  • 然后调用LoadLibrary加载它并传递 DLL 的完整路径。
  • 然后,当您调用 p/invokes 时,正确的 DLL 已加载到进程中,并且 p/invokes 绑定到它。

这依赖于 32 位和 64 位具有相同名称的非托管 DLL。如果情况并非如此,那么你就有麻烦了。在这种情况下,您可能需要通过 p/invoking 显式绑定到 DLL GetProcAddress。这一点也不好玩。或者你实现西蒙在他的回答中描述的那种脚手架。


Sim*_*ier 5

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

  • 使用“面向 32 位的名称”命名 32 位程序集。例如 MyAssembly.Native.x86.dll
  • 使用“面向 64 位的名称”命名 64 位程序集。例如 MyAssembly.Native.x64.dll
  • 将托管程序集编译为“任何 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);
Run Code Online (Sandbox Code Playgroud)

这是相应的“MyTest”函数,我将一直使用它(其他函数只是为了正确的位绑定)。它具有与其他 P/Invoke 相同的签名:

public static void MyTest(MyType myArg)
{
    if (IntPtr.Size == 8)
    {
        MyTest64(myArg);
        return;
    }

    MyTest86(myArg);
}
Run Code Online (Sandbox Code Playgroud)

优点是:

  • 您可以在同一路径中传送所有二进制文件(DLL、EXE、...)
  • 您支持具有相同文件布局的 32 位和 64 位进程和操作系统
  • 您不必求助于 Win32 api 来更改 dll 加载路径

不便之处在于:

  • 您将有 1 个“真实”方法的 3 个方法声明
  • 由于位测试,你会失去一些 CPU 周期
  • 根据您的上下文,有时您无法更改本机 DLL 名称,因此您无法执行此操作