C++程序启动时报“0xC000007B”无法启动的问题排查

目录

1、问题描述

2、启动时报缺少运行时库

3、拷贝运行时库后,启动报0xC000007B错误

4、继续分析引发0xC000007B错误原因

5、复盘出问题时所做的操作,最终确定问题

6、对于程序位数和操作系统位数不一致的详细说明

6.1、64位程序的限制

6.2、32位二进制文件不能和64二进制文件混用

6.3、32位程序可以运行在64位系统中

6.4、32位程序和64位程序甚至在注册表路径上也有区别

扫描二维码关注公众号,回复: 14470712 查看本文章

6.5、32位程序和64位程序的虚拟内存大小

6.6、到底是开发32位程序还是64位程序?

7、总结


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=M666https://blog.csdn.net/chenlycly/category_11397492.html        出于调试对比目的,测试同事的机器上需要运行Debug版本的软件,测试同事双击exe主程序后报出多个错误。本文详细介绍一下这些问题的排查过程。

1、问题描述

        近日出于调试对比的目的,测试同事的机器上要运行Debug版本的软件,于是我们开发这边将Debug版本的文件打包给测试同事,但同事双击exe主程序后报出多个错误。今天我们就来介绍一下这些问题的排查解决过程,并详细介绍下与问题相关的细节和知识点。

2、启动时报缺少运行时库

        测试同事双击exe主程序首先报找不到vcruntime140d.dll(140d中的d,是debug的意思,即Debug版本)运行时库的问题,如下所示:

于是开发的同事将Debug版本的运行时库发给测试同事,直接覆盖到exe的目录中,拷贝的Debug版本库如下:

Debug版本的程序在开发的机器上运行是没问题的,是因为开发机器上都安装了Visual Studio,安装Visual Studio时会将运行时库拷贝到系统路径中,所以开发这边运行主程序时可以搜索到运行时库,所以运行没问题。

       测试同事将库拷贝到exe程序的目录中,结果还是有问题,弹出找不到Release版本的msvc100.dll运行时库,估计是有些底层库用的是Release版本的。

我们的程序主要用VS2010和VS2017编译的,其中msvc100.dll中的100对应的就是VS2010,vcruntime140d.dll中的140对应的是VS2017。

3、拷贝运行时库后,启动报0xC000007B错误

        开发同事让测试同事在系统盘中搜一下msvc100.dll库,拷贝到exe的目录中。测试同事到系统路径中找到msvc100.dll,拷贝到exe目录中,再次运行exe主程序,不再报找不到运行时库的问题了,但还是报错,会报如下的错误:


错误提示中给出了错误代码0xC000007B,始终查不出来问题,于是找我去看看到底怎么回事。

        以前遇到过0xC0000022错误码,对应STATUS_ACCESS_DENIED宏,即访问被拒绝。出现访问被拒绝,可能有两个原因,一是没有足够的权限(比如需要管理员权限),二是要操作的文件被占用,访问被拒绝。之前写过一个排查0xC0000022的启动失败案例,可以查看这篇文章:

C++程序启动时报“0xc0000022”无法启动的错误icon-default.png?t=M666https://blog.csdn.net/chenlycly/article/details/1208776100xC000007B错误码之前没遇到过,需要搞清楚是什么意思,于是将STATUS_ACCESS_DENIED串输入到VS2017中,然后go到该定义该宏的头文件中,看看能不能搜到0xC000007B错误码的定义,看看其具体是什么含义。

        将0xC000007B拷贝到VS2017中居然不识别,没法go到定义处。打开上述文章,以前写文章的时候机器装的win7系统,现在机器换成了win10,按照文章中记录的头文件路径,到现在的win10系统上找,居然没找到。

       在C:\Program Files (x86)\Windows Kits路径中找不到10.0的文件夹,只能看到一个8.1文件夹,于是点击去,也找到ntstatus.h头文件,和文章中的路径是类似的:

C:\Program Files (x86)\Windows Kits\8.1\Include\shared\ntstatus.h

 在头文件中好到了0xC000007B错误码的含义:

0xC000007B值对应的宏为STATUS_INVALID_IMAGE_FORMAT,无效的二进制文件格式,可能是二进制文件的版本与操作系统不一致(比如64位程序不能运行在32位系统上),也可能是二进制文件中包含错误(比如二进制文件内容损坏或者被篡改)。

4、继续分析引发0xC000007B错误原因

        根据0xC000007B错误码的原因的说明,可能是二进制文件损坏了,难道是在文件传输的过程中有文件被损坏了?于是让开发同事重新编译了一个代码,重新发了新的压缩包过来,结果还是报0xC000007B错误。同样的版本,在开发机器上是可以运行的,那两边的差异应该主要还是在系统运行时库上。

        于是在开发同事的机器上先将Debug版本程序跑起来,然后使用Process Explorer查看Debug版本程序依赖的系统运行时库的目录。打开Process Explorer,在进程列表中点击目标进程,下方就会显示该目标进程加载的所有dll,目标进程引用的运行时库如下

按照上述界面中显示的路径,到路径中找到这些库:(把所有可能用到的库都带上)

