SetDllDirectory не работает каскадно, поэтому библиотеки зависимостей не могут быть загружены
Я выполняю exe из каталога, скажем, "C:/test"
Библиотеки находятся в каталоге "C:/test/dlls", поэтому в этом exe я вызываю:
SetDllDirectory("C:/test/dlls");
Тогда я звоню
lib1 = LoadLibrary("lib1.dll)
а также
ptrType pr = (ptrType) ::GetProcAddress(lib1, "test")
lib1.dll
нужны другие библиотеки DLL, которые находятся в каталоге "C:/test/dlls", но когда я выполняю pr(...)
получил от GetProcAddress
Я получаю ошибку:
"Программа не может запуститься, потому что lib2.dll отсутствует на вашем компьютере. Попробуйте переустановить программу, чтобы решить эту проблему."
Если я перееду lib2.dll
в "C:/test", он найден. Это означает, что SetDllDirectory()
действует только для загрузки первой DLL.
Кто-нибудь знает почему и как это исправить?
1 ответ
Это должно работать. SetDllDirectory
делает "каскад" и, следовательно, будет влиять на загрузку зависимых DLL. В принципе, SetDllDirectory
позволяет изменить порядок поиска по умолчанию для DLL-файлов процесса, и любые изменения, внесенные в него, влияют на весь процесс, включая любые DLL-библиотеки, которые могут быть загружены в этот процесс. Этот порядок поиска также используется Windows, когда он неявно загружает зависимости для дочерних процессов.
Тем не менее, есть фатальный недостаток вSetDllDirectory
, как указано в документации:
Каждый раз
SetDllDirectory
вызывается функция, она заменяет каталог, указанный в предыдущемSetDllDirectory
вызов.
Если какой-то другой код в вашем процессе вызываетSetDllDirectory
тогда он отменяет ваш первый звонок. Если это код в lib1.dll, который делает это до того, как попытается загрузить lib2.dll, то попытка загрузить lib2.dll не удастся.
Теперь, конечно, это плохое поведение со стороны lib1.dll. DLL являются гостями в процессе подачи заявки и поэтому не должны менять ковер. Но не весь код хорошо себя ведет, что делает эту стратегию хрупкой.
Другой путь, который мог бы пойти не так, был бы, если lib1.dll загружает lib2.dll, вызываяLoadLibraryEx
и передать один из множества флагов, которые переопределяют порядок поиска по умолчанию, например, LOAD_LIBRARY_SEARCH_APPLICATION_DIR
,
Лучшее решение - поместить все библиотеки DLL, от которых вы зависите, в каталог приложения. В Windows каталог приложения - это комплект приложений, так что это лучшее место для их размещения. Пользователи никогда не должны копаться в этом каталоге, поэтому "беспорядок" не является проблемой. Он решает все виды проблем, включая атаку с внедрением зависимостей, когда кто-то помещает DLL с тем же именем в текущий каталог. (Вы можете предотвратить этот тип атаки, позвонив SetDllDirectory
и передаем пустую строку, чтобы удалить текущий каталог из порядка поиска, но теперь мы вернулись к проблеме #1. Если в вашем доме есть гость, который изменяет порядок поиска в DLL, это не является надежным решением проблемы безопасности. Если вы поместите библиотеки DLL в каталог приложения, они будут найдены в первую очередь до начала поиска в текущем каталоге, закрывая вектор атаки, прежде чем его можно будет использовать.)
Если у вас есть действительно веская причина для помещения библиотек DLL в другой каталог, у вас есть ограниченное количество вариантов.
Вы не можете использовать AddDllDirectory
как следует из приведенной выше документации, потому что вы не управляете кодом в lib1.dll и, следовательно, не можете изменить его для вызова LoadLibraryEx
с LOAD_LIBRARY_SEARCH_USER_DIRS
флаг.
Загрузка lib1.dll путем указания полного пути не будет работать, потому что "Если у DLL есть зависимости, система ищет зависимые DLL, как если бы они были загружены только с именами их модулей. Это верно, даже если первая DLL была загружена указав полный путь."
Это оставляет вам возможность либо использовать перенаправление DLL, либо добавить информацию о ваших зависимостях в манифест вашего приложения.