使用Process Explorer和Dependency Walker排查C++程序中dll库动态加载失败问题

news2024/11/28 12:41:22

目录

1、exe主程序启动时的库加载流程说明

2、加载dll库两种方式

2.1、dll库的隐式引用

2.2、dll库的动态加载

3、本案例中的问题描述

4、使用Process Explorer和Dependency Walker分析dll库加载失败的原因

4.1、Process Explorer工具介绍

4.2、使用Process Explorer工具查看目标dll库有没有加载起来

4.3、使用Dependency Walker查看xxxpdll.dll库的依赖关系

5、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具案例集锦(正在更新中)https://blog.csdn.net/chenlycly/category_12279968.html?spm=1001.2014.3001.5482        有时在代码中会调用LoadLibrary或者LoadLibraryEx接口去动态加载dll库,可能会因为库的版本不一致导致dll库动态加载失败,我们可以使用Process Explorer和Dependency Walker两个工具去排查库加载失败的原因。本文将详细讲述这类问题的完整排查过程。

1、exe主程序启动时的库加载流程说明

       我们先来大概地看一下exe主程序启动时的库加载流程。

       当我们启动exe主程序时,操作系统根据exe程序的位数,给程序进程分配对应大小的虚拟内存空间。如果是32位exe程序,会分配4GB的虚拟内存。在Windows系统中,默认的用户态内存和内核态各占2GB;在Linux系统中,默认的用户态内存占3GB,内核态内存占1GB。

       接着,操作系统会把exe主程序依赖的dll库都加载到当前程序的进程空间中,待所有dll库加载成功后,才会将exe主程序启动起来,然后代码运行到exe主程序的main函数中,exe主程序开始运行。如果依赖的dll库加载失败,程序启动过程会被终止,exe程序会启动失败。比如exe主程序依赖libcurl.dll库,但exe主程序所在的路径中缺少这个库,则启动exe主程序时会报找不到libcurl.dll库:

程序启动失败。

       找不到依赖的dll库是一种场景,还有一种场景是调用的某个接口在目标dll库中找不到,会报类似如下的错误:

出现这种找不到接口的情况,可能是两种原因:

1)如果在产品业务库中找不到接口,可能是业务库的版本有问题,使用了老版本的dll库,需要使用新版本的库;
2)如果在操作系统的系统库中找不到接口,可能调用的接口是新接口,高版本的操作系统才支持,而当前低版本的操作系统不支持。

       dll和exe二进制文件加载到进程的进程空间中,二进制文件中存放的都是二进制代码(二进制代码与汇编代码是等价的,汇编代码是二进制代码的助记符,所以也可以看成是汇编代码),这些二进制代码是存放在代码段内存中,代码段内存也隶属于所在进程的虚拟内存,是从进程虚拟内存划拨出来的。

2、加载dll库两种方式

         加载dll库有两种方式,一种是隐式引用,一种是调用LoadLibrary或LoadLibraryEx去动态加载。

2.1、dll库的隐式引用

        所谓的隐式引用,就是包含dll库的API头文件,引入dll库对应的.lib库(链接时会用到):

#include "netdll_api.h"
#pragma comment(lib, "netdll.lib")

然后在代码中直接调用dll库的导出接口。对于这种隐式引用的方式,之所以叫隐式,是相对于显式调用LoadLibrary或LoadLibraryEx接口去动态记载的。隐式加载的dll库,在exe程序启动时系统会优先将这些dll加载到进程空间中。

2.2、dll库的动态加载

       有时代码中会调用LoadLibrary或LoadLibraryEx动态加载dll库,然后再调用GetProcAddress函数获取dll库导出函数的地址(函数名称就是函数代码段的首地址),然后再去call这个函数地址,完成函数调用。比如我们通过动态加载shell32.dll库,去动态地获取该库中的API导出接口SHOpenFolderAndSelectItems,相关代码如下所示:

