结论
在默认情况下,Windows 加载程序在用户磁盘上搜索 DLL 的搜索顺序:
- 包含可执行文件的目录。
- Windows 的系统目录,该目录可以通过 GetSystemDirectory 得到,一般为 System32 目录,若为 32 位程序跑在 64 位系统下,则为 SysWOW64 目录。
- 16 位的系统目录,即 Windows 目录中的 System 目录。
- Windows 目录,该目录可以通过 GetWindowsDirectory 得到。
- 进程的当前目录。
- PATH 环境变量中所列出的目录。
如果调用 LoadLibrary 时传入的是绝对路径,那么加载程序将只尝试从该绝对路径搜索 DLL。
附注
以上结论在「Windows 核心编程」中列出,书中指出:
注意,对应用程序当前目录的搜索位于 Windows 目录之后,这个改变始于 Windows XP SP2,其目的是防止加载程序在应用程序的当前目录中找到伪造的系统 DLL 并将它们载入,从而保证系统 DLL 始终都是从它们在 Windows 目录的正式位置载入的。
我对这个说法持保留意见,因为在我的验证中,在一个 Windows XP SP1 的环境中已经应用了此搜索顺序。
另外,有一些其它方法可以改变加载程序的搜索顺序,已知的有:
- SetDllDirectory 函数。如果传入一个有效路径,那么它将被插入在默认顺序的 1 与 2 之间。
- HKEY_LOCAL_MACHINES\SYSTEM\CurrentControlSet\Control\Session Manager 下的 SafeDllSearchMode 键值。
- 调用 LoadLibraryEx 函数时使用 LOAD_WITH_ALTERED_SEARCH_PATH 等标志。
验证
证明代码片段:
#include <Windows.h>
#include <stdio.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
char szPath[MAX_PATH] = {0};
GetModuleFileName(hinstDLL, szPath, MAX_PATH);
printf("lib.dll is in %s\n", szPath);
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return (TRUE);
}
用如下命令行生成 lib.dll 文件:
gcc lib.c -shared -o lib.dll
加载 lib.dll 的程序:
#include <Windows.h>
int main()
{
SetCurrentDirectory("D:\\test");
HMODULE hDll = (HMODULE)LoadLibrary("lib.dll");
if (hDll)
{
FreeLibrary(hDll);
}
return 0;
}
用如下命令行生成 test.exe 程序:
gcc test.c -o test.exe
测试方法:
- 在结论中提及的所有路径中各放置一份 lib.dll 文件。
- 运行 test.exe,可以看到控制台输出加载的 lib.dll 文件的路径。
- 把本次 test.exe 加载到的 lib.dll 文件删掉。
- 重复 2-3 步骤。