我使用两个由同一供应商生产的商业库,称为VendorLibA和VendorLibB。 这些库分发尽可能多的依赖于编译器版本的DLL(例如VC7,VC8)。 两个库都依赖于另一个由该供应商生产的库,名为VendorLibUtils,并包含在一个DLL中。
问题:VendorLibA使用与VendorLibB不同版本的VendorLibUtils。 这两个版本不是二进制兼容的,即使是这样,使用错误的版本也是一个坏主意。
有什么办法可以在同一个进程下使用这两个库吗?
注意: LoadLibrary无法解决这个问题,因为我的stream程并不是导入VendorLibUtils的stream程。
编辑:忘了提起明显的,我没有任何商业图书馆的源代码,可能我永远不会( 叹气 )。
编辑:另一个顺便说一句,是这样做的: 如何在Windows中结合GUI应用程序
由于您不直接使用VendorLibUtils,我假设您不能使用LoadLibrary等。
如果VendorLibUtils DLL仅按顺序导出,则可以重命名其中一个库并修补相应的VendorLib X ,以便为其导入使用不同的文件名。
如果VendorLibUtils DLL具有一个或多个具有相同名称的导出符号,则可能还需要修补导入和导出表,但是不要希望! 🙂
我认为你最有希望的选择是大声抱怨分销互不相容产品的供应商。 这反而违背了DLL的想法。
你不能把这些DLL放在不同的目录下。 一旦具有给定名称的DLL被加载,所有其他尝试加载具有相同模块名称的另一个DLL将简单地使用已加载的DLL,即使路径不同。
从这一点,我们可以得出结论,要加载VendorLibUtils的两个副本,一个副本需要有一个不同的名称。 你不能只重命名DLL文件; 您的程序中的代码将不知道要查找不同的文件。 因此,也许有办法编辑VendorLibB的导入表,使其认为它需要的功能在VendorLibUtilsB.dll而不是VendorLibUtils.dll。 恐怕我不知道会有这样做的效用,但我毫不怀疑这是可以做到的。
我有一个类似的问题。 具体而言,我想从嵌入在使用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发行版中的
QtGuiX.dll
重命名为QtGuiX.dll
,然后将QtCore4.dll
重命名为QtCoreX.dll
。 请注意,重命名保持相同数量的字符,这很重要。接下来,我在Notepad ++中打开了
QtGui.pyd
文件,QtGui.pyd
所有纯文本引用QtGui4.dll
为QtGuiX.dll
并将其从QtCore4.dll
为QtCoreX.dll
。 我重复了文件的过程:QtCore.pyd
,QtGuiX.dll
和QtCoreX.dll
。最后,我检查了我的PyQt测试应用程序仍然工作。 它做了! 然后,我尝试从嵌入式Python解释器运行PyQt测试应用程序,并且工作得很好。
所以,这似乎适用于一些微不足道的情况。 我期望我需要重复PyQt分发中的所有DLL和PYD的过程。
这可能不是正确的做法,但我想不出有什么具体的原因可能会炸毁(除了如果我改变文件名的长度)。
信任(或责备)给其他人的线索来鼓舞这个可怕的故事。
我不是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"));
不管这个工作是否有效还是取决于图书馆,所以它可能工作也可能不工作,但据我所知,这是唯一的可能性。
正如别人提到的,您可以重命名VendorLibUtils的一个副本,并修改关联的VendorLib DLL的导入表以链接到它,而不是它创建的VendorLibUtils.dll。
有几个工具可以让你用这种方式编辑EXE / DLL文件。 CFF资源管理器是一个相当不错的,允许编辑导入表。 如果打开VendorLib DLL并转到导入目录部分(在左边的树中),你会看到主窗口顶部的模块列表。 您可以通过双击其名称来重命名该模块。 然后,您只需保存DLL,它现在应该使用您的重命名的VendorLibUtils DLL。
当然,这里假设VendorLib使用导入表访问VendorLibUtils,它可能不使用LoadLibrary
/ GetProcAddress
,在这种情况下,您将看不到VendorLibUtils的导入表项。
实际上,如果VendorLib确实使用了导入表,而且在某些地方使用LoadLibrary
来访问VendorLibUtils DLL(我已经看到了),那么这些地方仍然会使用错误的地方。 如果您重命名这两个库,则至少可以看到一个错误(因为具有原始名称的DLL现在不存在)。 如果发生这种情况,有办法解决这个问题,但是现在开始变得相当复杂,所以除非你真的想/需要知道,否则我不会详细说明。
你的意思是,你有类似于MSVCRT80.DLL和MSVCRT90.DLL的情况? 微软对这些DLL进行编号有一个很好的理由。如果两者都被称为MSVCRT.DLL,则只有其中一个会被加载到一个进程中。
实际上可以隐式地将不同版本的dll加载到单个进程中。
这样做需要:
创建两个程序集,每个包含必须多次加载的dll版本。 听起来很复杂,但实际上它只需要创建(2)命名的子文件夹,每个文件夹都包含一个包含一些xml的.manifest文件,以及它自己的dll副本。 那么,VendorUtilsAssemblyV1和VendorUtilsAssemblyV2
使每个依赖dll使用汇编机制来解决隐式依赖 – 通过添加显式标识VendorUtilsAssemblyV1或V2的assemblyDependency指令。
点2有一些选项。如果VendorLibA和VendorLibB文件不包含自己的清单,那么您可以简单地添加名为VendorLibA.2.dll.manifest和VendorLibB.2.dll.manifest的必需dependentAssembly指令的清单文件。 如果他们已经包含清单(可能链接到VS2005或VS2008 C运行时),然后使用MT.EXE工具合并到新的依赖项中。