BOOL OpenFolderAndSelFile( CString strFilePath )
{
    LPITEMIDLIST pidl = NULL;
    LPCITEMIDLIST cpidl = NULL;
    LPSHELLFOLDER pDesktopFolder = NULL;
    WCHAR wfilePath[MAX_PATH+1] = { 0 };

    // 主工程已经初始化了COM库,此处不用在初始化了
    //::CoInitialize( NULL );

    if ( SUCCEEDED( SHGetDesktopFolder( &pDesktopFolder ) ) )
    {
        // IShellFolder::ParseDisplayName要传入宽字节
        LPWSTR lpWStr = NULL;
#ifdef _UNICODE
        _tcscpy( wfilePath, strFilePath );
        lpWStr = wfilePath;
#else
        MultiByteToWideChar( CP_ACP, 0, strFilePath.GetData(), -1, wfilePath, MAX_PATH ); 
        lpWStr = wfilePath;
#endif

        HRESULT hr = pDesktopFolder->ParseDisplayName( NULL, 0, lpWStr, NULL, &pidl, NULL );
        if ( FAILED( hr ) )
        {
            pDesktopFolder->Release();
            //::CoUninitialize();
            return FALSE;
        }

        cpidl = pidl;

        // SHOpenFolderAndSelectItems是非公开的API函数,需要从shell32.dll获取
        // 该函数只有XP及以上的系统才支持,Win2000和98是不支持的,考虑到Win2000
        // 和98已经基本不用了,所以就不考虑了,如果后面要支持上述老的系统,则要
        // 添加额外的处理代码
        HMODULE hShell32DLL = ::LoadLibrary( _T("shell32.dll") );
        ASSERT( hShell32DLL != NULL );
        if( hShell32DLL != NULL )
        {
            typedef HRESULT (WINAPI *pSelFun)( LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST  *apidl, DWORD dwFlags );
            pSelFun pFun = (pSelFun)::GetProcAddress( hShell32DLL, "SHOpenFolderAndSelectItems" );
            ASSERT( pFun != NULL );   
            if( pFun != NULL )
            {
                hr = pFun( cpidl, 0, NULL, 0 ); // 第二个参数cidl置为0,表示是选中文件
                if ( FAILED( hr ) )
                {
                    ::FreeLibrary( hShell32DLL );
                    pDesktopFolder->Release();
                    //::CoUninitialize();
                    return FALSE;
                }
            }
            else
            {
                FreeLibrary( hShell32DLL );
                pDesktopFolder->Release();
                return FALSE;
            }

            ::FreeLibrary( hShell32DLL );
        }
        else
        {
            pDesktopFolder->Release();
            //::CoUninitialize();
            return FALSE;
        }

        // 释放pDesktopFolder
        pDesktopFolder->Release();
    }
    else
    {
        //::CoUninitialize();
        return FALSE;
    }

    //::CoUninitialize();
    return TRUE;
}

       以前我们遇到过调用LoadLibrary去动态加载dll库加载失败的问题,而且这个问题还遇到过几次,一次是在自制的安装包程序中加载失败,一次是底层库加载库失败。在这些问题场景中,调用LoadLibrary时传入的是dll库的绝对路径,绝对路径中也有目标dll文件,但就是加载失败。后来为了记录这类问题,专门写了一篇文章,对问题的解决过程做了一个完整的总结!如果由朋友遇到类似的问题,可以参看我写的这篇文章:

查看开源操作系统ReactOS源码,解决dll库动态库加载失败问题(调用LoadLibrary加载失败)https://blog.csdn.net/chenlycly/article/details/129200442

3、本案例中的问题描述

       在某天测试时发现,软件UI层执行了某些业务子系统的操作时,一点反应都没有。正常情况下,服务器会给出一些回应,UI层会显示操作的结果。软件在业务和设计上,一般都是分层的,我们的软件也不例外,软件底层多个业务模块是动态启动的,上层软件可以根据自己的需要去选择要启动的模块。上层将要启动的业务模块信息告诉底层,底层去调用LoadLibrary或者LoadLibraryEx
去启动对应的dll库。以前我们遇到过类似的问题,估计这次又是因为底层的业务库加载失败了,库没有加载起来,导致UI层发出的操作请求到底层后没有处理,所以出现了上述问题。

4、使用Process Explorer和Dependency Walker分析dll库加载失败的原因

       类似的问题,我们以前遇到过,已经有相关的处理经验了,这类问题排查起来比较简单,直接拿Process Explorer和Dependency Walker两工具来分析一下就能知道原因了。

