【问题标题】:Can I use two incompatible versions of the same DLL in the same process?我可以在同一个进程中使用同一个 DLL 的两个不兼容版本吗?
【发布时间】:2010-09-23 20:06:01
【问题描述】:

我正在使用由同一供应商生产的两个商业库,称为 VendorLibA 和 VendorLibB。这些库分发的 DLL 取决于编译器版本(例如 VC7、VC8)。这两个库都依赖于由该供应商生产的另一个库,称为 VendorLibUtils 并包含在一个 DLL 中。

问题:VendorLibA 使用的 VendorLibUtils 版本与 VendorLibB 不同。这两个版本不是二进制兼容的,即使它们是,使用错误的版本也是一个坏主意。

有什么办法可以在同一个进程下使用这两个库?

注意:LoadLibrary 无法解决这个问题,因为我的进程不是导入 VendorLibUtils 的进程。

编辑:忘了提一个明显的问题,我不需要为任何商业库提供源代码,而且我可能永远也不会拥有(sigh)。

编辑: 顺便说一句,这样做是:How to combine GUI applications in Windows

【问题讨论】:

    标签: c++ windows winapi dll


    【解决方案1】:

    我遇到了类似的问题。具体来说,我想使用嵌入在使用不兼容版本 Qt 的应用程序中的 Python 解释器中的 PyQt。主应用程序使用了两个 Qt DLL:QtCore.dll 和 QtGui.dll。

    当我从嵌入式 Python 解释器加载 PyQt 时,我会收到一个错误:

    ImportError: DLL load failed: The specified procedure could not be found.
    

    这行发生了:

    from PyQt4 import QtGui
    

    问题在于,一旦将不兼容的 QtGui.dll 加载到主应用程序的进程空间中,对 QtGui.dll 的任何引用(例如来自文件 QtGui.pyd)都是不正确的。

    接下来发生的事情,我并不骄傲。

    首先我将 PyQt 发行版中的 QtGui4.dll 重命名为 QtGuiX.dll 然后将QtCore4.dll 重命名为QtCoreX.dll。请注意, 重命名保持相同的字符数,这一点很重要。

    接下来我在 Notepad++ 中打开文件QtGui.pyd,并替换了所有 QtGui4.dllQtGuiX.dll 和来自的纯文本引用 QtCore4.dllQtCoreX.dll。我重复了文件的过程: QtCore.pydQtGuiX.dllQtCoreX.dll

    最后我检查了我的 PyQt 测试应用程序是否仍然有效。它做了! 然后我尝试从嵌入式运行 PyQt 测试应用程序 Python 解释器,它也能正常工作。

    所以,它似乎适用于几个微不足道的情况。我希望我 需要对 PyQt 中的所有 DLL 和 PYD 重复该过程 分配。

    这可能不是正确的做事方式,但我想不出它会爆炸的任何具体原因(除非我更改文件名的长度)。

    感谢(或责备)线程上的其他人启发了这个可怕的故事。

    【讨论】:

    • 您好,这正是我要解决的问题。我们有一个大型 C++ 应用程序,它嵌入了一个 Python 解释器(通过 Python DLL)来支持 Python 插件。我们使用 Boost.Python 向 Python 插件公开 C++ API,并使用 PyQt 允许插件访问我们的用户界面。然而,我们发布了我们自己的 Qt DLL,所以 PyQt 需要使用它们而不是它自己的。您的解决方案是公认的做事方式吗? :) 你知道任何更“官方”的方式吗?您还没有在这里分享但值得了解的任何细节?
    • @FrançoisBeaune 抱歉,我不知道有什么更好的方法。
    【解决方案2】:

    实际上可以将不同版本的 dll 隐式加载到单个进程中。

    这样做需要:

    1. 创建两个程序集,每个程序集都包含一个必须多次加载的 dll 版本。听起来很复杂,但实际上它只需要创建 (2) 个命名的子文件夹,每个文件夹都有一个包含一些 xml 的 .manifest 文件和它自己的 dll 副本。所以,VendorUtilsAssemblyV1 和 VendorUtilsAssemblyV2

    2. 通过添加显式标识 VendorUtilsAssemblyV1 或 V2 的 assemblyDependency 指令,使每个依赖 dll 使用组装机制来解决隐式依赖关系。

    第 2 点有一些选项。如果 VendorLibA 和 VendorLibB 文件不包含它们自己的清单,那么您可以简单地使用名为 VendorLibA.2.dll.manifest 和 VendorLibB.2.dll 的所需依赖Assembly 指令添加清单文件。显现。 如果它们已经包含清单(可能链接到 VS2005 或 VS2008 C-Runtime),则使用 MT.EXE 工具合并新的依赖项。

    【讨论】:

      【解决方案3】:

      您的意思是,您的情况类似于 MSVCRT80.DLL 和 MSVCRT90.DLL ? Microsoft 对这些 DLL 进行编号是有充分理由的。如果两者都称为 MSVCRT.DLL,则只有其中一个会在单个进程中加载​​。

      【讨论】:

      • 我遇到的情况是两个 DLL 都称为 MSVCRT.DLL,但内容不同。
      【解决方案4】:

      正如其他人提到的,您可以重命名 VendorLibUtils 的副本之一,并修改关联的 VendorLib DLL 的导入表以链接到它,而不是创建它时使用的 VendorLibUtils.dll。

      有一些工具可以让您以这种方式编辑 EXE/DLL 文件。 CFF Explorer 是一个相当不错的工具,它允许编辑导入表。如果您打开其中的 VendorLib DLL 并转到 Import Directory 部分(在左侧的树中),您将在主窗口的顶部看到一个模块列表。您可以通过双击其名称来重命名模块。然后您只需保存 DLL,它现在应该使用您重命名的 VendorLibUtils DLL。

      当然,这假设 VendorLib 使用导入表来访问 VendorLibUtils,但它可能不会使用 LoadLibrary/GetProcAddress,在这种情况下您不会看到 VendorLibUtils 的导入表条目。

      实际上,如果 VendorLib 确实使用了导入表但在某些地方使用了LoadLibrary 来访问 VendorLibUtils DLL(我已经看到它完成了),那些地方仍然会使用错误一。如果重命名这两个库,在这种情况下,您至少可能会看到一个错误(因为现在不存在具有原始名称的 DLL)。如果发生这种情况,有一种方法可以解决,但此时它开始变得相当复杂,所以除非你真的想/需要知道,否则我不会详细说明。

      【讨论】:

        【解决方案5】:

        我不是 DLL 专家,但我认为可能的唯一方法是使用 LoadLibrary() 并显式加载 DLL。然后您可以使用GetProcAddress() 将函数/类等放置在单独的命名空间中。

        HMODULE v1 = LoadLibrary(_T("libv1_0.dll"));
        libv1_0::fun_in_lib = reinterpret_cast<FUNTYPE>(GetProcAddress(v1, _T("fun_in_lib"));
        

        HMODULE v2 = LoadLibrary(_T("libv2_0.dll"));
        libv2_0::fun_in_lib = reinterpret_cast<FUNTYPE>(GetProcAddress(v2, _T("fun_in_lib"));
        

        这是否可行取决于图书馆,所以它可能会或可能不会起作用,但据我所知,这是唯一的可能性。

        【讨论】:

          【解决方案6】:

          我认为您最有希望的选择是大声向销售互不兼容产品的供应商投诉。这与 DLL 的想法相反。

          您不能只是将 DLL 放在不同的目录中。加载具有给定名称的 DLL 后,所有其他尝试加载具有相同模块名称的另一个 DLL 将仅使用已加载的那个,即使路径不同。

          由此,我们可以得出结论,要加载 VendorLibUtils 的两个副本,一个副本需要具有不同的名称。您不能只重命名 DLL 文件;你程序中的代码不会知道去寻找不同的文件。因此,也许有一种方法可以编辑 VendorLibB 的导入表,使其认为它需要的函数在 VendorLibUtilsB.dll 中,而不仅仅是 VendorLibUtils.dll。恐怕我不知道有什么实用程序可以做到这一点,但我毫不怀疑它是可能的。

          【讨论】:

            【解决方案7】:

            由于您没有直接使用 VendorLibUtils,我假设您不能使用 LoadLibrary 等。

            如果 VendorLibUtils DLL 仅按序号导出,您可能可以重命名其中一个库并修补相应的 VendorLibX 以对其导入使用不同的文件名。

            如果 VendorLibUtils DLL 有一个或多个同名的导出符号,您可能也需要修补导入和导出表,但希望不要! :-)

            【讨论】:

            • 按名称导出为:/。有任何谷歌搜索词吗?
            • 制作一个测试应用程序和四个重现此场景的 DLL,看看您是否可以在两个“TestLibUtils”DLL 中具有相同的符号名称,以及 TestLibA 和 TestLibB 是否运行正确的函数。跨度>
            猜你喜欢
            • 1970-01-01
            • 2017-08-30
            • 2011-05-22
            • 2011-06-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多