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

目录

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.rhkb.cn/news/386907.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Javascript面试基础6【每日更新10】

Gulp gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成 Gulp的核心概念:流 流,简单来说就是建立在面向对象基础上的一种抽象的…

多微信聚合神器:高效沟通,一个界面全搞定!

大家都知道,频繁的来回切换微信,不仅浪费时间,还容易错过重要的信息。 今天,我要向大家推荐一款多微信管理神器——个微管理系统,助你实现统一管理,聚合聊天,让沟通变得更加高效。 1、网页扫码…

基于MindIE实现通义千问Qwen推理加速

一、昇腾开发者平台申请镜像 登录Ascend官网昇腾社区-官网丨昇腾万里 让智能无所不及 二、登录并下载mindie镜像 #登录docker login -u XXX#密码XXX#下载镜像docker pull XXX 三、下载Qwen的镜像 使用wget命令下载Qwen1.5-0.5B-Chat镜像,放在/mnt/Qwen/Qwen1.5-…

【无标题】Git(仓库,分支,分支冲突)

Git 一种分布式版本控制系统,用于跟踪和管理代码的变更 一.Git的主要功能: 二.准备git机器 修改静态ip,主机名 三.git仓库的建立: 1.安装git [rootgit ~]# yum -y install git 2.创建一个…

【策略工厂模式】记录策略工厂模式简单实现

策略工厂模式 1. 需求背景2. 代码实现2.1 定义基类接口2.2 排序策略接口定义2.3 定义抽象类,实现策略接口2.4 具体的排序策略实现类2.5 实现策略工厂类2.6 控制类 3. 启动测试4. 总结 1. 需求背景 现在需要你创建一个策略工厂类,来根据策略实现各种排序…

【JAVA】记录一次前端无能造成的 线上bug

有一个需求是 当方式切换 垫资时 清空 当前所选细单商品 但是前端的奇葩 操作是,只是在页面上清空 细单。 不请求 后台删除 细单 让前端 必须 清空同时 请求后台 删除细单 但是 该前端 技术不行, 嫌麻烦 不做 只好 后台 判断该类型时 进行删除操作…

AutoMQ 开源可观测性方案:夜莺 Flashcat

01 引言 在现代企业中,随着数据处理需求的不断增长,AutoMQ [1] 作为一种高效、低成本的流处理系统,逐渐成为企业实时数据处理的关键组件。然而,随着集群规模的扩大和业务复杂性的增加,确保 AutoMQ 集群的稳定性、高可…

权限管理的概述以及vue开发前端的路由、菜单、按钮权限控制实现方案

1. 权限管理概念 1.1 权限定义 权限管理是确保用户只能访问被授权资源的机制。在计算机系统中,权限通常指对特定数据或功能的访问权。权限的设置和控制对于保护数据安全和系统安全至关重要。 1.2 前端权限控制重要性 前端权限控制是用户与应用交互的第一道防线。…

黑马头条Day12-项目部署_持续集成

一、今日内容介绍 1. 什么是持续集成 持续集成(Continuous integration,简称CI),指的是频繁地(一天多次)将代码集成到主干。 持续集成的组成要素: 一个自动构建过程,从检出代码、…

jdk版本管理利器-sdkman

1.什么是sdkman? sdkman是一个轻量级、支持多平台的开源开发工具管理器,可以通过它安装任意主流发行版本(例如OpenJDK、Kona、GraalVM等等)的任意版本的JDK。通过下面的命令可以轻易安装sdkman: 2.安装 curl -s "https://…

什么是 5G?

什么是 5G? 5G 是第五代无线蜂窝技术,与以前的网络相比,它提供了更高的上传和下载速度、更一致的连接以及更高的容量。5G 比目前流行的 4G 网络更快、更可靠,并有可能改变我们使用互联网访问应用程序、社交网络和信息的方式。例如…

通过 WSL 2 在Windows 上挂载 Linux 磁盘

原文查看 曾为了传输或者共享不同系统的文件频繁地在 Windows 和 Linux 系统之间切换,效率过低,所以尝试通过 WSL 2 在Windows 上挂载 Linux 磁盘。 先决条件 需要在Windows 10 2004 及更高版本(Build 19041 及更高版本)或 Win…

Redis 缓存

安装 安装 Redis 下载: Releases tporadowski/redis (github.com) winr ----services.msc-----将redis 设置为手动(只是学习,如果经常用可以设置为自动) 安装 redis-py 库 pip install redis-py Redis 和 StrictRedis redis-py 提供 Redis 和 Str…

UDP通信 单播,广播,组播

UDP通信实现 #include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); - 参数&#xff1a; struct sockaddr *src_addr, socklen_t *addrlen…

vue基础3

1.推荐好用的第三方框架 BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 1.moment.js 2.dayjs 2.收集表达数据 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>Document</title><…

MyBatis-Plus自动生成代码

目录 前言一. 什么是 MyBatis-Plus1. Mybatis-Plus 的特点2. Mybatis-Plus 结构二. MyBatis-Plus 自动生成步骤1. 数据库准备2. 环境准备(1) 创建一个空的 Spring Boot 工程(2) 导入pom依赖(3) 编辑application.yml文件(4) 在启动类加入 @MapperScan 注解3. 配置代码4. 运行三.…

数据库安全:MySQL安全配置,MySQL安全基线检查加固

「作者简介」:冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础著作 《网络安全自学教程》,适合基础薄弱的同学系统化的学习网络安全,用最短的时间掌握最核心的技术。 这一章节我们需要知道MySQL的安全基线标准和加固方式。 MySQL基线检查 1、更新…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 围棋的气(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

unity3d:TabView,UGUI多标签页组件,TreeView树状展开菜单

概述 1.最外层DataForm为空壳编辑数据用。可以有多个DataForm&#xff0c;例如福利DataForm&#xff0c;抽奖DataForm 2.Menu层为左边栏层&#xff0c;每个DataForm可以使用不同样式的MenuForm预制体 3.DataForm中使用ReorderList&#xff0c;可排列配置 4.有定位功能&#xf…

stl-set

目录 目录 内部自动有序、不含重复元素 关于能不能自己造一个cmp&#xff0c;还挺复杂。 访问&#xff1a;只能用迭代器且受限 添加元素&#xff1a;没有pushback&#xff0c;用insert 复杂度&#xff1a;ologn ​编辑 查找元素find&#xff08;&#xff09;&#xff1…