4.1、Process Explorer工具介绍

       Process Explorer是Winternals公司(该公司已经被微软收购)开发的增强版任务管理器软件工具(procexp.exe),功能要比Windows系统自带的任务管理器的功能多很多。使用该工具可以查看服务、进程、线程、模块、句柄、磁盘使用状态、网络使用状态等多类信息。Process Explorer和下面要讲到的Process Monitor都是Winternals公司开发的免费工具,被放置在Sysinternals Suite工具包中。

       Process Explorer的主界面如下所示:

        在日常工作中,我们主要用该工具来查看进程的相关信息,以排查程序运行过程中出现的问题。平时用到的主要功能有:

1)查看进程启动时传入的命令行启动参数;
2)查看进程加载了哪些dll库(通过该功能确定目标dll有没有启动起来),以及这些库的详细信息;
3)查看进程的磁盘和网络使用统计信息;
4)查看进程的各个线程的信息,可以看到各线程的CPU占用比例和线程的函数调用堆栈(在排查进程CPU占用高时会用到,找到占用CPU高的那个线程);
5)查看进程的GPU占用情况(判断程序是否使用GPU硬件编解码);
6)查看进程的虚拟内存的占用情况。
7)查看进程占用的句柄信息,比如文件句柄、线程句柄、注册表句柄等。

4.2、使用Process Explorer工具查看目标dll库有没有加载起来

       要查看进程的库占用情况,如果是第一次使用该工具查看进程加载的dll库列表,需要点击工具栏中的“View DLLs”图标按钮:
 

才会显示目标进程加载的dll库列表。默认情况下显示的是句柄信息。

        在进程列表中,点击选择目标进程,下方就会显示加载了哪些库,然后去查看这些库的信息,比如占用库的路径,占用库的版本。通过库的路径或版本,确定是否加载了正确版本的库。通过查看进程加载的dll库列表得知,底层的xxxpdll.dll不在列表中,所以确定该库没加载起来,应该是加载失败了。

4.3、使用Dependency Walker查看xxxpdll.dll库的依赖关系

        为啥xxxmpdll.dll会加载失败呢?根据经验得知,肯定是xxxpdll依赖的底层库有问题,有可能是该库依赖的底层库找不到,有可能是调用到的接口在底层库中找不到。具体是什么问题,使用Dependency Walker工具打开xxxpdll库文件,查看依赖的库信息便知道了。

       Dependency Walker工具主要用来查看exe或dll文件的库依赖关系,如果依赖的库有问题,在库节点前会议红色(红色一般表示接口缺失或者接口参数不对)或者黄色问号(黄色一般表示对当前系统中找不到该库)标记出来。

       在出问题的电脑上,打开Dependency Walker工具,然后将xxxpdll拖入到Dependency Walker中查看dll库的依赖关系。果然看到了问题:

如果你机器用的是Win10系统,Dependency Walker工具对win10的兼容性不太好,打开exe程序时非常慢,有时会卡好几分钟甚至需要更长的时间,需要耐心等一下。

从上图可以看到,当前的xxxpdll.dll依赖的xxxControl.dll库有问题(xxxControl.dll库节点前面的图标显示红色),具体的问题为:xxxpdll.dll调用xxxControl.dll库中的RegisterRtcLogCallback接口,但该接口在xxxControl.dll中找不到(函数前面显示红色图标),这就是原因所在了!

       于是和维护xxxControl.dll库的同事沟通,RegisterRtcLogCallback是xxxControl.dll中新增的接口,新版本的xxxControl.dll库已经包含了,估计当前产品中使用的还是老版本的xxxControl.dll库。

       于是使用PE查看工具查看xxxControl.dll的时间戳,果然当时用的是老版本,然后将新版本取过来覆盖一下重新运行程序就号了。关于如何查看二进制文件的时间戳,可以参看我之前写的文章:

查看exe和dll等二进制文件时间戳(生成时间)的工具与方法介绍https://blog.csdn.net/chenlycly/article/details/130085431

