Изменение порядка загрузки Windows DLL? (порядок загрузки, а не порядок поиска)

Скажем, у меня есть один исполняемый файл: app.exe

Я использую 2 разных сторонних DLL в этом исполняемом файле: foo.dll bar.dll, и приложение должно неявно ссылаться на эти DLL, то есть я не могу использовать ::LoadLibrary для их загрузки.

(Примечание. Это не то, что я не могу вызвать LoadLibrary, но для этих DLL требуется статическое связывание (С++ DLL с __declspec(dllexport)), поэтому мне вызов LoadLibrary не имеет никакого смысла, потому что вызываемый загрузчик уже вызвал она.)

Эти две библиотеки DLL не имеют никаких зависимостей друг от друга, то есть порядок загрузки undefined, насколько я могу судить (и должен быть неактуальным). (Зависимости обоих в основном относятся только к стандартным DLL-окнам (kernel32, msvcrt и т.д.).

У меня теперь есть проблема с тем, что я хочу контролировать порядок загрузки этих DLL, то есть я хочу, чтобы foo.dll всегда загружался (DLL_PROCESS_ATTACH) перед bar.dll.

Как-то можно сказать, что загрузчик Windows DLL загружает одну DLL перед другой?

Изменить: до проверить порядок загрузки DLL исполняемого файла, можно использовать утилиту DUMPBIN.exe: (просто запустите командную строку Visual Studio)

Изменить: согласно этот ответ/эта запись в блоге, NT Погрузчик выполняет последовательность импорта последовательно. (Это приведет к загрузке независимых DLL в том порядке, в котором они отображаются в разделе импорта.)

C:\path\to\program> dumpbin /IMPORTS app.exe | grep -i \.dll
  MSVCR80D.dll
  KERNEL32.dll
  OLEAUT32.dll
  MSVCP80D.dll
  foo.dll
  bar.DLL

Этот вывод означает, что сначала будет загружен MSVCR80D.dll(и его зависимостей [a]), и этот бар .DLL будет загружен последним. Выгрузка произойдет в обратном порядке.

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


(Примечания)

[a]: Это означает, конечно, что, например, kernel32.dll будет загружен первым, потому что msvcr80d.dll будет зависеть от kernel32.dll.


В соответствии с некоторыми запросами я добавляю для этого обоснование: (Но please, я все еще заинтересован в этом вообще. Я знаю, как работать с проблемой MFC. )суб >

В Microsoft MFC DLL в этой отладочной версии встроено обнаружение утечки памяти (насколько я могу судить, это тот же механизм, который используется _ CrtSetDbgFlag и соответствующие инструменты.)

Отладочная DLL MFC сбрасывает всю незакрепленную память при ее разгрузке. Теперь, если у вас есть вторая DLL в вашем процессе, которая не зависит от MFC, и эта вторая DLL освобождает память от DLL_PROCESS_DETACH, механизм отчетности MFC будет сообщать о ложных утечках памяти, если MFC DLL выгружается перед другой dll.

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

Ответ 1

То, что я еще не выяснил, как повлиять на этот порядок загрузки...

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

Configuration Properties -> Linker -> Additional Dependencies ...

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

Итак, чтобы ответить на эту часть: Просто предоставите файлы lib в правильном порядке компоновщику.

Примечание. Я пробовал это на VS2005 и, похоже, работает. Я не знаю, где-то документировано, или если оно изменилось в новых версиях VС++.


Обновление. Пока он работал, сегодня я попал в случай, когда порядок загрузки был не, на который повлиял порядок командной строки компоновщика файлов lib. (Тем не менее) Не знаю, почему. (Все еще VS2005)

Однако мне удалось заставить его работать, добавив проблемные библиотеки DLL в список загруженных с задержкой DLL (например, в Macke answer).


Ответ 2

Вот идея: как насчет их маркировки как "Delay Loaded dlls" в опциях компоновщика app.exe?

Задержка загрузки позволит вам связать "статически" (т.е. без LoadLibrary() et.al), но не будет загружать DLL и выполнять привязку, пока она не понадобится.

Если это опция, то (предположив, что вы можете так долго ждать, т.е. не обращаться к функциям doo foo/bar перед main()), вы могли бы в main() получить доступ к функции (просто вызвать функцию ptr или что-то) в foo.dll, чтобы загрузить его и связать все "статически" связанные функции?

(Может быть, LoadLibrary() запускает ту же самую процедуру, что и при необходимости. Не уверен, но будет выглядеть более чистым в вашем коде.)

Ответ 3

Просто добавьте foo.dll в таблицу импорта bar.dll, загрузчик ОС будет обрабатывать остальные.

Вы можете сделать это без исходного кода для bar.dll, не уверен, что инструмент editbin имеет такую ​​возможность, но это довольно тривиальное редактирование PE файла.

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

Ответ 4

Если вы не связываете библиотеку импорта (foo.lib и bar.lib), тогда загрузчик не будет автоматически загружать библиотеки DLL при запуске, и вы можете вызвать LoadLibrary(), когда захотите.

На стороне примечания я написал небольшую небольшую библиотеку для инкапсуляции загрузки DLL на лету без необходимости напрямую обращаться к LoadLibrary/GetProcAddress. Вы можете прочитать об этом здесь.