Могу ли я использовать две несовместимые версии одной и той же библиотеки DLL в одном и том же процессе?

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

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

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

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

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

EDIT: Альтернативный вариант: это Как объединить приложения GUI в Windows

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

У меня была аналогичная проблема. В частности, я хотел использовать 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 распределение.

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

Кредит (или обвинение) другим в потоке, чтобы вдохновить эту страшную историю.

Ответ 4

Я не эксперт в 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"));

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

Фактически можно неявно загружать разные версии dll в один процесс.

Выполнение этого влечет за собой:

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

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

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