5、最后

       本文详细讲述了使用Process Explorer和Dependency Walker两工具分析dll库动态加载失败的完整分析过程,给大家提供一个借鉴和参考。我们很有必要去掌握一些诸如Process Explorer、Dependency Walker这些常用的分析工具,使用这些工具去有效地分析软件运行过程中遇到的问题。

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

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

相关文章

Mysql日志系统-mysql serve层

Mysql日志系统-服务层的日志 mysql给我们提供了很多有用的日志有mysql服务层提供的,有innodb引擎层提供的,下表是mysql服务层给我们提供的: 日志类型写入日志的信息二进制日志记录了对MySQL数据库执行更改的所有操作慢查询日志记录所有执行…

【JavaScript】2.JavaScript函数

JavaScript 函数 1. 函数的概念 函数&#xff1a;就是封装了一段可被重复调用执行的代码块 通过此代码块可以实现大量代码的重复使用 2. 函数的使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta na…

定点乘法器优化---华为杯

一. 简介 在本篇文章开始之前&#xff0c;先对上篇文章中的一个错误进行指正一下。在部分积生成的时候&#xff0c;需要计算-2A和2A的值&#xff0c;我的做法就是直接左移了一位&#xff0c;这样就会有一个问题&#xff0c;符号位被移掉了&#xff0c;为什么我的计算结果还是对…

Java Stream API 操作完全攻略:让你的代码更加出色 (三)

前言 Java Stream 是一种强大的数据处理工具&#xff0c;可以帮助开发人员快速高效地处理和转换数据流。使用 Stream 操作可以大大简化代码&#xff0c;使其更具可读性和可维护性&#xff0c;从而提高开发效率。本文将为您介绍 Java Stream 操作的所有方面&#xff0c;包括 gro…

c++中的类继承

面向对象编程的主要目的是之一是提供可重用的代码。开发新项目&#xff0c;尤其是当项目十分庞大时&#xff0c;重用经过测试的代码比重新编写代码要好得多。使用已有的代码可以节省时间&#xff0c;由于已有的代码已被使用和测试过&#xff0c;因此有助于避免在程序中引入错误…

在Github中77k星的王炸AutoGPT,会独立思考,直接释放双手

文章目录1 前言1.1 什么是AutoGPT1.2 为什么是AutoGPT2 AutoGPT部分实例2.1 类似一个Workflow2.2 市场调研2.3 自己写播客2.4 接入客服3 安装和使用AutoGPT3.1 安装3.2 基础用法3.3 配置OpenAI的API3.4 配置谷歌API3.5 配置Pinecone API4.讨论1 前言 迄今为止&#xff0c;Gith…

枚举类注解

一、枚举类&#xff1a; 1.枚举类的使用 类的对象只有有限个&#xff0c;确定的。比如&#xff1a;星期、性别、季节 当需要定义一组常量时&#xff0c;强烈建议使用枚举类。 定义枚举类的方法&#xff1a; 一&#xff1a;jdk5.0之前&#xff0c;自定义枚举类 package com.ypl…

https访问fastdfs图片

引用&#xff1a;https://blog.csdn.net/love8753/article/details/128872320 配置nginx 的SSL模块&#xff1a;https://www.cnblogs.com/ghjbk/p/6744131.html 获取ssl证书 将ssl证书&#xff0c;拷贝到服务器的一个目录下 nginx添加 http_ssl_module 最开始安装的nginx只有 …

Spimes x5.0主题模板全开源源码/Typecho主题模板

☑️ 品牌&#xff1a;Typecho ☑️ 语言&#xff1a;PHP ☑️ 类型&#xff1a;主题模板 ☑️ 支持&#xff1a;PCWAP &#x1f389;有需要的朋友记得关赞评&#xff0c;底部分享获取&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 Spimes x5.0主题模板全开…

【2023 年第十三届 MathorCup 高校数学建模挑战赛】 B 题 城市轨道交通列车时刻表优化问题 详细建模方案及代码实现

2023-4-15 更新了代码 【2023 年第十三届 MathorCup 高校数学建模挑战赛】 B 题 城市轨道交通列车时刻表优化问题 更新信息&#xff1a;2023-4-15 更新了代码 1 题目 列车时刻表优化问题是轨道交通领域行车组织方式的经典问题之一。 列车时刻表规定了列车在每个车站的到达和…

