Как плагин Win32 App может загрузить свою DLL в своем собственном каталоге - PullRequest
4 голосов
/ 14 апреля 2010

Мой код - это плагин для конкретного приложения, написанный на C ++ с использованием Visual Studio 8. Он использует две библиотеки DLL от внешнего поставщика. К сожалению, мой плагин не запускается, потому что библиотеки DLL не найдены (я поместил их в тот же каталог, что и сам плагин).

Когда я вручную перемещаю или копирую библиотеки DLL в каталог приложения хоста, плагин загружается нормально. Это перемещение было сочтено неприемлемо громоздким для конечного пользователя, и я ищу способ, чтобы мой плагин мог прозрачно загружать свои библиотеки DLL. Что я могу сделать?

Соответствующие данные:

  • Плагины хост-приложения находятся в каталоге, назначенном хост-приложением. Этот каталог не находится в пути поиска DLL, и я не контролирую его.
  • Плагин сам упакован как подкаталог каталога плагинов, содержащий сам код плагина, а также любой ресурс, связанный с плагином (например, изображения, файлы конфигурации ...). Я контролирую, что находится внутри этого подкаталога, называемого «комплектом», но не там, где он находится.
  • общая идиома установки плагина для этого приложения для конечного пользователя, чтобы скопировать комплект плагина в каталог плагина.

Этот плагин является портом от версии плагина для Macintosh. На Mac нет проблем, потому что каждый двоичный файл содержит свой собственный путь поиска в динамической библиотеке, который я установил так, как мне было нужно для моего двоичного файла плагина. Чтобы установить это на Mac, просто включите настройку проекта в Xcode IDE. Вот почему я хотел бы надеяться на что-то подобное в Visual Studio, но я не смог найти ничего уместного. Более того, помощь Visual Studio была не чем иным, как и Google.

Возможный обходной путь - мой код явно скажет Windows, где найти DLL, но я не знаю как, и в любом случае, поскольку мой код даже не запущен, у него нет возможности сделай так.

Как разработчик для Mac, я понимаю, что могу просить что-то очень элементарное. Если это так, я прошу прощения, но у меня кончились волосы, чтобы вытащить.

Ответы [ 4 ]

7 голосов
/ 14 апреля 2010

Вы не просите чего-то очень элементарного. Windows просто не поддерживает то, что вы хотите.

У вас есть несколько вариантов решения этой проблемы:

  • Создать две библиотеки DLL. Ваша реализация плагина dll, которая статически связывается с любыми другими dll, которые вам нужны. И простая "фасадная" dll, которая загружается приложением хостинга. Фасадная библиотека DLL вызывает метод SetDllDirectory, затем LoadLibrary для загрузки вашей библиотеки реализации с требуемым путем поиска, а затем для каждой экспортируемой функции плагина реализует функцию-заглушку, которая использует GetProcAddress, чтобы просто передать вызов прямо в вашу библиотеку реализации.

Если интерфейс плагина сложный, а используемый вами интерфейс dll - нет, то:

  • Откажитесь и просто используйте LoadLibrary (с явным путем) и GetProcAddress, чтобы получить доступ к функциональности ваших спутниковых DLL. Боль.

  • Последний вариант наименее документирован и наиболее плохо понимается программистами Windows. В основном мы используем версию технологии Windows, созданную для поддержки сборок .NET: Side by Side. Не пугайся «Сборка бок о бок» - это просто обычная старая dll, но с сопровождающим файлом .manifest, который предоставляет некоторую дополнительную информацию о ней.

Причина, по которой мы хотим это сделать, заключается в том, что порядок поиска DLL-файлов, связанных с помощью технологии SxS, отличается от обычного порядка поиска DLL: - А именно - после поиска c: \ windows \ WinSxS окна будут искать то же самое папка как dll, которая ссылается на dll, а не на папку exe.

Начните с инвентаризации всех dll-сателлитов, к которым должен подключиться dll-плагин, и создайте из них «сборку». Что означает: создать файл .manifest с кучей файлов = узлов. Вам необходимо дать имя сборке. Давайте назовем это «MyAssembly».

Создайте файл «MyAssembly.manifest» в папке вашего dll, содержимое которого будет примерно таким: (перечисляя каждый из dll, который вам нужно включить)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="MyAssembly" processorArchitecture="*" type="win32" version="1.0.0.1"/>
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>

Теперь это ваш манифест сборки . Мы наполовину закончили.

Следующая половина состоит в том, чтобы на самом деле заставить вашу dll использовать сборку, и для этого вам нужно добавить ресурс манифеста в ваш файл Dll. В конечном итоге этот манифест должен содержать следующее содержание: -

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="MyAssembly" version="1.0.0.1" processorArchitecture="*"/>
        </dependentAssembly>
    </dependency>
</assembly>

Очевидно, что манифесты приложения (которые вводят в заблуждение при вводе в dll), также могут использовать узел <file>, поэтому можно пропустить создание сборки и просто перейти с

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>

как манифест длл. Я еще не играл с этой итерацией, поэтому я не уверен, как это изменит нормальный путь поиска DLL (если вообще).


Не зная вашей среды разработки, сложно понять, как посоветовать вам, как добавить манифест в dll. Если вы редактируете файл .rc и вводите манифест вручную, знайте, что в Dlls идентификатор ресурса для использования равен 2, а не 1, что обычно используется в примерах exe.

Если вы используете DevStudio 2005 или выше, есть удобная директива #pragma, которая заставит все магически иметь правильные идентификаторы и быть в правильных местах.


Если в настройках проекта установлены значения по умолчанию, VS2005 и выше автоматически сгенерируют и при необходимости вставят манифест. эта #pragma добавит дополнительные зависимости сборки в сгенерированный манифест: -

#if _MSC_VER >= 1400 // VS2005 added this directive
#pragma comment(linker, \
    "\"/manifestdependency:type='Win32' "\
    "name='Company.Product.Subsystem' "\
    "version='6.0.0.0' "\
    "processorArchitecture='*' "\
    "language='*'\"")
#endif
1 голос
/ 15 апреля 2010

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

Итак, допустим, вы хотели отложить загрузку библиотеки XmlLite. Сначала вы должны указать /DELAYLOAD:XmlLite.dll в флагах компоновщика. Затем в функции инициализации вашего модуля (предпочтительно DllMain) вы распаковываете библиотеку XmlLite во временную папку и затем вызываете LoadLibrary для нее. После этого каждый вызов любой функции, экспортируемой XmlLite.dll, будет разрешаться автоматически.

0 голосов
/ 14 апреля 2010

Если исходный код и что вы можете использовать явную динамическую ссылку во время выполнения (вместо любой формы неявной ссылки), используйте GetModuleHandle и GetModuleFileName для узнай, откуда твоя dll работает.

HMODULE hModule = GetModuleHandleW(L"RunningDll.dll");
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);

Затем замените базовое имя библиотеки на имя файла plugin.dll, который вы хотите загрузить.

CString plugin(path);
int pos = plugin.Find(L"RunningDll.dll");
plugin = plugin.Left(pos);
plugin += L"pluginName.dll";

Вызов LoadLibrary для сгенерированной строки.

0 голосов
/ 14 апреля 2010

Используйте GetModuleFileName (), чтобы найти путь, где находится ваша DLL. Затем используйте SetDllDirectory (), чтобы добавить этот путь к пути поиска DLL.

...