记一次问题排查心得
平时程序运行的好好的,昨天收到一则用户上报,在xp系统下面,程序启动后弹出“应用程序正常初始化(0xc0150002)失败,请单击确定,终止应用程序”.
遇到这个问题后,在自己的XP虚拟机里面调用一把,果然也出现这个问题,接下来记录解决这个问题的全过程。
定位问题
测试同事平时也测过XP系统,但在某些XP系统上面无法启动,考虑可能是某些XP系统上面没有安装对应的VC运行库导致的,于是乎在本机上面分别下载各种版本(05,08,10)的VC运行库,因为我们的开发环境是VS2010,所以最高支持到10即可。
然后就是各种安装、卸载、检测组合情况,最后发现,只有在同时安装05和08的redist才能正常运行,因此,推测出程序中使用了用05和08的VS版本编译出来的库。考虑到现在程序中大部分的库都是自己写的组件,这样的问题只有可能发生在引用到的第三方库上面去。
由于临近发布,想要在不改变程序的基础上解决问题,最快的方法是拷贝05和08中某些影响到程序正常启动的dll,
-
通过depends工具查看exe所依赖的dll,将缺失的dll从外部拷贝进去,发现不行
- 微软原版系统安装好后,是不会自带VC++运行库和.Net Framework的
- LoadLibrary加载dll,如果指定dll给了完整路径,则只会在指定路径下面去寻找,如果没有给完整路径,只给了名称,则依次从应用程序exe当前目录,系统目录(system32),环境变量Path中去寻找dll,找到则加载到内存,没找到则报错。
-
通过安装卸载05和08的redist安装程序,对比WIndows目录下面WinSxs目录的变化情况,猜测05和08安装后具体安装了哪些dll文件,将这些缺失的dll文件手动拷贝进程序当前目录下,发现还是不行
- 通过这种方法的尝试,我知道了VC运行库不仅仅是一些dll,还有配合这些dll一起使用的mainfest,如果仅仅是拷贝dll到应用程序当前目录的话,是不能解决问题的,详细见mainfest文件参考.
-
因为知道如何让程序正常启动,考虑从正常启动的角度去查看在启动过程中加载的dll,由于是在虚拟机里面,因此如何调试在虚拟机里面的程序,就成了下一步的问题
- 如何在VS中远程调试虚拟机中的程序,参考链接 VS2010远程调试
- 通过查看正常情况下的启动,没发现什么异常。通过查看异常情况下的启动,发现程序在加载libcurl.dll时就报错退出了,错误情况,可以参考 LdrpWalkImportDescriptor错误解决,在这里面学到一招,可以通过直接查看dll的二进制源码,以Hex进制查看,搜索VC关键字得知该dll是由哪个版本的编译出来的,如果当前系统没有对应版本的运行库,那就会启动失败。
解决问题
经过种种尝试后,决定在不改变exe的情况下,只能重新编译curl库,为了保证上线的正确性,重新编译curl库需要的源码版本需要和当前使用的curl库源码版本一致,所使用的编译选项也要一致,编译curl参考网上现有教程,如何在Windows平台下面编译libcurl库
Manifest
以下内容来自 What is a Manifest(in Windows)
Mainfest文件可以嵌入Exe或者Dll,也可以以独立文件存在,当为独立文件存在时,命名必须为应用程序的名字,后缀为.manifest.被dll文件或者其他库使用的manifest通常被称为assembly manifests,被嵌入dll中的manifest用来指定dll中调用dll运行时的指定版本依赖.
Mainfest中可以指定程序需要的运行级别(requestedExecutionLevel),指定窗口公用组件版本(Microsoft.Windows.Common-Controls)