“应用程序配置不正确,程序无法启动”详细举例(1)(vc2008 sp1)
来源:http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.htm
内容我就不copy了,大家可以自己去看,总来说产生这个问题的原因可以归结如下:
vc2005/vc2008采用了新的程序部署技术(manifest清单文件),manifest清单文件实际上类似于我们常用的makefile文件,它定义了程序运行的依赖关系(程序运行所需要的dll库的名称、版本等)。
程序运行,首先根据manifest清单文件(这个文件可以嵌入到exe或dll中,或者单独生成外部文件,可以通过vc2005/vc2008的编译选项控制:工程“属性”->“配置属性”->“清单工具”->“输入输出”->“嵌入清单文件”,选择“是”或“否”来控制)来查找程序运行需要的dll库的名称、版本等,如果所在的系统中没有程序运行所需要的dll库和相应的manifest清单文件,则弹出“应用程序配置不正确,程序无法启动”对话框。
另外要注意,由于vc2005/vc2008与.net集成,导致出现一个新的概念:在.net中,将exe、dll都看成“程序集(assemble)”,每个程序集(assemble)都附带有一个manifest清单文件,因此使得vc2005/vc2008的CRT(C 运行时库)、MFC、ATL等dll库都附带有一个manifest清单文件。
归根结底是由于老版本的系统没有我们开发的程序运行所需要的基本运行时库(2k、xp系统只有vc6的一些dll库,而没有vc2005、vc2008所需要的dll库以及相应的manifest清单文件,而在vista系统或者即将到来的windows 7系统上则包含有vc2005、vc2008的dll库和manifest清单文件)
ps:上面的那段话说的有点幼稚和简单了,这里涉及到很多的问题:程序的升级更新、vs的补丁、库的版本问题等等,不是简单的拷贝、粘贴就能解决的。。。
举个例子:(在XP SP3系统下)
使用vc2008 express sp1版(没有mfc、atl),新建一个“HelloWorld”的“win32控制台应用程序”工程,在release下编译,此时默认的编译选项:(在这里我们只关注与我们的问题相关的几个选项)
1、工程“属性”->“配置属性”->“c/c++”->“代码生成”->“运行库”
默认选项为/MD(release)、/MDd(debug),对这几个编译选项不清楚的可以参见: VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结
2、工程“属性”->“配置属性”->“清单工具”->“输入输出”->“嵌入清单文件”
默认选项为“是”(表示将manifest清单文件嵌入到程序中);当然,我们也可以选择“否”,从而单独生成一个manifest清单文件,不过这会增加不必要的依赖项,因此不建议选择“否”。
编译->链接之后在“ HelloWorld ”工程的release或debug目录下,我们能够看到一个HelloWorld.exe.intermediate.manifest清单文件(根据编译选项,见上,vc2008将manifest清单文件嵌入到了exe程序中,HelloWorld.exe.intermediate.manifest清单文件是一个临时文件,但它的内容与嵌入到exe程序的manifest文件是一样的),用文本编辑器打开该文件(用“记事本”也行,不过格式太乱,看不清楚内容,推荐使用vim或其它的文本编辑器查看),大致内容如下:
ps:在网上看到另外的一个方法,用记事本打开exe或dll程序,查看嵌入到exe或dll中的manifest清单文件,方法:“打开记事本,然后将exe或dll拖入到记事本中,当然了,肯定会出现大段的乱码,没关系,直接往后看,就能发现类似于下面的内容的部分”
XML语言: HelloWorld.exe.intermediate.manifest
01 <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
02 <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
03 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
04 <security>
05 <requestedPrivileges>
06 <requestedExecutionLevel level='asInvoker' uiAccess='false' />
07 </requestedPrivileges>
08 </security>
09 </trustInfo>
10 <dependency>
11 <dependentAssembly>
12 <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
13 </dependentAssembly>
14 </dependency>
15 </assembly>
我们重点查看红色部分,这说明编译后的exe程序依赖于vc90(也即vc2008)的CRT(C运行时库),版本9.0.210022.8(这是由于使用/MD选项,程序动态的依赖于CRT,如果使用/MT选项,则会将CRT静态链接到程序中,当然,这会使程序的尺寸急剧的增长,大概有10倍的大小差距)
当exe程序执行时,它会根据嵌入的manifest文件查找相应的依赖项,在这个HelloWorld.exe程序中,它依赖于vc90 CRT,因此它会在“C:\WINDOWS\WinSxS”和“当前目录”下查找相应的dll库以及manifest文件,(这里指的是xp系统,不考虑vista系统,具体的参见:程序集搜索顺序)
在我的机器上有2个版本的vc90 CRT(由于安装了vc2008 express sp1)
vc90 CRT的dll库位于(9.0.21022.8版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375”
相应的manifest文件则位于“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest”
vc90 CRT的dll库位于(9.0.30729版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e”
相应的manifest文件则位于“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e.manifest”
在这里我们就有一个疑问了,我们的开发环境是vc2008 express sp1,那么我们的程序链接的CRT版本应该是9.0.30729版本的啊?(这个不是我瞎说的,大家可以用dependency walker来查看程序实际链接的DLL版本),为什么在manifest文件中依赖的CRT却是9.0.21022.8版本的?这里就涉及到一个新的名词“policy ",操作系统会根据C:\WINDOWS\WinSxS\Policies\x86_policy.9.0.Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_x-ww_b7353f75\9.0.30729.1.policy文件的内容,进行dll版本的跳转(重点看深蓝斜体字部分)从而选择了9.0.30729版本的vc90 CRT (这个所谓的“policy跳转”是道听途说来的,具体的英文资料藏在microsoft的什么地方我就不得而知了。里面夹带了一些我自己的主观猜测,不然的话,没有办法解释manifest版本号9.0.21022.8是,而实际链接的dll的版本号却是9.0.30729)
如果我们将这个HelloWorld.exe拷贝到其它的机器上(没有安装vc2008 sp1或Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)),则程序因为没能找到vc90 CRT,而不能运行,弹出“应用程序配置不正确,程序无法启动”对话框。
根据参考资料的文章中的内容,对于release版程序,有一个简单的办法就是安装“vcredist_x86.exe”,文件大小4M左右,自动安装在“C:\WINDOWS\WinSxS”目录下,包含了CRT、MFC、ATL等库的dll和manifest清单文件;整个安装时间不到1分钟。
如果机器上安装了vc2005/vc2008,则会自动的安装vcredist_x86.exe程序,安装后在“控制面板”->“添加删除程序”中有一项“Microsoft Visual c++ 2008 Redistributable - x86 9.0.3.729”(我安装的是Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) 版本)
注意:要根据编译器版本以及vc2005/vc2008是否安装了sp1补丁进行选择对应的vcredist.exe版本
来源:http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html
参考资料 1、VS2005解决"应用程序配置不正确,程序无法启动"问题 2、VS2005安装文件 "由于应用程序配置不正确,应用程序未能启动" 3、Microsoft Visual C++ 2008发布程序的部署问题 4、VC编写的程序不能在其他机器上运行的解决方案 新增(先看看上面的4个链接之后,遇到问题之后再看下面的几个链接) 5、关于vs2008 sp1 C++生成的 manifest中运行库版本号的问题 (推荐1) 6、在VC++2008的项目中,如何显示地指定要使用的C++库的版本? (推荐2) 7、VC9 SP1 generates manifests with the wrong version number ps:有人认为这是一个bug,并汇报到ms网站上,但“推荐1”认为这不是一个bug 8、VC Runtime Binding...(ms的官方blog对这个问题的解释) 关于VC运行时绑定(上面链接的中文翻译) 9、部署 (C++)(推荐,比较难看懂) 关于链接9下几个比较有用的链接: 程序集搜索顺序(英文),主要讲的是CRT、MFC等的DLL和manifest文件的部署方式 选择部署方法 使用 Program Files\Microsoft Visual Studio 8\VC\Redist目录中提供的文件将特定 Visual C++程序集作为应用程序的私有程序集安装。允许没有管理员权限的用户安装应用程序或可以通过共享运行应用程序时,建议使用这种方法。有关示例,请参见如何:使用 XCopy进行部署。(摘自:选择部署方法)
总结如下: 使用vs2008/vs2008开发的程序有2种部署方法:共享并行程序集和私有程序集部署方法 所谓的共享并行程序集部署方法是指程序依赖的CRT、MFC、ATL的DLL和manifest文件位于目标机器上的c:\windows\winsxs目录中,发布程序的时候只需要将程序拷贝到目标机器上就可以了;私有程序集部署方法指的是发布程序程序的时候,将所依赖的crt、mfc、atl的dll放在程序的当前目录下
对于release版程序 比较的简单的方法是采用共享程序集的方式来部署,安装vcredist.exe (Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) 也可以采用下面debug程序的私有程序集的部署方法
对于debug版本程序 ◆ 若目标机器安装了VS开发环境(vs2005 sp1/vs2008 sp1),则在机器上同时也安装了共享并行程序集,包含各个版本的dll(8.0、9.0版本,位于C:\Windows\Winsxs目录下),则不需做任何的部署,直接将需要发布的程序拷贝到目标机器上就可以了,这和release版程序的发布方式是一样的 ◆ 在没有安装VS开发环境(安装了vs2005 sp1/vs2008 sp1)的机器上,只能采用私有程序集的方式来部署(因为vcredist.exe只安装了release版的CRT、MFC、ATL的DLL和manifest文件,没有对应的debug版本) 已知的2种方法:(针对vs2008 sp1,安装了sp1之后,在系统上会存在两个版本的CRT、MFC、ATL的DLL:9.0.21022.8和9.0.30729.1) 1、使当前程序的manifest文件中的依赖项的版本号与vc安装目录下的redist目录下的dll的版本一致,均为9.0.30729.1 方法: a、在编译项目时定义一个符号_BIND_TO_CURRENT_VCLIBS_VERSION,该符号定义于C:\Program Files\Microsoft Visual Studio 9.0\VC\include\crtassem.h 文件中(假设VC安装在c盘),这样使得编译后的程序的manifest依赖于CRT 9.0.30729.1版本(同样的,对于MFC也应该定义一个类似的符号,大家可以自己在VC的include目录下搜索“9.0.30729.1”或“9.0.21022.8”,就可以找到对应的定义该符号的头文件) b、通过外部工具修改生成的exe或dll中manifest文件(好像windows sdk中的mt.exe可以做到,不过关于这个工具的资料十分的少) 2、将VC安装目录下的redist目录下(C:\Program Files\Microsoft Visual Studio 9.0\VC\redist)的Microsoft.VC90.CRT拷贝到要发布的程序的当前目录下,修改Microsoft.VC90.CRT目录中的Microsoft.VC90.CRT.manifest文件中的版本号,改成9.0.21022.8,这样使得程序误以为该目录下的vc的dll版本是9.0.21022.8(实质上仍然是9.0.30729.1版本)
说明: 1、链接4 的说法是错误的,根据我自己的实验,如果采用私有程序集的部署方法,必须保证manifest文件中的版本号都是相等的,不存在要发布的程序的manifest文件中的版本号大于等于依赖项(CRT、MFC、ATL的dll)的版本号的说法 2、采用共享并行程序集部署方式发布的程序,会自动根据所谓的“policy”(位于C:\WINDOWS\WinSxS\Policies目录下)进行跳转(由低版本号向高版本号跳转);例如程序中的manifest的版本号为9.0.21022.8,而实际上程序是用vc2008 sp1编译的(版本号为9.0.30729.1),在程序实际执行的时候,会根据 x86_policy.9.0.Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_x-ww_037be232目录下的9.0.30729.1.policy文件(可以用记事本打开该文件)中的内容选择9.0.30729.1版本的debugCRT
我个人推荐的阅读顺序:① 先看前面的4个链接,大致有点印象,知道什么是manifest、如何查看manifest文件的内容(能力强的话,也可以自己编写manifest文件)、在vc中如何查看编译过程中生成的manifest文件内容、知道C:\WINDOWS\WinSxS\目录是干什么的、知道vcredist.exe这个程序; ② 再尝试着看看链接7、8、9,这些链接的文章内容十分的晦涩,有的还是英文的,需要有点耐心看; ③ 最后仔细的看看链接5、6,并多多试验(特别推荐链接5,这个链接中的内容十分的详细) |