C ++: манифестирует и динамически загружает библиотеки DLL из другого каталога

Длинная история того, чего я пытаюсь достичь

Я работаю над программой, которая динамически загружает библиотеки DLL как плагины. Я компилирую программу с помощью Microsoft Visual C ++ 2008. Тем не менее, давайте предположим, что должна поддерживаться любая версия Visual C ++, с которой работает Qt. Структура каталога программы следующая:

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| program.exe

program.exe обнаруживает все файлы DLL плагинов, выполняет для них LoadLibrary () и вызывает определенную функцию подписи, чтобы узнать, действительно ли это плагин или нет. Это очень хорошо работает на компьютерах, на которых установлен vcredist для MSVC90. Естественно, чтобы программа работала на всех компьютерах, я должен распространять ее с файлами msvc * .dll и с соответствующим файлом манифеста. Библиотеки DLL Qt также требуют запуска redist.

Теперь я настроил cmake для автоматического копирования соответствующих перераспределенных DLL и манифеста в зависимости от выбранной версии Visual Studio. Для простоты будем предполагать, что я работаю с MSVC90. Когда redist копируется в каталог программы, макет выглядит следующим образом:

| plugins/
|   plugin1.dll
|   plugin2.dll
| QtCore4.dll
| QtGui4.dll
| msvcm90.dll
| msvcp90.dll
| msvcr90.dll
| Microsoft.VC90.CRT.manifest (I'm also aware that this file is bugged in VS2008)
| program.exe

Относительно ошибки в файле манифеста: http://www.cmake.org/pipermail/cmake/2008-September/023822.html

Проблема

Программа с таким макетом теперь работает на компьютерах, на которых не установлен Redist, но плагины не загружаются. Чтобы загрузить плагины, мне нужно выполнить одно из следующих действий:

  1. Скопируйте файл манифеста в каталог plugins/. Удалите все ссылки на файлы msvc * .dll из файла манифеста. Это работает, но это нехорошо, потому что мне пришлось бы поддерживать разные версии редактируемых файлов манифеста в зависимости от версии используемого MSVC. Кроме того, я понятия не имею, не сломается ли это с Visual Studio, кроме 2008.
  2. Скопируйте весь список redist в каталог plugins/. Это не требует каких-либо изменений в файле манифеста, но теперь program.exe тупо пытается загрузить файлы msvc * .dll, думая, что это плагины. Естественно, это терпит неудачу, так что большого вреда не будет. Другой недостаток - размер пакета программы увеличивается более чем на 1 МБ. Однако я могу смириться с обеими этими проблемами.
  3. Компилируйте плагины с переключателем / MT. Краткое тестирование показало, что это действительно работает, но я не уверен, что это ничего не сломает в будущем, если и Qt, и program.exe будут / MD.

Вопросы)

Какое лучшее решение? Какое правильное решение? Если существует более одного правильного решения, то какое решение лучше всего? Я первый, кто когда-либо пытался это сделать?

Обновление 1 (18 ноября 2012 г.)

Пока вопрос остается без ответа, я решил пойти по маршруту, который вызывает наименьшую головную боль. До сих пор я использовал раствор №1 и решил придерживаться его. Если CMake обнаружит, что пользователь использует версию MSVC, отличную от версии 2008, он отобразит предупреждающее сообщение о том, что автоматическая упаковка не полностью поддерживается.


person ZalewaPL    schedule 04.11.2012    source источник
comment
Почему бы вам не связать статически время выполнения для вашего приложения и для ваших плагинов?   -  person Nathan Moinvaziri    schedule 05.11.2012
comment
Qt уже требует vcredist, поэтому мне все равно придется его включить. Кроме того, существуют проблемы с распределением ресурсов, когда ресурс выделяется в одном статически связанном объекте, а затем освобождается в другом - это небезопасно, и программа может аварийно завершить работу. Я не знаю, как это будет вести себя с Qt, если Qt - это / MD.   -  person ZalewaPL    schedule 05.11.2012
comment
Я думаю, вы должны позволить авторам плагинов решать, как они хотят связываться с библиотекой времени выполнения, а не пытаться заставить их динамически связываться с определенной версией среды выполнения. Что касается проблем с распределением, о которых вы упомянули, если вы вызываете плагин для выделения ресурса, вам следует снова позвонить в плагин, чтобы освободить ресурс.   -  person Nathan Moinvaziri    schedule 05.11.2012


Ответы (3)


Если ваша целевая ОС имеет _WIN32_WINNT> = 0x0502, вы можете использовать функцию

SetDllDirectory()

перед загрузкой плагинов.

Укажите путь к основной папке программы.

Вызов отменяет порядок загрузки системы:

  1. Каталог, из которого загружено приложение.
  2. Каталог, указанный путем в вызове SetDllDirectory ().

Итак, вы можете вызвать функцию после запуска приложения. Это безопасно во всех случаях. Удачи!

person Brian Cannard    schedule 07.11.2012

Вы можете указать полные пути к файлу "LoadLibrary", чтобы вы могли загружать свои плагины с их путями. Я использовал этот точный макет для загрузки нескольких версий одной и той же библиотеки из подкаталогов текущей dll в Visual Studio 2005.

Сначала вам нужно получить текущий путь к текущей dll, используя:

static LPSTR strDLLPath1 = new TCHAR[_MAX_PATH+1];
::GetModuleFileName((HINSTANCE)&__ImageBase, strDLLPath1, _MAX_PATH);

Хотя, если ваш program.exe уже обнаруживает эти файлы плагинов, я предполагаю, что у вас уже есть доступ к их полным путям.

person MultiMat    schedule 27.11.2014

Вы можете создать жесткие ссылки на DLL VC с помощью функции CreateHardLink () в процессе установки. При использовании метода (1), который вы описали, могут возникнуть проблемы с разными копиями файлов DLL VCRT. Hardlinks или SetDllDirectory () кажется лучшим решением.

Не смешивайте в одном процессе статическую и динамическую ссылку на MSVCRT - это ВСЕГДА создает проблемы!

person Brian Cannard    schedule 07.11.2012