Могу ли я использовать две несовместимые версии одной и той же DLL в одном процессе? - PullRequest
11 голосов
/ 24 ноября 2008

Я использую две коммерческие библиотеки, выпущенные одним и тем же поставщиком, называемые VendorLibA и VendorLibB. Библиотеки распространяются в виде большого количества библиотек DLL, которые зависят от версии компилятора (например, VC7, VC8). Обе библиотеки зависят от другой библиотеки, созданной этим поставщиком, которая называется VendorLibUtils и содержится в одной DLL.

Проблема: VendorLibA использует версию VendorLibUtils, отличную от VendorLibB. Две версии не являются двоично-совместимыми, и даже если бы они были, было бы плохой идеей использовать неправильную версию.

Можно ли как-то использовать две библиотеки в одном процессе?

Примечание: LoadLibrary не может решить эту проблему, так как мой процесс не тот, который импортирует VendorLibUtils.

РЕДАКТИРОВАТЬ: Забыл упомянуть очевидное, мне не нужно исходный код для любой из коммерческих библиотек и, вероятно, у меня никогда не будет ( вздох ).

РЕДАКТИРОВАНИЕ: Альтернатива, кстати, заключается в следующем: Как объединить приложения с графическим интерфейсом в Windows

Ответы [ 7 ]

5 голосов
/ 25 ноября 2008

Я думаю, что ваш самый многообещающий вариант - громко жаловаться продавцу, который распространяет несовместимые продукты. Это скорее идет вразрез с идеей DLL.

Вы не можете просто поместить библиотеки DLL в разные каталоги. После загрузки DLL с заданным именем все другие попытки загрузить другую DLL с тем же именем модуля будут просто использовать уже загруженную, даже если пути разные.

Из этого можно сделать вывод, что для загрузки двух копий VendorLibUtils одна копия должна иметь другое имя. Вы не можете просто переименовать файл DLL; код в вашей программе не будет знать, чтобы искать другой файл. Поэтому, возможно, есть способ отредактировать таблицу импорта VendorLibB, чтобы заставить ее думать, что необходимые функции находятся в VendorLibUtilsB.dll, а не просто VendorLibUtils.dll. Боюсь, я не знаю ни одной утилиты, которая это сделает, но я почти не сомневаюсь, что это возможно.

4 голосов
/ 17 октября 2012

У меня была похожая проблема. В частности, я хотел использовать PyQt из интерпретатора Python, встроенного в приложение, использующее несовместимую версию Qt. В основном приложении использовались две библиотеки Qt DLL: QtCore.dll и QtGui.dll.

Когда я загружал PyQt из встроенного интерпретатора Python, я получал ошибку:

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

Это произошло на линии:

from PyQt4 import QtGui

Проблема заключается в том, что при загрузке несовместимого файла QtGui.dll в пространство процесса основного приложения любые ссылки на файл QtGui.dll (например, из файла QtGui.pyd) являются неправильными.

То, что случилось потом, я не горжусь.

Сначала я переименовал QtGui4.dll в дистрибутиве PyQt на QtGuiX.dll и затем переименовали QtCore4.dll в QtCoreX.dll. Обратите внимание, что переименование поддерживается с тем же количеством символов, это важно.

Далее я открыл файл QtGui.pyd в Notepad ++ и заменил все текстовые ссылки от QtGui4.dll до QtGuiX.dll и от QtCore4.dll до QtCoreX.dll. Я повторил процесс для файлов: QtCore.pyd, QtGuiX.dll и QtCoreX.dll.

Наконец я проверил, что мое тестовое приложение PyQt все еще работает. Это сделал! Затем я попытался запустить тестовое приложение PyQt из встроенного Интерпретатор Python, и это тоже сработало.

Так что, похоже, работает в паре тривиальных случаев. Я ожидаю, что я необходимо повторить процесс для всех библиотек DLL и PYD в PyQt распределение.

Вероятно, это неправильный способ сделать что-то, но я не могу придумать какие-либо конкретные причины, по которым он может взорваться (за исключением того, что я изменю длину имени файла).

