Windows下DLL查找顺序

Windows下DLL查找顺序

作者:Tocy    时间:2014-10-18

一、写作初衷

在Windows下单个DLL可能存在多个不同的版本,若不特别指定DLL的绝对路径或使用其他手段指定,在应用程序加载DLL时可能会查找到错误的版本,进而引出各种莫名其妙的问题。本文主要考虑以下两个方面:

a. 参考MSDN,给出Windows下DLL查找顺序

b. 简单使用ProcessMonitor来验证DLL查找顺序

二、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查找路径基础

应用程序可以通过以下方式控制一个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、 AddDllDirectoryRemoveDllDirectory

三、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的使用可以参考帮助文档,介绍整体比较详细,这里不做赘述。

四、验证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结束。

五、总结

本文主要介绍了Windows下DLL查找顺序,理清这个查找顺序基本可以找到LoadLibrary返回NULL,提示"找不到指定模块"的原因。通常可以考虑一下几点:

a. 待加载DLL文件是正确的、完整的吗? 是否有损坏? 加载全路径是否正确?

b. 待加载DLL的依赖项是否存在?(Dependency Walker可以查看依赖性)这些依赖项在是否都在系统可以查找到的目录下?

另外,本文简单介绍了使用Process Monitor分析程序运行对文件系统、注册表、进程/线程的操作,工具不错,有待开发。

还有一个问题,需要特别注意的是当前目录,因为很多情况下当前目录会因为SetCurrentDirectory或者OpenFile的操作而改变,某些情况下会对加载DLL造成意外的麻烦。

注:版权所有,请勿用于商业用途,转载请注明原文地址。本人保留所有权利。

时间: 2024-10-10 01:17:47

Windows下DLL查找顺序的相关文章

windows下文件查找

今天再看树的结构时,想起文件文件的储存就是一个典型的树结构,因此基于MFC提供的函数,写了一个关于文件查找的代码. 因为需要用到MFC的类,所以在建立控制台项目时,需要关联MFC: 该代码删除了由于关联MFC而生成的一些其他代码,保留_tmain()函数即可: 采用宽字符,因此使用了wcout输出 功能: 收缩指定磁盘中的文件,可以进行简单的模糊查找. 1 #include "stdafx.h" 2 #include "ListWindowsFile.h" 3 4

windows下如何查找相关的进程和结束进程

netstat -ano | findstr 8443 tasklist | findstr 3816 taskkill -PID 3816 -F 原文地址:https://www.cnblogs.com/han-guang-xue/p/10658358.html

vim7.4版本在windows下的配置文件及所在位置

1.vim在windows下默认首先会查找"_vimrc"文件,如果没有则会找".vimrc".造成这个原因是windows早期不支持以点开头的文件及目录.2.wim7.4版本在windows下默认的配置文件为$VIM/_vimrc,默认的插件文件夹为$VIM/vimfiles,有时候我们也可以在$HOME目录下建立_vimrc文件和vimfiles目录来管理,他们的优先级高于默认的.3.vim7.4版本在windows下,查找顺序是,$HOME/_vimrc,$H

WIndows下编译libexif-0.6.21,生成VS下可用的dll和lib

1.下载libexif-0.6.21.zip 下载地址是http://sourceforge.net/projects/libexif/files/libexif/0.6.21/ 2.解压libexif-0.6.21.zip 我解压到了d:\opensources\下 3.安装Mingw 下载地址是http://www.mingw.org/ 点击右上角的Download Installer 记得安装Msys 我的安装路径是:C:\MinGW 下面依次有如下文件夹: bin include lib

Windows下程序打包发布时的小技巧(使用Dependency Walker侦测不理想,改用VS自带的dumpbin则万无一失,还可查看dll导出的函数)

Windows下开发的应用程序在发布时,需要将其依赖的一些动态链接库一起打进安装包里面去.这个时候,快速确定这个程序到底依赖哪些动态链接库变得非常重要.很久以前写过一篇关于Qt程序安装包制作的博客,里面介绍了Dependency Walker这个小工具.但是实际操作起来并不理想,因为Dependency Walker将exe文件依赖的所有动态库以及动态库所依赖的动态库都列出来了.看得你一脸懵逼: 你很难分清楚:倒底哪些DLL是需要打包的,哪些DLL是系统自带的.而作为打包过程中的一个小步骤,我根

Windows下安装Tensorflow报错 “DLL load failed:找不到指定的模块&quot;

Windows下安装完tensorflow后,在cmd下运行python后import tensorflow出现如下错误: Traceback (most recent call last): File "D:\Python\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 18, in swig_import_helper return importlib.import_

Windows下尝试PHP7提示丢失VCRUNTIME140.DLL的问题解决

前天PHP7.0.0正式版发布了,有一些比较好的改进,官方也说速度比php5.6快了两倍,性能上有了很大提升,并且也发布了从php5.x向php7迁移的问题,所以今后php网站迁移后能够大幅度的提升网站性能,所以为了尝鲜我也去php官网下载了7.0的版本,通过命令行进行独立的测试,下载zip包后解压出来,下载后进入目录,将php.ini-development改为php.ini其余的参数暂时不用修改,然后在当前目录下新建test.php,输入简单的代码: 1 <?php 2 echo "H

在 Windows 下用 TDM-GCC(MinGW)开发 DLL 涉及到数据同步锁及 DLL 初始化终止化函数的问题

在 Windows 下用 TDM-GCC(MinGW)开发 DLL 如果要用到数据同步锁,理论上可以采用 Windows API 提供的临界区实现(需要用到的函数有 InitializeCriticalSection.DeleteCriticalSection.EnterCriticalSection.LeaveCriticalSection),也可以采用 GCC 的 pthread 库中的 pthread mutex lock 互斥锁来实现(需要用到的函数有 pthread_mutex_ini

[转]windows下VS2010中lib与dll文件的生成与使用

原文地址:https://my.oschina.net/SysuHuyh5LoveHqq/blog/644622 近期在windows下开发了某个程序,需要将其生成静态文件(lib)以及动态库文件(dll),其中参考了不少帖子,有的讲得也不是很清楚明白,先将本人实践过的记录一下,供后期自己查询,也供各位大牛点评. 一.lib文件的生成与使用 1.lib的生成 相对来说,静态库文件还是比较容易生成和使用的,在代码上,貌似也不需要更改什么,举例说明: 头文件函数声明形式如下: extern bool