【问题标题】:Target 32 Bit or 64 Bit native DLL depending on environment目标 32 位或 64 位本机 DLL,具体取决于环境
【发布时间】:2014-06-06 14:05:50
【问题描述】:

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

有没有关于如何做到这一点的最佳做法?有什么可以指导我的例子吗?

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

还有其他解决办法吗?

【问题讨论】:

标签: c# .net dll pinvoke


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    看看 Microsoft.WinAny.Helper 和它是一个 DynamicNativeLibrary 类,它可以帮助您满足您的需要。

    【讨论】:

    • 这个项目看起来很有趣。尤其是它能够从字节数组加载库,这使您可以将本机 DLL 作为资源嵌入。虽然查看代码,但对于我的口味来说,它是太多不安全/本机代码,而且似乎没有得到维护。西蒙斯的解决方案目前似乎更简单。不过谢谢!
    • 该库的名称非常具有误导性。它不是微软开发的!事实上它是由@HABJAN 开发的,应该在这个答案中披露。
    【解决方案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”函数,这是我将一直使用的函数(其他的只是为了正确的位数绑定)。它与其他 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 个“真实”方法,您将拥有 3 个方法声明
    • 由于位数测试,您会丢失一些 CPU 周期
    • 根据您的上下文,有时您无法更改本机 DLL 名称,因此您不能这样做

    【讨论】:

    • 目前似乎是最简单的方法。我将选择 Environment.Is64BitProcess 而不是 IntPtr.Size == 8,这使意图更加清晰。谢谢!
    • @aKzenT - 是的,为什么不呢,我更喜欢我的代码,因为 Is64BitProcess 只存在于 .NET Framework 4.0 :-)
    • 如果 DLL 有不同的名称,这可能是最好的。但是,如果您可以将 DLL 放在具有相同文件名的 DLL 名称的不同目录中,那么这比需要的要复杂。
    • @DavidHeffernan - 这是有争议的。从部署/调试的角度来看,我发现在没有子目录的情况下将所有内容部署在一个地方要容易得多,而且只需查看其名称就更容易识别本机 DLL 位数(不可能出现 BadImageFormatException)
    • 好吧,这取决于。在我看来,这两种方式都可以。你的方法有很多样板。它可能超出您的控制范围。无论如何,如果您确实希望将所有内容都放在一个文件夹中,那么您确实会采用您的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    • 2019-03-28
    相关资源
    最近更新 更多