English 中文(简体)
我可以在同一进程中使用两个不兼容版本的相同DLL吗?
原标题:
  • 时间:2008-11-24 20:09:02
  •  标签:

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

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

我是否能在同一进程下使用两个库?

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

编辑: 忘记提到一个显而易见的问题,我没有任何商业库的源代码,也许我永远都不会有。(叹气)

编辑:另一种选择是这样做:如何在Windows中组合GUI应用程序

最佳回答

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

如果VendorLibUtils DLL只有按序号导出,您可以重命名其中一个库并修补相应的VendorLib X,以便其使用不同的文件名进行导入。

如果VendorLibUtils 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文件)都是不正确的。

接下来发生的事情,我并不以此为傲。

First I renamed QtGui4.dll in the PyQt distribution to QtGuiX.dll and then renamed the QtCore4.dll to QtCoreX.dll. Notice that the renaming maintained the same number of characters, this is important.

Next I opened the file QtGui.pyd in Notepad++, and replaced all plain-text references of QtGui4.dll to QtGuiX.dll and from QtCore4.dll to QtCoreX.dll. I repeated the process for the files: QtCore.pyd, QtGuiX.dll and QtCoreX.dll.

Finally I checked that my PyQt test application still worked. It did! Then I tried running the PyQt test application from the embedded Python interpreter, and it worked as well.

So, it seems to works in a couple of trivial cases. I expect that I need to repeat the process for all DLLs and PYDs in the PyQt distribution.

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

将这归功于论坛中的其他人鼓舞了这个可怕的故事。

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

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

当然,这基于VendorLib使用导入表来访问VendorLibUtils,但也可能不是这样-它可能使用LoadLibrary/GetProcAddress,那么您就看不到VendorLibUtils的导入表项。

实际上,如果VendorLib确实使用导入表但在某些地方使用LoadLibrary访问VendorLibUtils DLL(我见过它的实现),那么这些地方仍然会使用错误的库。如果你重命名这两个库,如果情况是这样的,你至少会看到一个错误(因为原始名称的DLL现在不存在)。如果出现这种情况,有一种处理方法,但在这一点上,它开始变得相当复杂,所以除非你真正想要/需要知道,否则我不会进行详细说明。

我不是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"));

and: 和

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

这取决于图书馆,所以它可能会或可能不会工作,但就我所知,这是唯一的可能性。

你的意思是,你有一个类似MSVCRT80.DLL和MSVCRT90.DLL的情况吗?微软对这些DLL进行编号有很好的原因。如果两个都被称为MSVCRT.DLL,那么在单个进程中只会加载其中一个。

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

这样做包括:

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

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

There are some options for point 2. If the VendorLibA and VendorLibB files do not contain their own manifests, then you can simply add manifest files with the required dependentAssembly directive named VendorLibA.2.dll.manifest and VendorLibB.2.dll.manifest. If they already contain manifests (probably to link to the VS2005 or VS2008 C-Runtime) then use the MT.EXE tool to merge in the new dependency.





相关问题
热门标签