Благодарю (или обвиняйте) других в цепочке за то, что вдохновили эту ужасную историю.

3 голосов
/ 24 ноября 2008

Поскольку вы не используете VendorLibUtils напрямую, я предполагаю, что вы не можете использовать LoadLibrary и т. Д.

Если DLL-библиотеки VendorLibUtils имеют экспорт только по порядковому номеру, вы, вероятно, могли бы переименовать одну из библиотек и исправить соответствующую VendorLib X , чтобы использовать другое имя файла для импорта.

Если в библиотеках VendorLibUtils есть один или несколько экспортируемых символов с одинаковыми именами, вам может также потребуется исправить таблицы импорта и экспорта, но будем надеяться, что нет! : -)

2 голосов
/ 25 ноября 2008

Как уже упоминалось, вы можете переименовать одну из копий VendorLibUtils и изменить таблицу импорта связанной DLL-библиотеки VendorLib для ссылки на нее, а не на VendorLibUtils.dll, с которой он был создан.

Существует несколько инструментов, позволяющих редактировать файлы EXE / DLL таким способом. CFF Explorer - довольно приличный, который позволяет редактировать таблицу импорта. Если вы откроете в нем VendorLib DLL и перейдете в раздел Import Directory (в дереве слева), вы увидите список модулей в верхней части главного окна. Вы можете переименовать модуль, дважды щелкнув его имя. Затем вы просто сохраняете библиотеку DLL, и теперь она должна использовать вашу переименованную библиотеку VendorLibUtils.

Конечно, это предполагает, что VendorLib использует таблицу импорта для доступа к VendorLibUtils, чего не может быть - он может использовать LoadLibrary / GetProcAddress, в этом случае вы не увидите запись таблицы импорта для VendorLibUtils.

На самом деле, если VendorLib действительно использует таблицу импорта, но также использует LoadLibrary для доступа к DLL-библиотеке VendorLibUtils в некоторых местах (я видел, что это было сделано), эти места все равно будут использовать неправильную , Если вы переименуете обе библиотеки, вы можете, по крайней мере, увидеть ошибку, если это так (поскольку DLL с оригинальным именем не будет существовать сейчас). Есть способ справиться с этим, если это произойдет, но в этот момент он становится довольно сложным, поэтому я не буду вдаваться в подробности, если вы действительно не хотите / не должны знать.

2 голосов
/ 24 ноября 2008

Я не эксперт в библиотеках 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"));

Будет ли это работать или нет, зависит от библиотеки, поэтому она может работать или не работать, но, насколько я могу судить, это единственная возможность.

1 голос
/ 03 декабря 2008

Вы имеете в виду, у вас есть ситуация, похожая на MSVCRT80.DLL и MSVCRT90.DLL? Есть веская причина, по которой Microsoft пронумеровала эти DLL. Если бы они оба назывались MSVCRT.DLL, только один из них был бы загружен в одном процессе.

0 голосов
/ 12 декабря 2011

На самом деле можно неявно загружать разные версии DLL в один процесс.

Это влечет за собой:

  1. Создание двух сборок, каждая из которых содержит версию библиотеки DLL, которая должна быть загружена несколько раз. Звучит сложно, но практически это влечет за собой немного больше, чем создание (2) именованных подпапок, каждая с файлом .manifest, содержащим некоторый xml, и собственной копией dll. Итак, VendorUtilsAssemblyV1 и VendorUtilsAssemblyV2

  2. Заставление каждой зависимой dll использовать механизм сборки для разрешения неявной зависимости - путем добавления директивы assemblyDependency, которая явно идентифицирует VendorUtilsAssemblyV1 или V2.

Есть несколько вариантов для пункта 2. Если файлы VendorLibA и VendorLibB не содержат своих собственных манифестов, то вы можете просто добавить файлы манифеста с необходимой директивой зависимой сборкой с именем VendorLibA.2.dll.manifest и VendorLibB.2.dll. .manifest. Если они уже содержат манифесты (возможно, для связи с C-Runtime VS2005 или VS2008), используйте инструмент MT.EXE для объединения в новую зависимость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...