目的:
探究采用VC运行时库生成的应用程序,当系统中存在多个版本的VC运行时库,如何查找正确的版本,来保证程序的正常执行。
环境:
本文使用的开发环境为Visual Studio2005,语言为C++,使用VC运行时库来生成应用程序。
必备工具:
Dependency Walker
正文:
本人现在所用Windows XP系统中有两个版本的VC运行时库分别是CRT8.0.50727.42和CRT8.0.50727. 4053,其位于目录“C:/WINDOWS/WinSxS/”下。下面来生成一个使用了VC运行时库的小程序。
建立一个名为test.cpp的C++源文件,如下:
打开VS2005自带的命令行工具,输入编译命令:
cl -nologo -c -EHsc -MD -Fotest.obj test.cpp
命令执行完后生成一个名为test.obj的文件,编译选项中-MD表示采用多线程DLL的方式使用VC运行时库。再输入连接命令:
link -nologo -manifest -manifestfile:test.intermediate.manifest -OUT:test.exe test.obj
命令执行完后,可以看到生成了一个名为“test.intermediate.manifest”的清单文件以及“test.exe”的可执行文件。test.intermediate.manifes其实是一个XML文本文件,其内容如下:
很显然其中指明了所依赖的VC运行时库的版本为8.0.50608.0。再继续输入命令:
mt -nologo -manifest test.intermediate.manifest -outputresource:test.exe;1
该命令的作用是将test.intermediate.manifest清单文件嵌入到test.exe可执行文件中。这时test.exe可以正常运行。打开vs2005的集成开发环境,从菜单打开test.exe,可以看到一个树形结构,双击“RT_MANIFEST”的子选项可以看到其内容与test.intermediate.manifest中的一模一样,所以依赖的VC运行时库的版本都是8.0.50608.0。于是问题就出来了,我的系统中根本没有8.0.50608.0版本,为什么test.exe仍可以正常运行呢?
用Dependency Walker打开test.exe可以看到其所依赖的运行时库为“c:/windows/winsxs/x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.4053_x-ww_e6967989/MSVCP80.DLL”,该运行时库的版本为8.0.50727.4053。在微软MSDN网站的一篇文章“Side-by-side Assemblies”中找到这样一句话:“If there is no relevant manifest, the system loads the default version of the assembly”,也就是说当系统中没有指定的VC运行时库时,系统会使用默认的VC运行时库。在MSDN的另外一篇文章“Assembly Versions”中还有一句话“Each assembly version is associated with a version number having four parts separated by periods: major.minor.build.revision….. A version number that changes only in the build or revision parts indicates that the assembly is backward compatible with prior versions.”,也就是说当只有运行时库版本号的后面两部分改变时,该版本是向后兼容的。
从上面的分析中可以推断出以下两点
- test.exe运行时首先在系统中搜索版本为8.0.50608.0的VC运行时库,当没有搜索到所依赖的版本时,系统会调用默认的版本8.0.50727.4053。
- 版本为8.0.50727.4053的VC运行时库对老版本8.0.50608.0向后兼容,所以test.exe完全可以正常运行。
那么系统为什么会选择8.0.50727.4053作为默认的版本呢,系统中还有一个版本8.0.50727.42存在?我们推断系统会在兼容范围内选择版本高的库作为默认的版本。这一点可以在路径“C:/WINDOWS/WinSxS/Policies”中找到答案。在该路径下找到VC80.CRT的文件夹,里面有两个后缀为“.Policy”的文件:8.0.50727.42.policy和8.0.50727.4053.Policy,用文本工具分别打开:
8.0.50727.42.policy的中有一句关键的:
<bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.42"/>
可以看到CRT8.0.50727.42对版本号在8.0.41204.256-8.0.50608.0范围内的运行时库兼容。
而8.0.50727.4053.policy的中关键的有两句:
<bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.4053"/>
<bindingRedirect oldVersion="8.0.50727.42-8.0.50727.4053" newVersion="8.0.50727.4053"/>
可以看到CRT8.0.50727.4053对范围在8.0.41204.256-8.0.50608.0和范围在8.0.50727.42-8.0.50727.4053的版本都兼容,自然系统会选择CRT8.0.50727.4053作为系统默认的VC运行时库啦。
就写到这里吧,希望我的经历对大家有所帮助。