让手机、电视都用上Windows,微软开起了网吧

二十多年前 Windows 95 开启了家用 PC 时代。 Win XP 、Win7 … 一代代经典系统的诞生&#xff0c;Windows的市场份额彻底实现一家独大。 不过随着移动设备的兴起&#xff0c;Windows 的地位显然不如从前。 苹果、Google 都在多平台发力&#xff0c;而微软 Windows Phone 的试…

安全信息和事件管理 (SIEM)工具

组织的 IT 基础架构每天都会生成大量日志数据。这些日志包含重要信息&#xff0c;可提供有关用户行为、网络异常、系统停机、策略违规、内部威胁、法规遵从性等的见解和网络安全情报。 但是&#xff0c;在没有自动日志分析器工具的情况下手动分析这些事件日志和系统日志的任务…

2023年4月中国数据库排行榜:达梦厚积薄发夺探花,亚信、星环勇毅笃行有突破

青山遮不住&#xff0c;毕竟东流去。 生机勃勃的春天送来了2023年4月的 墨天轮中国数据库流行度排行。 本月共有263个数据库参与排名&#xff0c;排行榜前30的数据库中&#xff0c;有13个数据库崭露头角&#xff0c;处于上行趋势&#xff0c;中国数据库行业整体流行度有所增加。…

在云服务器上搭建Tomcat

这里&#xff0c;我使用的是putty和winscp。 具体步骤&#xff1a; 以下是在云服务器上搭建Tomcat的步骤&#xff1a; 1. 在本地电脑上打开winscp&#xff0c;连接到云服务器。将Tomcat程序包上传到服务器上。 2. 登录服务器&#xff0c;在终端中输入以下命令&#xff0c;解…

Nginx概述与配置

一、Nginx概述 1、Nginx的特点 一款高性能、轻量级web服务 • 稳定性高 • 系统资源消耗低高 • 对HTTP并发连接的处理能力 单台物理服务器可支持30000~50000个并发请求 2、 Nginx编译安装 • 安装支持软件 • 创建运行用户、组 • 编译安装Nginx 3、Nginx运行控制 …

mmaction行为识别模型数据集训练

参考文献 https://mmaction2.readthedocs.io/en/latest/supported_datasets.html **MMAction2 支持两种类型的数据格式&#xff1a;帧的格式和视频。**前者广泛应用于TSN等之前的项目中。当 SSD 可用时很快&#xff0c;但无法扩展到快速增长的数据集时。&#xff08;例如&#…

ESP8266 +I2C SSD1306 OLED仿数码管时钟

ESP8266 +I2C SSD1306 OLED仿数码管时钟 📌相关篇《ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟》🎬显示效果:🔖本工程基于Arduino IDE框架下开发。🌿采用esp8266:Nodemcu📑功能组成部分 🌿屏幕驱动显示🌿时间获取🌿自定义字体📓屏幕驱动显示 🍁…

Hadoop Partition函数应用(归档)

一、实例描述 在这个实例里我们使用简单的数据集&#xff0c;里面包含多条数据&#xff0c;每条数据由姓名、年龄、性别和成绩组成。实例要求是按照如下规则归档用户。 1.找出年龄小于20岁中男生和女生的最大分数   2.找出20岁到50岁男生和女生的最大分数   3.找出50岁以上…

神器集合!这12个免费工具可以让您的工作更高效

好的工具&#xff0c;能够帮助我们更高效地完成工作&#xff0c;节省时间和精力; 节省出更多的摸鱼时间&#xff01; 本文将介绍 12 款绝佳的免费效率工具&#xff0c;这些工具可以让你事半功倍&#xff0c;提高工作效率。无论你是一名程序员、设计师、学生还是白领&#xff0c…

微观世界的详细地图:微生物生态位

是什么定义了微生物的栖息地 - 生态位&#xff1f;它是温度、湿度和营养成分等环境因素的组合。很难预测每一个因素的确切贡献度。Bas E. Dutilh研究组 基于生活在一起的微生物群体&#xff0c;重新定义了微生物生态位。无论是在温泉、人体肠道还是深海中&#xff0c;微生物几乎…