Я использую две коммерческие библиотеки, созданные одним и тем же поставщиком, которые называются VendorLibA и VendorLibB. Библиотеки распространяются по количеству DLL в зависимости от версии компилятора (например, VC7, VC8). Обе библиотеки зависят от другой библиотеки, созданной этим поставщиком, которая называется VendorLibUtils и содержится в одной DLL.
Проблема: VendorLibA использует другую версию VendorLibUtils, чем VendorLibB. Две версии несовместимы на двоичном уровне, и даже если бы это было так, было бы плохой идеей использовать неправильную версию.
Могу ли я использовать две библиотеки в одном процессе?
Примечание: LoadLibrary не может решить эту проблему, поскольку мой процесс не импортирует VendorLibUtils.
Обновлено: Забыл упомянуть очевидное: мне не нужно исходный код для какой-либо из коммерческих библиотек и, вероятно, у меня никогда не будет (вздох).
Обновлено: Альтернативный вариант, кстати, сделать это: Как объединить приложения с графическим интерфейсом в Windows





Я не эксперт в библиотеках 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 напрямую, я предполагаю, что вы не можете использовать LoadLibrary и т. д.
Если библиотеки DLL VendorLibUtils экспортируются только по порядковому номеру, вы, вероятно, можете переименовать одну из библиотек и исправить соответствующую VendorLibИкс, чтобы использовать другое имя файла для ее импорта.
Если библиотеки DLL VendorLibUtils содержат один или несколько экспортируемых символов с одинаковыми именами, вам мощь также необходимо исправить таблицы импорта и экспорта, но будем надеяться, что нет! :-)
Создайте тестовое приложение и четыре библиотеки DLL, воспроизводящие этот сценарий, чтобы убедиться, что у вас может быть одно и то же имя символа в двух библиотеках DLL TestLibUtils и что TestLibA и TestLibB запускают правильную функцию.
Я думаю, что ваш самый многообещающий вариант - громко пожаловаться поставщику, который распространяет несовместимые продукты. Это скорее противоречит идее DLL.
Вы не можете просто поместить библиотеки DLL в разные каталоги. После загрузки библиотеки DLL с заданным именем все остальные попытки загрузить другую DLL с тем же именем модуля будут просто использовать уже загруженную, даже если пути будут другими.
Из этого можно сделать вывод, что для загрузки двух копий VendorLibUtils одна копия должна иметь другое имя. Вы не можете просто переименовать файл DLL; код в вашей программе не будет искать другой файл. Поэтому, возможно, есть способ отредактировать таблицу импорта VendorLibB, чтобы заставить ее думать, что нужные функции находятся в VendorLibUtilsB.dll, а не только в VendorLibUtils.dll. Боюсь, я не знаю ни одной утилиты, которая бы это сделала, но не сомневаюсь, что это возможно.
Как уже упоминалось, вы можете переименовать одну из копий VendorLibUtils и изменить таблицу импорта связанной DLL VendorLib, чтобы связать с ней, а не с VendorLibUtils.dll, с которой она была создана.
Есть несколько инструментов, которые позволяют редактировать файлы EXE / DLL таким образом. CFF Explorer - довольно приличный, позволяющий редактировать таблицу импорта. Если вы откроете в ней DLL VendorLib и перейдете в раздел Import Directory (в дереве слева), вы увидите список модулей в верхней части главного окна. Вы можете переименовать модуль, дважды щелкнув его имя. Затем вы просто сохраняете DLL, и теперь она должна использовать вашу переименованную DLL VendorLibUtils.
Конечно, это предполагает, что VendorLib использует таблицу импорта для доступа к VendorLibUtils, чего не может быть - он может использовать LoadLibrary / GetProcAddress, и в этом случае вы не увидите запись таблицы импорта для VendorLibUtils.
На самом деле, если VendorLib использует таблицу импорта, но также использует LoadLibrary для доступа к VendorLibUtils DLL в некоторых местах (я видел, как это было сделано), эти места все равно будут использовать неправильную. Если вы переименуете обе библиотеки, вы, по крайней мере, можете увидеть ошибку, если это так (поскольку DLL с исходным именем сейчас не существует). Есть способ справиться с этим, если это произойдет, но на этом этапе это становится довольно сложным, поэтому я не буду вдаваться в подробности, если вы действительно не хотите / не нуждаетесь в этом.
То есть у вас ситуация похожая на MSVCRT80.DLL и MSVCRT90.DLL? Microsoft не зря присвоила этим библиотекам номера. Если бы обе они назывались MSVCRT.DLL, только одна из них была бы загружена в одном процессе.
В моей ситуации обе библиотеки DLL называются MSVCRT.DLL, но различаются по своему содержанию.
На самом деле можно неявно загружать разные версии dll в один процесс.
Это влечет за собой:
Создание двух сборок, каждая из которых содержит версию dll, которую необходимо загружать несколько раз. Звучит сложно, но на практике это влечет за собой немного больше, чем создание (2) именованных подпапок, каждая с файлом .manifest, содержащим некоторый xml, и собственной копией dll. Итак, VendorUtilsAssemblyV1 и VendorUtilsAssemblyV2
Заставить каждую зависимую dll использовать механизм сборки для разрешения неявной зависимости - путем добавления директивы assemblyDependency, которая явно идентифицирует VendorUtilsAssemblyV1 или V2.
Есть несколько вариантов для пункта 2. Если файлы VendorLibA и VendorLibB не содержат своих собственных манифестов, вы можете просто добавить файлы манифеста с необходимой директивой зависимой сборки с именами VendorLibA.2.dll.manifest и VendorLibB.2.dll.manifest. Если они уже содержат манифесты (возможно, для связи с VS2005 или VS2008 C-Runtime), используйте инструмент MT.EXE для объединения в новую зависимость.
У меня была аналогичная проблема. В частности, я хотел использовать PyQt из интерпретатора Python, встроенного в приложение, которое использовало несовместимую версию Qt. Основное приложение использовало две библиотеки DLL Qt: 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) неверны.
То, что произошло потом, я не горжусь.
First I renamed
QtGui4.dllin the PyQt distribution toQtGuiX.dlland then renamed theQtCore4.dlltoQtCoreX.dll. Notice that the renaming maintained the same number of characters, this is important.Next I opened the file
QtGui.pydin Notepad++, and replaced all plain-text references ofQtGui4.dlltoQtGuiX.dlland fromQtCore4.dlltoQtCoreX.dll. I repeated the process for the files:QtCore.pyd,QtGuiX.dllandQtCoreX.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.
Вероятно, это неправильный способ делать что-то, но я не могу придумать никаких конкретных причин, по которым он может взорваться (кроме случаев, когда я изменяю длину имени файла).
Поблагодарите (или обвините) других в ветке за то, что они вдохновили эту ужасную историю.
Здравствуйте, это именно та проблема, которую я пытаюсь решить. У нас есть большое приложение на C++, в которое встроен интерпретатор Python (через Python DLL) для поддержки подключаемых модулей Python. Мы используем Boost.Python, чтобы предоставить C++ API для плагинов Python и PyQt, чтобы позволить плагинам получить доступ к нашему пользовательскому интерфейсу. Однако мы поставляем наши собственные библиотеки DLL Qt, поэтому PyQt должен использовать их вместо собственных. Ваше решение - общепринятый способ ведения дел? :) Вы знаете какой-нибудь еще "официальный" способ? Какие-нибудь подробности, которыми вы не поделились здесь, но о которых стоит знать?
@ FrançoisBeaune Я не знаю лучшего способа сделать это, извините.
Экспортируется по имени: /. Какие-нибудь поисковые слова Google для этого?