1、写做初衷 ios
在Windows下单个DLL可能存在多个不一样的版本,若不特别指定DLL的绝对路径或使用其余手段指定,在应用程序加载DLL时可能会查找到错误的版本,进而引出各类莫名其妙的问题。本文主要考虑如下两个方面:windows
a. 参考MSDN,给出Windows下DLL查找顺序安全
b. 简单使用ProcessMonitor来验证DLL查找顺序app
2、DLL查找顺序 函数
(本部分多数内容是参考MSDN上的Dynamic-Link Library Search Order一文,连接以下http://msdn.microsoft.com/en-us/library/ms682586(v=vs.85).aspx。多数为翻译,有部份内容修改。本文仅关注桌面应用程序的查找顺序,对于Windows Store apps请参考MSDN原文。)工具
1. DLL查找路径基础spa
应用程序能够经过如下方式控制一个DLL的加载路径:使用全路径加载、使用DLL重定向、使用manifest文件。若是上述三种方式均未指定,系统查找DLL的顺序将按照本部分描述的顺序进行。命令行
对于如下两种状况的DLL,系统将不会查找,而是直接加载:线程
a. 对于已经加载到内存中的同名DLL,系统使用已经加载的DLL,而且忽略待加载DLL的路径。(注意对某个进程而言,系统已经加载的DLL必定是惟一的存在于某个目录下。)翻译
b. 若是该DLL存在于某个Windows版本的已知DLL列表(unkown DLL)中,系统使用已知DLL的拷贝(包括已知DLL的依赖项)。已知DLL列表能够从以下注册表项看到:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs。
这里有个比较坑的地方,对于有依赖项的DLL(即便使用全路径指定DLL位置),系统查找其所依赖DLL的方法是按照实际的模块名称来的,所以若是加载的DLL不在系统查找顺序目录下,那么动态加载该DLL(LoadLibrary)会返回一个"找不到模块"的错误。
2. 系统标准DLL查找顺序
系统使用的标准DLL查找顺序依赖因而否设置了"安全DLL查找模式"(safe DLL search mode)。"安全DLL查找模式"会将用户当前目录置于查找顺序的后边。
"安全DLL查找模式"默认是启用的,禁用的话,能够将注册表项HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode设为0。调用SetDllDirectory函数能够禁用"安全DLL查找模式",并修改DLL查找顺序。
Windows XP下,"安全DLL查找模式"默认是禁用的,须要启用该项的话,在注册表中新建一个SafeDllSearchMode子项,并赋值为1便可。"安全DLL查找模式"从Windows XP SP2开始,默认是启用的。
启用"安全DLL查找模式"时,查找顺序以下:
a. 应用程序所在目录;
b. 系统目录。GetSystemDirectory返回的目录,一般是系统盘\Windows\System32;
c. 16位系统目录。该项只是为了向前兼容的处理,能够不考虑;
d. Windows目录。GetWindowsDirectory返回的目录,一般是系统盘\Windows;
e. 当前目录。GetCurrentDirectory返回的目录;
f. 环境变量PATH中全部目录。
若是"安全DLL查找模式"被禁用,查找顺序以下:
a. 应用程序所在目录;
b. 当前目录。GetCurrentDirectory返回的目录;
c. 系统目录。GetSystemDirectory返回的目录,一般是系统盘\Windows\System32;
d. 16位系统目录。该项只是为了向前兼容的处理,能够不考虑;
e. Windows目录。GetWindowsDirectory返回的目录,一般是系统盘\Windows;
f. 环境变量PATH中全部目录。
3. 修改系统DLL查找顺序
系统使用的标准DLL查找顺序能够经过如下两种方式调整:
3.1 使用LOAD_WITH_ALTERED_SEARCH_PATH标志调用LoadLibraryEx函数;
这种方式调用LoadLibraryEx函数,须要设置lpFileName参数(绝对路径)。与标准查找策略不一样的是,使用LOAD_WITH_ALTERED_SEARCH_PATH标志调用LoadLibraryEx函数的DLL查找顺序将"查找应用程序所在目录"修改成lpFileName指定的目录。
3.2 调用SetDllDirectory函数。
注意:SetDllDirectory函数在Windows XP SP1开始支持的。
函数SetDllDirectory在调用参数lpPathName是一个路径时,可支持修改DLL搜索路径。修改以后的搜索顺序以下:
a. 应用程序所在目录;
b. 函数SetDllDirectory参数lpPathName给定的目录;
c. 系统目录。GetSystemDirectory返回的目录,一般是系统盘\Windows\System32;
d. 16位系统目录。该项只是为了向前兼容的处理,能够不考虑;
e. Windows目录。GetWindowsDirectory返回的目录,一般是系统盘\Windows;
f. 环境变量PATH中全部目录。
若是lpPathName参数为空字符串,这样就会把当前目录从DLL搜索路径中去掉。
若是用NULL参数调用SetDllDirectory函数,能够恢复按照系统注册表的"安全DLL查找模式"来查找DLL。
固然win8或者windows server 2012提供更多的可定制方法,这个能够参考MSDN上介绍。好比:SetDefaultDllDirectories、 AddDllDirectory、RemoveDllDirectory。
3、ProcessMonitor使用
ProcessMonitor能够从http://technet.microsoft.com/en-us/sysinternals/bb896645下载。
官网给出的介绍资料以下:
Process Monitor一款系统进程监视软件,整体来讲,Process Monitor至关于Filemon+Regmon,其中的Filemon专门用来监视系统 中的任何文件操做过程,而Regmon用来监视注册表的读写操做过程。 有了Process Monitor,使用者就能够对系统中的任何文件和 注册表操做同时进行监视和记录,经过注册表和文件读写的变化, 对于帮助诊断系统故障或是发现恶意软件、病毒或木马来讲,很是 有用。
软件下载以后,解压就能够直接运行。Process Monitor默认会启用针对真当前系统的"File System"、"Registry"、"Process"的全部操做的记录,相似wireshark网卡抓包软件,只是抓取的信息不一样。若是仅关心某个进程的事件,能够在工具栏或者菜单中选择Filter-Filiter,弹出下图所示对话框:
举个例子,咱们只关心进程名为"qwe.exe"的相关操做,能够作以下处理:从第一个下拉列表框中选择ProcessName,将进程名字填入输入框,而后点击"Add"按钮,点击 "OK"(若是已经开始监测,能够直接点Apply按钮)。
其余关于Process Monitor的使用能够参考帮助文档,介绍总体比较详细,这里不作赘述。
4、验证Windows下DLL加载顺序是否正确
那么咱们能够考虑在win7下验证下DLL加载顺序,想法很简单,随便写一个系统中不存在的DLL,用LoadLibray动态加载下看看,用Process Monitor记录当前进程的操做记录。
代码以下:
1 #include <windows.h>
2 #include <iostream> 3 4 int main(int argc, char ** argv) 5 { 6 using std::cout; 7 using std::endl; 8 9 // 随便设置一个不存在的dll名 10 HMODULE hMod = LoadLibrary("123.dll"); 11 12 if (NULL != hMod) 13 FreeLibrary(hMod); 14 15 cout << "LoadLibrary Test" << endl; 16 17 return 0; 18 }
使用MinGW编译以后,在命令行下运行该程序。Process Monitor输出以下信息(这里仅截取关于123.dll加载的信息):
能够看到这里搜索的路径跟系统标准DLL搜索路径时一致的。个人环境变量Path从D:\software\Subversion\Apache2\bin开始到D:\software\tortoiseGit\bin结束。
5、总结
本文主要介绍了Windows下DLL查找顺序,理清这个查找顺序基本能够找到LoadLibrary返回NULL,提示"找不到指定模块"的缘由。一般能够考虑一下几点:
a. 待加载DLL文件是正确的、完整的吗? 是否有损坏? 加载全路径是否正确?
b. 待加载DLL的依赖项是否存在?(Dependency Walker能够查看依赖性)这些依赖项在是否都在系统能够查找到的目录下?
另外,本文简单介绍了使用Process Monitor分析程序运行对文件系统、注册表、进程/线程的操做,工具不错,有待开发。
还有一个问题,须要特别注意的是当前目录,由于不少状况下当前目录会由于SetCurrentDirectory或者OpenFile的操做而改变,某些状况下会对加载DLL形成意外的麻烦。
注:版权全部,请勿用于商业用途,转载请注明原文地址。本人保留全部权利