我正在使用两个由同一供应商生产的商业库,名为VendorLibA和VendorLibB。这些库分布有许多依赖于编译器版本的DLL(例如VC7,VC8)。这两个库都依赖于另一个由该供应商生成的库,名为VendorLibUtils,并包含在一个DLL中。
问题:VendorLibA使用与VendorLibB不同的VendorLibUtils版本。这两个版本不是二进制兼容的,即使它们使用错误的版本也是一个坏主意。
有没有办法在同一个过程中使用这两个库?
注意: LoadLibrary无法解决此问题,因为我的流程不是导入VendorLibUtils的流程。
编辑:忘了提及显而易见的,我没有任何商业图书馆的源代码,也许我永远不会(叹息)。< / p>
编辑:替代顺便说一句,就是这样做:How to combine GUI applications in Windows
答案 0 :(得分:5)
我认为您最有希望的选择是大声抱怨分发互不兼容产品的供应商。这反而违背了DLL的想法。
您不能只将DLL放在不同的目录中。一旦加载了具有给定名称的DLL,所有其他尝试加载具有相同模块名称的另一个DLL将只使用已加载的DLL,即使路径不同。
由此,我们可以得出结论,要加载两个VendorLibUtils副本,一个副本需要具有不同的名称。你不能只重命名DLL文件;程序中的代码不知道要查找不同的文件。因此,也许有一种方法可以编辑VendorLibB的导入表,使其认为所需的功能在VendorLibUtilsB.dll而不仅仅是VendorLibUtils.dll中。我担心我不知道会有什么用处,但我毫不怀疑它有可能做到。
答案 1 :(得分:4)
我有类似的问题。具体来说,我想使用嵌入在使用不兼容的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.dll
到QtGuiX.dll
的纯文本引用QtCore4.dll
到QtCoreX.dll
。我重复了文件的过程:QtCore.pyd
,QtGuiX.dll
和QtCoreX.dll
。最后,我检查了我的PyQt测试应用程序是否仍然有用。它做了! 然后我尝试从嵌入式运行PyQt测试应用程序 Python解释器,它也可以工作。
因此,它似乎适用于几个微不足道的案例。我希望我 需要在PyQt中重复所有DLL和PYD的过程 分配。
这可能不是正确的做事方式,但我想不出任何具体的原因它会如何爆炸(除非我改变文件名的长度)。
对线上其他人的信任(或责备)鼓励这个可怕的故事。
答案 2 :(得分:3)
由于您没有直接使用VendorLibUtils,我认为您无法使用LoadLibrary等。
如果VendorLibUtils DLL仅按顺序导出,则可以重命名其中一个库并修补相应的VendorLib X ,以便为其导入使用不同的文件名。
如果VendorLibUtils DLL具有一个或多个具有相同名称的导出符号,则可能也需要修补导入和导出表,但我们希望不要! : - )
答案 3 :(得分:2)
我不是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"));
这是否有效仍然取决于图书馆,所以它可能会或可能不会起作用,但据我所知,这是唯一的可能性。
答案 4 :(得分:2)
正如其他人提到的那样,您可以重命名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 :(得分:1)
你的意思是,你的情况类似于MSVCRT80.DLL和MSVCRT90.DLL?微软对这些DLL进行编号是有充分理由的。如果两者都被称为MSVCRT.DLL,则只能在一个进程中加载其中一个。
答案 6 :(得分:0)
实际上可以隐式地将不同版本的dll加载到单个进程中。
这样做需要:
创建两个程序集,每个程序集包含必须多次加载的dll版本。听起来很复杂,但实际上它只需要创建(2)命名的子文件夹,每个子文件夹都包含一个包含一些xml的.manifest文件,以及它自己的dll副本。因此,VendorUtilsAssemblyV1和VendorUtilsAssemblyV2
使每个依赖dll使用汇编机制来解析隐式依赖 - 通过添加一个显式标识VendorUtilsAssemblyV1或V2的assemblyDependency指令。
点2有一些选项。如果VendorLibA和VendorLibB文件不包含自己的清单,那么您只需添加名为VendorLibA.2.dll.manifest和VendorLibB.2.dll的所需dependentAssembly指令的清单文件。 。表现。 如果它们已经包含清单(可能链接到VS2005或VS2008 C-Runtime),那么使用MT.EXE工具合并新的依赖关系。