目录
1、问题说明
2、使用Process Explorer查看目标dll动态库有没有动态加载起来
3、使用Dependency Walker查看xxpadll.dll库的库依赖关系,找到xxpadll.dll加载失败的原因
4、使用PE信息查看工具查看目标dll库的时间戳
5、关于xxsipstack2.dll中调用xxdatanet.dll中接口的小插曲
6、最后
C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达600多个,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新500多篇,订阅量已达数百个,欢迎订阅,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到实战(重点专栏,专栏文章已更新300多篇,欢迎订阅,持续更新中...)
https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585分析C++软件问题的实用软件与高效工具实战案例集锦汇总(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_2276111.html 在软件的日常开发与维护过程中,我们需要熟练使用一些常用软件分析工具去辅助排查软件运行时遇到的多个问题,常用的工具有SPY++、Dependency Walker、PE查看工具、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg和IDA等。今天我们就来讲一个项目中遇到的动态库动态加载失败的问题实例,讲述如何使用Process Explorer、Dependency Walker和PE信息查看工具来快速排查这个问题,以供大家借鉴或参考。
1、问题说明
某天测试人员反馈,客户端软件中出现问题,导致客户端无法注册登录到平台的某个业务服务器上,导致与该业务服务器相关的业务出现异常。经维护该业务模块的同事排查,初步怀疑是客户端底层的某个业务dll动态库没有动态加载起来,导致登录业务服务器失败。我们的客户端软件,从UI层到底层业务模块,包含了上百个dll库,排查起来似乎不太容易,但实际上我们使用Process Explorer、Dependency Walker和PE信息查看工具这几个工具就能快速定位问题。在这个问题中,这三个工具的主要用途如下:
1)Process Explorer:查看目标dll在程序启动后有没有动态加载起来。
2)Dependency Walker:查看没有动态加载起来的动态库的库依赖关系,确定该动态库为什么动态加载失败。
3)PE信息查看工具:查看dll动态库的时间戳,即动态库的生成时间。
2、使用Process Explorer查看目标dll动态库有没有动态加载起来
经维护业务模块开发人员初步排查,怀疑客户端底层的某个dll业务库没有动态启动起来。其实确认这个dll有没有动态加载起来,很简单,直接使用Process Explorer工具就可以立即看出来。打开Process Explorer,在进程列表中找到客户端进程,选中该进程,在下方显示的加载库列表中去查找目标dll库,如果找不到,就表示这个dll库没有加载起来。
本问题没有加载起来的库是xxpadll.dll,库列表中找不到这个库:
所以该库没加载起来。
有人可能会问,既然xxpadll.dll库没有加载起来,那程序启动时为什么没有报错呢?这里涉及到一个知识点,动态库加载有两种方式,一种是隐式加载,一种是显式加载。
所谓隐式加载,就是在代码中包含dll动态库的头文件并引入dll动态库对应的.lib库,然后在代码中直接调用dll库的导出接口。对于隐式加载的dll库,程序启动时会优先将该dll库加载到进程空间中,如果dll库加载失败,则会弹出报错提示框,比如:
然后程序启动过程终止,程序启动失败。
所谓显式加载,就是先调用LoadLibrary或LoadLibraryEx将库动态加载起来,然后通过函数名称去获取函数地址,然后去调用该函数。对于显式加载的动态库,不会在程序启动时加载,而是在执行到LoadLibrary或LoadLibraryEx接口调用时才会动态加载,如果库加载失败,并不会弹出报错提示框。
在本案例中,没加载起来的xxpadll.dll库,是显式动态加载的,所以加载失败时不会弹出报错提示框。
3、使用Dependency Walker查看xxpadll.dll库的库依赖关系,找到xxpadll.dll加载失败的原因
dll动态库加载失败,应该都是其依赖的dll库有问题导致的,比如在运行的机器上找不到依赖的某个dll库,或者是调用的某个dll库中的接口在该库中找不到(可能接口名称修改了,可能是接口参数修改了,所以找不到了)。要确定dll库加载失败的具体原因,很简单,直接用Dependency Walker打开这个加载失败的xxpadll.dll库,查看该库依赖的库关系,就能搞清楚。
于是用Dependency Walker打开xxpadll.dll库,看到了异常,如下所示:
xxpadll.dll依赖了xxsipstack2.dll库,而这个xxsipstack2.dll库依赖了xxdatanet.dll库,在xxdatanet.dll库节点前显示异常的红色图标,具体看到xxsipstack2.dll调用了xxdatanet.dll库中的GetProductName、RetrieveVendor、strucase和SubvendorReturn2One这几个接口,但接口节点前也显示红色图标,这表示这几个接口在当前的xxdatanet.dll库中找不到(在exe主程序安装目录中的xxdatanet.dll库中找不到这些接口)。
当前问题中的xxsipstack2.dll和xxdatanet.dll等库,属于协议层的库,而昨天协议层向我们的软件代码流上发布了新的协议库(包含了多个dll库),所以根据以往的经验,估计是xxsipstack2.dll和xxdatanet.dll这两个库的版本不一致导致的,此时需要查看这两个库的生成时间去确定。
我之前详细总结了DLL动态库加载失败导致程序启动报错以及DLL库加载失败的常见原因,很有实战参考价值,可以查看对应的文章:
【C++动态库】DLL动态库加载失败导致程序启动报错以及DLL库加载失败的常见原因分析与总结https://blog.csdn.net/chenlycly/article/details/142714236 关于使用工具排查dll动态库加载失败的实战分析案例,可以查看我之前写的文章:
使用Process Explorer和Dependency Walker排查C++程序启动时缺少ucrtbase.dll等运行时库以及报0xC000007B错误https://blog.csdn.net/chenlycly/article/details/140731927使用Process Explorer和Dependency Walker排查dll动态库没法调试的问题(dll库加载失败导致没法动态调试)
https://blog.csdn.net/chenlycly/article/details/140803687 此外,有个细节点需要注意一下,Dependency Walker打开dll库文件时会特别慢,有时甚至需要等到好几分钟才能打开,然后才能去查看库与库的依赖关系。我们当前使用的Dependency Walker是2006年的版本:
可能是这个老版本的Dependency Walker对当前的Win10系统兼容不好,所以打开的非常慢。
在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)
专栏1:(该专栏是核心精品专栏,当前订阅量已达到600多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,已经更新到200篇以上!欢迎订阅!)
C++软件调试与异常排查从入门到精通系列文章汇总https://blog.csdn.net/chenlycly/article/details/125529931
本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!
考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!
专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!
专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达300多个,专栏文章已经更新到500多篇,持续更新中...)
C/C++实战进阶(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。
专栏3:
C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/131405795
常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!
专栏4:
VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/article/details/124272585
将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。
专栏5:
C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.csdn.net/chenlycly/category_12695902.html
根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。
4、使用PE信息查看工具查看目标dll库的时间戳
上面怀疑是xxsipstack2.dll和xxdatanet.dll版本不一致导致的,可以使用PE信息查看工具查看文件的时间戳(文件编译生成时间)确定模块版本。注意,模块的时间戳不能等同于Windows系统中文件的修改时间:
修改时间只代表该文件在本系统中的修改时间,并不等同于文件的生成时间。要查看文件的生成时间,应该使用PE信息查看工具查看文件的PE头中的时间戳。
用PE信息查看工具PeViewer打开xxsipstack2.dll:
查看到该文件的生成时间(时间戳)为2025/02/21 14:59:40。用PeViewer打开xxdatanet.dll文件:
看到该文件的生成时间(时间戳)为2024/08/29 14:13:11,两个文件的生成时间相差了大半年,所以可能是版本不一致导致的。
于是和协议组的同事确认这次发布过来的协议的库是不是都包含这两个库。协议组同事确认,这两个库应该是同一天编译生成的,即这两个库的生成时间是同一天的,而当前客户端程序的安装目录中的这两个文件的生成时间居然相差大半年,所以初步确定是dll库的版本不一致导致的。
怀疑可能是协议组发布库时xxdatanet.dll编译失败了,导致协议组这次发布新库时没有带上xxdatanet.dll,所以在客户端程序中使用的还是大半年前老版本的xxdatanet.dll库,进而出现版本不一致的问题。经协议组同事确认,xxdatanet.dll库的编译确实有问题,于是去分析xxdatanet.dll库编译失败的原因,将最新版本的xxdatanet.dll库发布过来就好了。
查看dll或exe二进制文件的时间戳和位数,除了使用PE工具,还可以使用Visual Studio自带的dumpbin工具,相关方法及实战使用案例可以查看我的文章:
使用Dumpbin工具查看C++二进制文件的位数、时间戳及dll库的依赖关系https://blog.csdn.net/chenlycly/article/details/140153214使用Dependency Walker和dumpbin工具定位C++软件启动时找不到接口的报错问题
https://blog.csdn.net/chenlycly/article/details/125665650
5、关于xxsipstack2.dll中调用xxdatanet.dll中接口的小插曲
在和协议组的同事排查这个问题的过程中,协议组的同事说xxsipadapter2.dll库中并没有调用GetProductName、RetrieveVendor、strucase和SubvendorReturn2One这几个接口,这个dependency walker工具显示的是否有问题?这个肯定没问题的,dependency walker是读取xxsipadapter2.dll库的PE头中导入表信息得知调用的接口及库依赖关系的,是不会有问题的。
于是让同事在项目代码中搜索这几个接口,看看到底是哪些模块调用的。反馈是xxprotocommon库调用的,但xxsipadapter2.dll库并没有调用这几个接口!于是我估计,这个xxprotocommon库是静态库,直接编译到xxsipadapter2.dll库中了(协议组的同事可能不太了解这一点),所以dependency walker上显示xxsipadapter2.dll库调用了上述几个接口!协议组同事回复,这个xxprotocommon库确实是静态库,这样就能解释的通了。
6、最后
Process Explorer、Dependency Walker和PE信息查看工具,这些小工具看似功能简单,但排查一些问题时确实很有用,能够快速定位问题,有必要把这些工具都学会!常用的软件工具有SPY++、Dependency Walker、PE查看工具、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg和IDA等,关于如何使用这些工具以及使用这些工具排查项目问题的实战分析案例,可以查看我的专栏文章:
分析C++软件问题的实用软件与高效工具实战案例集锦汇总https://blog.csdn.net/chenlycly/article/details/131405795 本文的问题排查起来比较简单,但有一定的代表性,对于开发新手或者不会使用工具的人,有较大的参考价值!