使用Process Explorer和Dependency Walker排查dll动态库加载失败的问题

news2024/11/17 8:35:45

目录

1、问题描述

2、如何调试Release版本的代码?

3、使用Process Explorer查看exe主程序加载的dll库列表,发现mediaplay.dll没有加载起来

4、使用Dependency Walker查看rtcmpdll.dll的库依赖关系和接口调用情况,定位问题

4.1、使用Dependency Walker工具查看到rtcmpdll.dll依赖的mediaplay.dll库有问题

4.2、为啥rtcmpdll.dll调用的接口在mediaplay.dll中找不到时没有弹出报错提示框呢?

4.3、动态库的入口函数DllMain什么时候会执行到?

4.4、解决办法

5、关于调试底层dll库

5.1、附加到进程调试

5.2、配置exe主程序启动dll调试

6、最后


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础入门与实战进阶(专栏文章已更新280多篇,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlWindows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       音视频开发组的同事要调试他们维护的动态库代码,但始终没有命中断点,于是找到我,让我帮忙看看是什么问题导致的。本文记录一下这个问题的排查过程。

1、问题描述

       音视频开发组的同事为了排查某个问题,需要直接在Visual Studio中调试用来播放音视频的媒体处理动态库mediaplay.dll(直接调试Release版本的库)。

       同事在其机器上安装了exe软件,并将本地Visual Studio编译出来的mediaplay.dll文件拷贝到exe主程序的安装目录中,将原先的mediaplay.dll库覆盖掉。

之所以覆盖,因为本地编译的mediaplay.dll指向的pdb文件在本地可以找到,可以直接借助pdb中的符号信息调试mediaplay.dll库的源码。

然后在打开mediaplay.dll工程的Visual Studio中,打开mediaplay工程的属性窗口,在调试节点下,配置当前dll的启动程序为exe主程序:

然后启动VS调试,当代码执行到dll库中,就可以调试当前dll的源码了。

       因为要调试当前dll的初始化过程的代码,直接将断点设置在dll的入口函数DllMain中,但当exe主程序运行起来后,并没有命中断点。同事搞不懂为啥没有命中断点,不知道是不是哪里配置的有问题,于是找到我,让我帮忙排查一下,看看到底是怎么回事。

2、如何调试Release版本的代码?

       同事说他要调试Release下的代码,当前没有命中断点,不能调试mediaplay.dll库的源码,是不是哪里配置的有问题。

       其实,调试Release下的代码,只要做两点操作就行了。首先,要关闭工程属性中的优化选项:(到C/C++ --> 优化,将优化关闭掉)

因为不关闭优化,很多行的代码可能会被优化掉,这样调试源码时会很麻烦,且很奇怪。

        其次,是将本地编译生成的dll文件拷贝到exe主程序目录中。这样保证exe主程序跑起来后,调用的是本地带调试信息的mediaplay.dll库。其实调试信息主要是存放在本地mediaplay.dll对应的pdb文件mediaplay.pdb中。那调试mediaplay.dll的源码时,是怎么找到其对应的pdb文件呢?是这样的,mediaplay.dll编译时会将对应的pdb文件的完整路径写到mediaplay.dll二进制文件中,类似下面的:

这样调试器可以根据mediaplay.dll中写入的pdb文件的完整路径找到对应的pdb文件,就能将其加载起来,就能使用到pdb文件中的符号信息了,就能进行代码调试了。

       有人可能会问,release下生成pdb文件是否需要手动配置?其实,默认情况下,是自动配置了生成pdb文件的,如下所示:(到链接器 --> 调试 中查看)

当然在进行release调试之前,也可以到上述页面确认一下有没有打开生成pdb的选项。

3、使用Process Explorer查看exe主程序加载的dll库列表,发现mediaplay.dll没有加载起来

       exe主程序下有一层是业务组件层,业务组件层包装了当前的音视频播放库。包装当前mediaplay.dll库的业务组件库是rtcmpdll.dll,这个rtcmpdll.dll是动态加载的(调用LoadLibraryEx接口动态加载的)。因为我这边排查过多个与业务组件库动态加载有关的多个问题,所以猜测可能是rtcmpdll.dll没有动态加载起来,导致更底层的mediaplay.dll没有加载起来。

       要确认dll库有没有加载起来,很简单,直接使用Process Explorer工具查看exe主程序加载的dll列表即可。看看加载的dll列表中是否有mediaplay.dll和rtcmpdll.dll。具体的做法是,打开Process Explorer,找到我们的exe主程序,选中之,就可以看到exe主程序加载的dll列表:

列表中果然没有mediaplay.dll和rtcmpdll.dll两个库。

4、使用Dependency Walker查看rtcmpdll.dll的库依赖关系和接口调用情况,定位问题

       业务组件库rtcmpdll.dll没有动态加载起来,应该是其依赖的库有问题导致的,使用Dependency Walker工具打开rtcmpdll.dll查看一下库的依赖关系和接口状况就可以确定问题了。

4.1、使用Dependency Walker工具查看到rtcmpdll.dll依赖的mediaplay.dll库有问题

       打开Dependency Walker,将rtcmpdll.dll文件拖进来,打开rtcmpdll.dll文件。Dependency Walker是2006年的版本,没有更新的版本了,可能是对后来上市的Win10、Win11兼容不好,打开文件比较慢,可能要等上好几分钟才能打开。打开后,一眼就看到了问题,如下所示:

rtcmpdll.dll依赖的mediaplay.dll库有问题,mediaplay.dll库节点之前显示淡红色,选中mediaplay.dll节点,可以看到rtcmpdll.dll调用的Draw接口(接口所在行显示行色图标)在mediaplay.dll中找不到。往下看,mediaplay.dll库的导出接口列表中是有Draw接口,再仔细一看,两个接口的参数不一样:

mediaplay.dll导出的Draw接口中使用的是tagVIDEO_FRAME结构体,而rtcmpdll.dll中调用的Draw接口中使用的是VIDEO_FRAME结构体,结构体名称之前是没有tag字样的。

       于是问同事有没有修改结构体名称,他说确实修改了,原先的结构体如下所示:

同事看到这个地方定义的有问题,应该在typedef时的原始结构体名称前加一个tag字样,修改后的如下:

这就是问题所在了。当前要调试代码的mediaplay.dll库本地修改了头文件中的结构体名称,但没有拿到业务组件的代码中去编译,即当前业务组件库rtcmpdll.dll使用的还是之前未修改的结构体名称,而当前本地的mediaplay.dll已经使用了修改后的tagVIDEO_FRAME,所以导致rtcmpdll.dll中调用的Draw接口在mediaplay.dll中找不到了(Draw接口的参数不一样了),所以rtcmpdll.dll加载失败了。


       在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:(该精品技术专栏的订阅量已达到500多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有PE工具、Dependency Walker、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏3: (本专栏文章已经更新到280多篇,还在持续更新中,欢迎订阅)

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点、C++11新特性、C++开源库介绍与使用、代码分享(调用系统API、使用开源库)、编程技术(动态库、多线程、多进程和网络编程等)、软件UI编程(duilib/QT)、C++软件调试技术(排查软件异常的手段与方法、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与进阶内容等。

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: 

Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了Windows C++ 应用软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


4.2、为啥rtcmpdll.dll调用的接口在mediaplay.dll中找不到时没有弹出报错提示框呢?

       一般情况下,如果程序中调用的接口在对应的dll中找不到时,启动exe主程序时会弹出如下的类似提示框:

程序是怎么知道该到哪个dll库中去检查调用的接口是否存在呢?这是代码编译链接时确定的,调用的接口会在链接时确定关系,到哪个库中去链接接口。然后在生成dll二进制文件时,会将调用的其他dll中的接口(从其他dll导入的接口)写到二进制文件的PE头中,这样程序启动时在加载二进制文件时会从PE头信息找到其调用哪些库中的哪些接口,就可以直接去校验了。

       弹出上述报错提示框,是使用#pragma comment(lib, "xxx.lib")或者在工程配置中引入lib库(dll对应的lib库,用于链接使用),隐式调用dll接口的场景。而类似于本例中的使用LoadLibrary或LoadLibraryEx动态启动的dll库,如果其依赖的库有问题,是不会弹出报错提示框的。

4.3、动态库的入口函数DllMain什么时候会执行到?

       在排查问题的过程中,同事问我,当前要调试的mediaplay.dll库的DllMain函数在什么时候回执行到?程序启动时,是先进入到mediaplay.dll库的DllMain函数,还是先进入exe主程序的main函数呢?

       其实这个过程很简单,双击启动exe主程序时,会优先把exe依赖的dll库加载到进程空间里来,要加载的dll库,也需要将其依赖的dll库先加载起来,等exe主程序依赖的dll库及底层的dll库都加载起来后,才会将exe主程序文件加载到进程空间中,然后exe主程序开始运行,进入main函数。在加载dll库时,会进入DllMain函数,所以DllMain函数应该是先于exe主程序的main函数被执行到。

4.4、解决办法

       解决办法很简单,将对结构体VIDEO_FRAME的名称修改回退掉即可,先能将代码调试起来排查问题。

       至于结构体VIDEO_FRAME名称定义的不规范,后面再修改,修改后发布到业务组件那边,让他们重新编译包装mediaplay.dll的rtcmpdll.dll库的代码即可。

5、关于调试底层dll库

       比如本案例中的底层库mediaplay.dll,和上层的exe主程序及中间的业务组件层,是不同的开发组负责的,这些模块的代码都不在一起的,没法放到一起直接调试的。

       如果要调试底层的dll库,则需要依赖上层的exe主程序,因为dll库是不能独立运行的,是需要依附在exe主程序中才能跑起来的。需要拷贝Debug版本的exe程序(包含其依赖的底层的库),或者安装release版本的exe程序。然后使用依附到exe主程序的方式去调试底层的库。

当然有的dll库,为了测试dll库的接口及内部的功能,会手写一个python exe主程序,将dll依附在该python exe主程序上测试。但这种测试相对有限,不能集成到具体的产品业务中去测试,所以仅仅依赖这种单元测试是远远不够的。而依附到产品的exe主程序中去测试才是全面的。

       依附exe主程序去调试代码,主要有两种方式,一种是附加到进程调试,一种是配置exe主程序其启动dll调试。两种方式都需要将本地编译出来的dll库拷贝到exe主程序的路径中。

5.1、附加到进程调试

       Visual Studio有一个附加到进程调试的功能。先将exe主程序运行起来,然后在打开dll库源码的Visual Studio中,点击菜单栏中的调试 -> 附加到进程调试,打开附加到进程调试的对话框:

在窗口的进程列表中找到exe主程序的进程,选中之,点击下方的附加按钮,可以开启附加到exe主程序的调试。

5.2、配置exe主程序启动dll调试

       可以到dll工程的属性中,在调试节点下,设置当前dll的启动exe程序的完整路径,如下所示:

这样在Visual Studio开启调试时,会优先根据设置的exe主程序的路径去把exe主程序启动起来,然后就可以调试dll代码了。

       这种方式,便于调试dll库中的初始化代码,如果使用附加到进程调试,要exe主程序启动起来后,才能附加到进程,等exe主程序启动起来后,初始化的代码可能已经执行完了。

        其实为了使用附加到进程调试,可以尝试在初始化的代码中弹出一个MessageBox对话框阻塞一下:

::MessageBox( NULL, _T("将代码阻塞住,给附加进程创造时机!"), _T("Tip"), MB_OK );

给附加到进程创造时机。这个代码会弹出如下的这个提示框:

先不要点确定,将VS附加到进程上,等附加上后再点这个窗口中的确定按钮,把窗口关闭掉,让程序向下跑,就可以调试初始化的代码了。

6、最后

       虽然process Explorer和Dependency Walker都是小工具,但这些小工具在排查小问题时确实很好用、很实用,所以很有必要把这些工具用起来。

       关于日常开发工作中常用的十大软件分析工具,之前专门写了介绍的文章,可以查看:(对应的文章专栏中还撰写了这些工具的实战使用案例,有较大的实战参考价值)

C++软件开发值得推荐的十大高效软件分析工具icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/127608247

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1963260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html+css+js 实现3D透视倾斜按钮,javascript库之vanilla-tilt.js详解

前言:哈喽,大家好,今天给大家分享htmlcss 绚丽效果!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 文…

常用游戏运行库 v4 官方版下载与安装教程 (游戏DLL补全包)

前言 游戏运行库包含了VC运行库合集,.NET2.0到.NET4.8合集,DirectX9.0 Rapture3D 等游戏必备的系统组件,如果你的游戏安装后无法运行,那么安装这些组件基本上就可以解决。本必备运行库安装包集成32位和64位运行库,是目…

(自用)MyLog 简单日志 .net6.0 等

appsettings.json {"LogOnOff": true //true 开启日志&#xff1b;false 关闭日志 } MyLog.cs using System.ComponentModel;namespace Namespace {/// <summary>/// 日志类型 枚举/// </summary>public enum LogType{[Description("调试日志&q…

Android经典面试题之实战经验分享:如何简单实现App的前后台监听判断

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 在Android中判断一个应用是否处于前台或后台&#xff0c;可以使用ActivityLifecycleCallbacks 和 ProcessLifecycleOwner。在Kotlin中&#xff…

实验2-5-3 求平方根序列前N项和

//实验2-5-3 求平方根序列前N项和/*本题要求编写程序&#xff0c; 计算平方根序列123⋯的前N项之和。 可包含头文件math.h&#xff0c;并调用sqrt函数求平方根。*/#include<stdio.h> #include<math.h> int main(){int n0;scanf("%d",&n);//输入Nint …

【Canvas与艺术】三环莫比乌斯圈

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>三环莫比乌斯圈</title><style type"text/css"&g…

测桃花运(算姻缘)的网站系统源码

简介&#xff1a; 站长安装本源码后只要有人在线测算&#xff0c;就可以获得收入哦。是目前市面上最火的变现利器。 本版本无后台&#xff0c;无数据。本版本为开发的逗号联盟接口版本。直接对接逗号联盟&#xff0c;修改ID就可以直接运营收费赚钱。 安装环境&#xff1a;PH…

可以个性化的网盘与相册服务 PDS

可以个性化的网盘与相册服务 PDS 什么是PDS企业版企业网盘团队管理用户管理安全策略企业设置文件设置及其他设置专属登录配置 使用建议企业网盘用户端开发者版体验感受 什么是PDS 在正式开始测评PDS之前&#xff0c;首先来了解一下什么是PDS。PDS 网盘与相册服务&#xff08;D…

Pythonic 的从远程列表中提取分支名称方法

1、问题背景 在 Git 版本控制系统中&#xff0c;我们需要经常使用 git ls-remote 命令来获取远程仓库的分支列表。 这个命令的输出通常包含分支的哈希值和分支名称&#xff0c;就像这样&#xff1a; db6ad7246abf74cb845baa60e6fe45dacf897612 HEAD 1fc347b17201054d8b5b9…

YOLOv8 基于BN层的通道剪枝

YOLOv8 基于BN层的通道剪枝 1. 稀疏约束训练 在损失项中增加对BN层的缩放系数 γ \gamma γ和偏置项 β \beta β的稀疏约束&#xff0c; λ \lambda λ系数越大&#xff0c;稀疏约束越严重 L ∑ ( x , y ) l ( f ( x ) , y ) λ 1 ∑ γ g ( γ ) λ 2 ∑ β g ( β ) L…

华杉研发九学习日记18 集合 泛型

华杉研发九学习日记18 一&#xff0c;集合框架 1.1 集合和数组的区别 集合就是在java中用来保存多个对象的容器 集合是数组的升级版&#xff0c;集合中只能放置对象[object]. 数组: 在java中用来保存多个具有相同数据类型数据的容器 数组弊端&#xff1a; 1.数组只能保存…

2024AICoding公司全景图及评分

AI Coding背景 AI coding 领域的产品和公司在 2024 年开始爆发了&#xff0c;主要涉及技术进步、市场需求和开发者生态系统的变化。 本文会从技术背景&#xff0c;市场需求&#xff0c;生态以及相关评分为大家完整梳理一下相关内容。 底层技术 大规模预训练模型 技术背景&#…

C#使用OPC组件方式和AB的PLC通信

目录 一、PLC硬件配置 1、创建PLC程序 &#xff08;1&#xff09;程序工程选择 &#xff08;2&#xff09;变量和程序 2、配置程序在模拟器中运行 &#xff08;1&#xff09;打开RSLkin Classic &#xff08;2&#xff09;仿真器配置 &#xff08;3&#xff09;PLC程序…

我终于搭建完成了我的个人网站!(仅分享,非教程)

先看看我的个人网站~ https://yaoqx.pages.devhttps://yaoqx.pages.dev 来看看我搭建的过程吧&#xff01; &#xff08;仅分享&#xff0c;非教程&#xff09; 网站技术 前端框架&#xff1a;Astro主题&#xff1a;Frosti代码托管&#xff1a;Github网页部署&#xff1a;Cl…

Vscode ssh Could not establish connection to

错误表现 上午还能正常用vs code连接服务器看代码&#xff0c;中午吃个饭关闭vscode再重新打开输入密码后就提示 Could not establish connection to 然后我用终端敲ssh的命令连接&#xff0c;结果是能正常连接。 解决方法 踩坑1 网上直接搜Could not establish connectio…

浮点数如何存储

一、浮点数存储格式 符号&#xff08;sign&#xff09; s是符号位&#xff0c;1表示负&#xff0c;0表示正阶码&#xff08;exponent&#xff09; E的作用是对浮点数加权&#xff0c;这个权重是2的E次幂尾数&#xff08;significand&#xff09; M是一个二进制小数 二、举例说…

被爬网站用fingerprintjs来对selenium进行反爬,怎么破?

闲暇逛乎的时候&#xff0c;看到了这个问题&#xff1a; Fingerprintjs实际上就是专门用来识别和追踪浏览器的&#xff0c;要应对起来&#xff0c;确实并非易事。那么&#xff0c;我们要如何应对FingerprintJS的唯一标记技术呢&#xff1f; 接下来&#xff0c;我们将一起来探讨…

【自学深度学习梳理2】深度学习基础

一、优化方法 上一篇说到,使用梯度下降进行优化模型参数,可能会卡在局部最小值,或优化方法不合适永远找不到具有最优参数的函数。 1、局部最小值 梯度下降如何工作? 梯度下降是一种优化算法,用于最小化损失函数,即寻找一组模型参数,使得损失函数的值最小(局部最小值…

【Python体验】第五天:目录搜索、数据爬虫(评论区里写作业)

文章目录 目录搜索 os、shutil库数据爬虫 request、re作业&#xff1a;爬取案例的top250电影的关键信息&#xff08;名称、类型、日期&#xff09;&#xff0c;并保存在表格中 目录搜索 os、shutil库 os 模块提供了非常丰富的方法用来处理文件和目录。 os.listdir(path)&#x…

STM32的外部中断实现按键控制led灯亮灭(HAL库)

一&#xff1a;stm32外部中断概述 1&#xff1a;stm32的外部中断线 STM32的每个IO都可以作为外部中断输入。 STM32的中断控制器支持19个外部中断/事件请求&#xff1a; 线0~15&#xff1a;对应外部IO口的输入中断。 线16&#xff1a;连接到PVD输出。 线17&#xff1a;连接到R…