Загрузка модуля Win32 из нескольких каталогов - PullRequest
3 голосов
/ 20 декабря 2011

У меня есть программа, которая хранит плагины в нескольких каталогах, например:

root/
  core/bin/
    app.exe
    core.dll
    plugin.dll
    support.dll
  a/bin/
    a.dll
    a_support.dll

В этом примере a.dll импортирует core.dll, support.dll и a_support.dll (они в указанном порядке в таблице импорта). a_support.dll импорт support.dll. Я могу изменить все, кроме модулей поддержки, это редиректы сторонней библиотеки.

Мой код вызывает LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) для загрузки каждого плагина. Для core.dll и plugin.dll это работает нормально.

Когда я пытаюсь загрузить a.dll, происходит сбой , говоря, что a_support.dll не найден . Нет ошибок о core.dll или support.dll, возможно, потому что они уже в памяти.

Я подозреваю, что когда загружено a_support.dll, support.dll не может быть найдено, но это кажется необычным, поскольку a.dll, кажется, импортирует support.dll до a_support.dll.

Возможно ли использовать эту компоновку модулей ? Сможет ли система использовать уже загруженные вспомогательные библиотеки DLL, или она начнет их искать и произойдет сбой? Будет ли способ справиться с этим через манифесты? Есть ли способ заставить это работать, или мне придется переместить все модули в один каталог?

Редактировать: По предложению Адриана МакКарти я запустил последовательность загрузки с отслеживанием Process Monitor, и кажется, что когда я вызываю LoadLibrary("root/a/bin/a.dll", ...), он начинается с поиска в корневом каталоге, затем в системных каталогах, затем вниз по пути. По какой-то причине он никогда не ищет a/bin/, что ему очень нужно.

Я дважды проверил пути и заметил, что мои вызовы для загрузки plugin.dll используются по неправильному пути (root, вместо root / core / bin). В любом случае, core.dll загружался правильно. После исправления я попробовал еще раз, и на этот раз a.dll находит a_support.dll и, кажется, загружается. Однако это не имеет никакого смысла, если только загрузчик не использует support.dll откуда-то еще. Журнал procmon не показывает, что он даже пытался загрузить support.dll снова, поэтому я не совсем уверен, есть ли на самом деле проблема (кроме поведения загрузчика, которое не имеет смысла).

Ответы [ 4 ]

1 голос
/ 21 декабря 2011

Я предлагаю использовать Process Monitor , чтобы увидеть, что на самом деле происходит. Вы увидите, смотрит ли он в нужном месте, открыт ли a_support.dll, но не загружается ли потому, что чего-то еще не хватает, и т. Д.

1 голос
/ 21 декабря 2011

Это довольно запутанное приложение.

Некоторые вопросы тогда:

  • Какие dll'ы неявно импортирует app.exe?
  • core.dll неявно загружается a.dll И как плагин через LoadLibraryEx?
  • Как был успешен вызов LoadLibraryEx для /plugin.dll? Если путь был FQ и не указывал на фактическую dll, LoadLibrary должен был бы полностью потерпеть неудачу на этой dll.
1 голос
/ 20 декабря 2011

Конечно, одним из решений будет размещение всех библиотек DLL в одном каталоге, в том же каталоге, что и .exe. Если вы можете заставить себя сделать это, это будет самый простой подход.

Если нет, то у вас будет немного больше работы. Я предполагаю, что вы ожидаете, что загрузчик будет искать в каталоге, где живет DLL. К сожалению, это не так. Вместо этого загрузчик будет сначала искать в каталоге исполняемого файла, а затем в остальной части порядка поиска DLL . Вот почему a_support.dll не удается загрузить, потому что он не находится в том же каталоге, что и исполняемый файл.

Тот факт, что модули уже находятся в памяти, не имеет значения. Загрузчик ищет файл. Когда он находит нужный файл, он проверяет, загружен ли он. Если это так, то он просто увеличивает счетчик ссылок на этот модуль. В противном случае он загружает его в процесс.

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

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

Так что я думаю, что основной оставшийся вариант - SetDllDirectory. Позвоните прямо перед загрузкой плагина. Вам нужно только добавить a/bin к пути поиска, так как остальные модули находятся в каталоге исполняемого файла и поэтому будут найдены без проблем. Восстановите этот параметр по умолчанию, снова вызвав SetDllDirectory, передав NULL, как только плагин загрузит и разрешит все свои импорты.

Если у вас несколько подкаталогов, используйте AddDllDirectory.

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

Я не могу сказать, вы даете пример кода или реальный код.Вы написали:

LoadLibrary("root/a/bin/a.dll", ...)

Если это реальный код, здесь есть две проблемы.

Во-первых, LoadLibrary не делает то, что вы ожидаете с относительным путем.From MSDN :

Чтобы загрузить модуль из относительного пути без поиска другого пути, используйте GetFullPathName, чтобы получить не относительный путь, и вызовите LoadLibrary с не относительным путем.Для получения дополнительной информации о порядке поиска DLL см. Порядок поиска в библиотеке динамических ссылок.

По сути, вы даете ему полный путь и получаете этот файл, или вы разрешаете ему искать имя во всех«обычные» локации.Если вы дадите ему относительный путь, он в основном игнорирует этот путь, захватывает имя и смотрит в обычных местах.

Если вы действительно имели в виду LoadLibraryEx, учтите, что когда вы используете LOAD_WITH_ALTERED_SEARCH_PATH, вы получаете "неопределенное поведение "если передать относительный путь.Снова кавычки MSDN:

Если это значение используется, а lpFileName указывает относительный путь, поведение не определено.

Во-вторых, у вас есть впередкосая черта вместо обратной косой черты.Ни LoadLibrary, ни LoadLibraryEx не нравятся те.

...