【问题标题】:Using pinvoke for both 32 and 64 bit dlls from the same managed dll对来自同一个托管 dll 的 32 位和 64 位 dll 使用 pinvoke
【发布时间】:2013-03-04 09:59:58
【问题描述】:

我正在使用 .NET (C#) dll 中的 pinvoke 调用本机 dll。现在我想将我的 .NET dll 编译为“AnyCPU”,但是当“pinvoking”时,我必须知道我是否必须调用 32 位或 64 位 dll。我将本机 dll 的两个版本都安装到子文件夹 bin32 和 bin64 中。现在,当我的 DLL 被加载时,我想检查一下我们是处于 32 位模式还是 64 位模式,并使用适当的路径调用 SetDllDirectory。这种方法似乎没有问题,除了从哪里调用 SetDllDirectory 的一个很好的“入口点”。在本机 DLL 中有一个 DllMain 条目,当 DLL 附加到进程时会调用该条目。 .NET DLL 中是否有类似的入口点? 你觉得动态调用 SetDllDirectory 是个好主意吗?

【问题讨论】:

    标签: .net pinvoke


    【解决方案1】:

    .NET 确实有一个等效的 DllMain() 函数,它被称为 module initializer。然而,C# 和 VB.NET 代码无法实现它,您只能在 IL 或 C++/CLI 中创建一个。 C++/CLI 本身具有位依赖性,因此只留下 IL。您可以在this answer 中找到其中之一的示例代码。将其链接到您的程序集非常尴尬,构建系统不直接支持运行程序集链接器。

    下一个最好的事情是同一篇文章中提到的“类型初始化器”,在 C# 中称为静态构造函数。你确实需要在你的代码中进行一些组织来使这些得到回报,一个保证在你的“数千个方法”之一被调用之前被使用的类。用这么多方法应该很难。

    除了必须由应用程序在其 Main() 方法中调用的初始化方法之外,并没有留下太多东西。当然还有标准解决方案,两个安装程序,一个用于 32 位机器,另一个用于 64 位机器。这也确保您的应用程序最终位于“正确”目录中,即 c:\program files 与 c:\program files (x86)。

    更新:.NET 5 现在在 C# v9 中使用 [ModuleInitializer] attribute 支持模块初始化器

    【讨论】:

    • 我认为这对你来说是一个很好的解决方案msdn.microsoft.com/en-us/library/windows/desktop/… 至少在 Windows 上。我不得不对注册表做一些魔术来解决我的问题,所以我使用了 Module Initilazer aprocah。如果您将托管对象的创建包装在您将要进行初始化的类工厂中,或者尝试将其放入您保留 pinvoke 定义的静态类(构造函数)中,这也可以避免。
    【解决方案2】:

    本质上,您要问的是您是否可以编写将在首次加载程序集时执行的代码。这个问题在这里解决:.Net: Running code when assembly is loaded

    在你的立场上,我会把责任推给你图书馆的用户。提供一个初始化库的函数,并要求库的用户在任何其他函数之前调用它。

    如果您愿意,您可以使用延迟初始化在您的库中进行该初始化。因此,您的所有方法都可能如下所示:

    private static void EnsureInitialized()
    {
        if (!MyLibraryInitialized)
            InitializeMyLibrary();
    }
    
    public static void DoSomething()
    {
        EnsureInitialized();
        .... // implementation of DoSomething
    }
    

    我也建议不要使用SetDllDirectory。有一种更简单的方法可以做到这一点。由于您可以获得需要加载的 DLL 的完整路径,只需调用 LoadLibrary() 将其加载到 InitializeMyLibrary()。加载 DLL 后,您的 p/invokes 将自动绑定到您已加载的 DLL。

    【讨论】:

    • 谢谢,在许多情况下,LoadLibrary 肯定比 SetDllDirectory 好,但不幸的是,在我的情况下,可能会加载数十个 DLL,具体取决于用户调用的方法。如果不需要,我不想消耗这个内存空间。
    • @Gerhard 我不建议在启动时将它们全部加载。只需加载所需的内容。但是,如果这些 DLL 相互之间存在依赖关系,则需要修改 DLL 搜索路径,以便正确解析依赖关系。
    • 您的回答并没有明确说明托管 DLL 中没有“DllMain”,您提供了一个很好的解决方法。不幸的是,我的 DLL 中有数千种方法,所以我不能使用“初始化”预检查。如果没有人在托管代码中提出 DllMain,我会将您的答案作为已接受的答案进行检查。再次感谢。
    • 如果你不能使用延迟初始化,那么你可以要求用户调用你的初始化函数。但是,我不相信您的库中真的有成千上万的 pinvoke 调用。
    猜你喜欢
    • 2014-11-06
    • 1970-01-01
    • 2011-01-05
    • 2015-01-07
    • 2020-08-30
    • 2017-04-01
    • 2010-11-03
    • 2012-02-29
    相关资源
    最近更新 更多