然后将这些dll发到测试同事那边,覆盖后居然就不崩溃了,估计还是运行时库版本不一致导致的,会不会之前开发同事提供的运行时库版本是64位的,与我们当前32位主程序不匹配?C++程序启动时报“0xc0000022”无法启动的错误

5、复盘出问题时所做的操作,最终确定问题

        后来我们又去找测试同事沟通,测试同事说当时开发同事提供的库不全,还少了一个msvcr100.dll,开发同事让其直接在系统盘中搜索一下msvcr100.dll,找到后将之覆盖到Debug版本的exe程序目录中。当即感觉可能就是我们上面猜测的问题。

        测试同事重复了当时的操作,到系统盘中去搜索msvcr100.dll,看到了如下的搜索记录:

仔细一看,测试机器上只有C:\Windows\system32目录下有这个库,测试同事拷贝的就是这个库。这就是问题所在了,C:\Windows\system32目录下存放的都是64位库,放到32位的主程序目录中,肯定是不匹配的,这就是导致0xC000007B错误的原因了,至此终于真相大白了。

6、对于程序位数和操作系统位数不一致的详细说明

6.1、64位程序的限制

        64位程序不能运行在32位操作系统中,只能运行在64位系统中。因为64程序是按64位地址进行寻址的,32位操作系统不支持。

6.2、32位二进制文件不能和64二进制文件混用

       32位二进制文件不能和64二进制文件混用。一个Windows exe程序依赖一堆操作系统的系统dll库,可能会依赖开发者自己编写的一些dll库。对于32位exe或者dll,不能引用64位dll,只能引用32位的dll;对于64位exe或dll,不能引用32位dll,只能引用64位dll。

        如果exe主程序是32位的,则其使用的底层库都必须编译32位版本的。

6.3、32位程序可以运行在64位系统中

       32位程序可以运行在64位系统中,64位系统对32位程序是兼容的。Windows程序底层必然会使用到系统dll库,64位Windows系统为了同时兼容32程序和64位程序,系统库都提供了两个版本,64位的系统dll库放置在目录C:\Windows\System32中,32位的系统dll库则放置在目录C:\Windows\SysWOW64中。

6.4、32位程序和64位程序甚至在注册表路径上也有区别

       甚至注册表的有的路径对32位程序和64位程序也有区分,比如存放64位程序卸载信息的注册表路径为:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

而存放32位程序卸载信息的注册表路径中会多出一个WOW6432Node节点

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall

        下面我们以腾讯QQ的安装注册表信息为例,打开控制面板中已安装的程序列表:

已安装的腾讯QQ在界面中显示的信息,均保存在注册表中,对应注册表的路径为:

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{052CFB79-9D62-42E3-8A15-DE66C2C97C3E}

注册表中保存的所有信息如下:

上图中的InstallLocation节点存放的是QQ的安装路径:D:\Program Files (x86)\Tencent\QQ\
因为QQ是32位程序,所以在注册表节点中有个WOW6432Node节点。

6.5、32位程序和64位程序的虚拟内存大小

        用户双击启动32位exe程序时,系统把32位程序依赖的所有dll库加载到进程空间中,最后再将exe主程序启动起来,系统会给进程分配2^32=4GB的虚拟地址内存,对于Windows系统,一般内核态和用户态各占2GB的大小,对于Linux系统,一般用户态占3GB,内核态占1GB。用户态内存是供应用程序代码使用的,内核态内存则是供操作系统内核使用的。

        对于程序而言,这个用户态的虚拟内存,在运行exe程序时,exe依赖的所有dll文件都会加载到地址空间中,这些二进制文件占用的内存时代码段的内存,都是从进程的虚拟内存中划拨的。如果当前应用程序是大型程序,会占用大量的虚拟内存空间,2GB的用户态虚拟内存可能就不够用了。而系统在加载64位程序时,系统会分配2^64=2^32GB,会分配很大的虚拟地址空间,基本是用不完的。

6.6、到底是开发32位程序还是64位程序?

        这个需要分情况来看的。对于Windows客户端软件,主要看操作系统的位数,现在大部分人都用win10系统了,win10系统基本都是64位的,但还有少部分人在用win7系统,其中有部分win7系统还是32位的,因为32位程序才能兼容所有的系统,可以在所有系统上运行,所以Windows客户端软件都目前基本都选择开发32位版本。但32位程序只有2GB的用户态内存,对于大型软件来说,有限的内存可能不太够用,这是个头疼的事情。

        对于服务器侧的软件,大多数使用的是Linux系统,也有一部分人使用Windows Server服务器系统,但服务器系统一般都是软件开发商自行配置提供给用户的(服务器设备时软件开发上提供的,内置在服务器设备中的操作系统是开发商选定的,即自行控制的),肯定都是选用64位操作系统,所以对于服务器侧的软件,一般都开发64位的,都会有充足的虚拟内存可供使用。

7、总结

        问题一开始毫无头绪,随着排查的慢慢展开与逐步推进,逐渐找到线索并最终定位原因。在排查问题的过程,经历的多个细节,很有参考价值,所以在此给大家分享出来。细节出真知,我会争取写出更多有质量的文章,尽可能地多讲一些细节,详细梳理一下相关知识点。

        最后,感谢大家一直以来的支持与认可,我会持续输出更多的原创文章。

猜你喜欢

转载自blog.csdn.net/chenlycly/article/details/126298265