vs2008 sp1 C++ 发布程序 .

文章来源: http://blog.csdn.net/jtop0/article/details/5759040 
关于VC运行时绑定

写在译文之前:许多人都在纳闷为啥VS2005编译的程序需要安装一个运行时,而2008的却只需要安装2005的运行时呢?也许因此有了本文的诞生。

下面是内容:

原文作者是George Mileka,在VC team里面写库的。原文链接:VC team blog

在四月发布了Visual C++ 2008 Feature Pack之后我们收到了很多来自MVP和用户的反馈。其中一项必须要澄清的问题是在嵌入程序的manifest文件中的依赖项版本的问题。

问题是这样的: 为什么 VS team 提供了如下两个选项:

    (1) 将程序绑定到RTM版本的VC运行时。

    (2) 将程序绑定到当前版本的VC运行时。

那么,哪个是默认的行为呢?

对于VS2005默认行为(不受到SP1的影响)是场景2。

对于VS2008默认是场景1。

reference web:

http://liveck.spaces.live.com/blog/cns!B21C645F6C9CFD52!210.entry?wa=wsignin1.0&sa=935022575  

 

 

 

关于vs2008 sp1 C++ 生成的 manifest 中运行库版本号的问题

一次偶然的发现vs2008 sp1生成manifest文件中vc++ runtime的版本号为9.0.21022.8,而sp1的vc++ runtime的版本应该是9.0.30729.1。
google了一下,发现有人说这是vs2008 sp1的bug,并提交到ms的feedback:VC9 SP1 generates manifests with the wrong version number
然而今天查看了msdn,事实上这并不是一个bug。MSDN:重新分发应用程序并将其绑定到特定库

其中默认绑定行为有下面描述


本节介绍用 Visual C++ 2008 和更高发行版编译的应用程序的默认行为。在编译应用程序时,它会绑定到可用库的原始发行版。即使您的计算机上安装了更高的发行版本,也是如此。例如,如果您的计算机上安装了 Visual C++ 2008 SP1,则在此计算机上编译的任何应用程序都仍将依赖 Visual C++ 2008 的原始发行版。


并附带一个说明


如果您的应用程序依赖最新发行版中引入的功能,则必须分发并重新安装新库,否则应用程序将无法运行。


由此可以发现vs2008 sp1生成的manifest并不是错误的,而且从process explorer中查看应用程序调用的vc++ runtime的版本号你会发现实际上调用的是9.0.30729.1。至于原理请参考MSDN:Visual C++ Libraries as Shared Side-by-Side Assemblies
如果你想强制manifest中vc++ runtime为9.0.30729.1
可以使用下面的代码


如果希望强制应用程序要求在计算机上安装最新的库,请使用下面的一行或多行代码:
#define _BIND_TO_CURRENT_CRT_VERSION 1;
#define _BIND_TO_CURRENT_ATL_VERSION 1;
#define _BIND_TO_CURRENT_MFC_VERSION 1;
#define _BIND_TO_CURRENT_OPENMP_VERSION 1;
借助下面的代码行,可以将应用程序绑定到所有 Visual C++ 库的当前版本。此代码行的效果是上面所有代码行的效果之和。
#define _BIND_TO_CURRENT_VCLIBS_VERSION 1;
说明:
建议您在命令行级别定义这些宏。如果您更愿意在代码中包括这些定义,则必须将它们放在每个编译单元的最前面,例如 stdafx.h 中的前几行。
如果将应用程序绑定到一组库,则在装有这些库的早期版本的计算机上,应用程序将无法运行。如果将应用程序绑定到由最少库组成的特定版本,则建议您随应用程序一起提供所需版本的库。



不过有一个弊端,如果使用了vs2008编译的不带相关宏的静态库,这时manifest中会出现下面这种情况。只有定义相关的宏重新编译所用到的lib才会只有第一个Microsoft.VC90.CRT的描述。


  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.30729.1' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>

 

 

reference web:

http://blog.squallatf.info/%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91/%E5%85%B3%E4%BA%8Evs2008sp1c%E7%94%9F%E6%88%90%E7%9A%84manifest%E4%B8%AD%E8%BF%90%E8%A1%8C%E5%BA%93%E7%89%88%E6%9C%AC%E5%8F%B7%E7%9A%84%E9%97%AE%E9%A2%98/

 

 

 

 

VS2008部署问题

异常处理 2009-09-02 01:47:55 阅读994 评论1 字号:大中小

参考资料

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,这个链接中的内容十分的详细)

 

参考资料: 应用程序配置不正确,程序无法启动 的解决方法资料收集

内容我就不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)

XML语言: 9.0.30729.1.policy

01 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

02 <!-- Copyright (c) Microsoft Corporation. All rights reserved. -->

03 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

04     <assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>

05     <dependency>

06         <dependentAssembly>

07             <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>

08             <bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8" newVersion="9.0.30729.1"/>

09             <bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1" newVersion="9.0.30729.1"/>

10         </dependentAssembly>

11     </dependency>

12 </assembly>

 

如果我们将这个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版本

 

 

上述的解决办法我称之为共享程序集部署方法,同样的我们也可以采用私有程序集的部署方式来发布程序

Helloworld例子的私有程序集的部署方法:(针对release版本,仍然是采用上面的例子

,采用参考资料中提到的第2中私有程序集部署方法:将Microsoft.VC90.CRT目录下的manifest文件的版本号修改为9.0.21022.8)

1、将编译后的程序拷贝到一个目录下,假定为d:/helloworld

2、将vc安装目录下的redist/x86目录下的Microsoft.VC90.CRT目录拷贝到d:/helloworld(假定vs安装在C:/Program Files/Microsoft Visual Studio 9.0,则vc安装目录为C:/Program Files/Microsoft Visual Studio 9.0/VC)

3、将Microsoft.VC90.CRT目录下的manifest文件的版本号修改为9.0.21022.8(用记事本打开修改)

最终发布程序的目录结构

D:/helloworld

      |--helloworld.exe

      |--Microsoft.VC90.CRT

                     |--Microsoft.VC90.CRT.manifest

                    |--msvcm90.dll

                     |--msvcp90.dll

                    |--msvcr90.dll

这个时候,程序的manifest文件(已经内嵌到exe中了)依赖的vc90 CRT的版本号和Microsoft.VC90.CRT.manifest文件的版本号对应一致,都是9.0.21022.8(但是要注意的是,我们的helloworld程序实际上依赖的vc90 CRT版本是9.0.30729版本,这里只是采用了一种欺骗的方法,因为我们编译时链接的CRT的版本是9.0.30729版本)

reference web:

http://tangxingqt.blog.163.com/blog/static/2771087220098214755269/

 

 

D:/发布程序/Microsoft Visual C++ 2008 发布程序的部署问题 - 我的文章 - 电脑工具箱.mht

Microsoft Visual C++ 2008 发布程序的部署问题
 
 

Microsoft Visual C++ 2008 发布程序的部署问题

这个问题有好多Blog和Forum已经讨论过了,但都不详尽,在具体的操作过程中还是有许多疑问。我摘录并整合了许多网络文章,希望能够做个最终了结。

一、VC2005和VC2008编译出来的程序如何发布
http://www.cppblog.com/lf426/archive/2008/04/12/46885.aspx

VC2005和VC2008编译出来的程序放到别人的电脑上为什么有可能无法运行呢?
1:Microsoft Visual C++ 2008 Express Edition可以发布软件吗?
        能!
        很多人说,因为是Express版,不是Studio,所以只是用来练习语言的,不能发布软件——错!
        除了没有MFC和ATL,基本上跟 .net 版本是一样的。发布出来的,是完整的可执行文件。

2:VC 2008 (2005) 发布出来的程序必须附带上他们特定的dll文件吗?
        不一定。
        如果目标系统是个经常升级的系统,微软已经为其打上了所需要的dll文件补丁了,不需要在软件包里面附加特定的dll文件。特别在Vista系统中,你更是不需要VC8和VC9的dll文件。但是在一些老版本的系统中,这些文件就是必须的。

3:VC2008和VC2005特定的dll文件是哪些?

VC8: msvcm80.dll, msvcp80.dll, msvcr80.dll
VC9: msvcm90.dll, msvcp90.dll, msvcr90.dll

4:如何部署文件?

首先,请选择release版本;在生成可执行文件(exe文件)的时候,会得到相应的部署文件(manifest文件)。比如,得到a.exe文件,就会同时生成a.exe.intermediate.manifest文件。请将这2个文件放在同一文件夹下。然后,你需要VC8和VC9的部署文件:Microsoft.VC80.CRT.manifest和Microsoft.VC90.CRT.manifest。请到你的VC安装目录下寻找,比如:
C:/Program Files/Microsoft Visual Studio 9.0/VC/redist/x86/Microsoft.VC90.CRT
我这里也把2个部署文件直接贴出来,没装的直接用就是了:
Microsoft.VC80.CRT.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<noInheritable></noInheritable>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
<file name="msvcr80.dll" /> <file name="msvcp80.dll" /> <file name="msvcm80.dll" />
</assembly>


Microsoft.VC90.CRT.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <noInheritable/>
    <assemblyIdentity
        type="win32"
        name="Microsoft.VC90.CRT"
        version="9.0.21022.8"
        processorArchitecture="x86"
        publicKeyToken="1fc8b3b9a1e18e3b"
    />
    <file name="msvcr90.dll" /> <file name="msvcp90.dll" /> <file name="msvcm90.dll" />
</assembly>


然后将VC8的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名为Microsoft.VC80.CRT。
同样将VC9的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名为Microsoft.VC90.CRT。
将这2个文件夹放到与exe文件(及其部署文件)所在目录下就OK了。

二、“应用程序配置不正确,程序无法启动”的原因
http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.html

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清单文件)


举个例子:(在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或其它的文本编辑器查看),大致内容如下:

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
     <security>
       <requestedPrivileges>
         <requestedExecutionLevel level='asInvoker' uiAccess='false' />
       </requestedPrivileges>
     </security>
</trustInfo>
<dependency>
     <dependentAssembly>
       <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
     </dependentAssembly>
</dependency>
</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)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32-policy" name="policy.9.0.Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
            <bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8" newVersion="9.0.30729.1"/>
            <bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1" newVersion="9.0.30729.1"/>
        </dependentAssembly>
    </dependency>
</assembly>

如果我们将这个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版本

上述的解决办法我称之为共享程序集部署方法,同样的我们也可以采用私有程序集的部署方式来发布程序。

Helloworld例子的私有程序集的部署方法:(针对release版本,仍然是采用上面的例子

,采用参考资料中提到的第2中私有程序集部署方法:将Microsoft.VC90.CRT目录下的manifest文件的版本号修改为9.0.21022.8)

1、将编译后的程序拷贝到一个目录下,假定为d:/helloworld

2、将vc安装目录下的redist/x86目录下的Microsoft.VC90.CRT目录拷贝到d:/helloworld(假定vs安装在C:/Program Files/Microsoft Visual Studio 9.0,则vc安装目录为C:/Program Files/Microsoft Visual Studio 9.0/VC)

3、将Microsoft.VC90.CRT目录下的manifest文件的版本号修改为9.0.21022.8(用记事本打开修改)

最终发布程序的目录结构

D:/helloworld

      |--helloworld.exe

      |--Microsoft.VC90.CRT

                     |--Microsoft.VC90.CRT.manifest

                    |--msvcm90.dll

                     |--msvcp90.dll

                    |--msvcr90.dll

这个时候,程序的manifest文件(已经内嵌到exe中了)依赖的vc90 CRT的版本号和Microsoft.VC90.CRT.manifest文件的版本号对应一致,都是9.0.21022.8(但是要注意的是,我们的helloworld程序实际上依赖的vc90 CRT版本是9.0.30729版本,这里只是采用了一种欺骗的方法,因为我们编译时链接的CRT的版本是9.0.30729版本)

三、发生二个DLL版本错乱的原因

http://blogs.msdn.com/vcblog/archive/2008/05/15/vc-runtime-binding.aspx
Virtual C++ 2008提供一个选项,可以选择使用那个版本的DLL,是9.0.21022.8还是9.0.30729.1
1. 9.0.21022.8也称RTM版本
2. 9.0.30729.1也称Current当前版本
VC提供ploicy DLL版本策略转发功能,可以将老版本的DLL请求转发到新版本上,这意味着
1. 使用RTM版本的库可以被指向RTM版本或更高版本
2. 使用当前版本的库可以被指向当前版本或以后新出的版本

对于下面情况,一般使用RTM版本的库
alan使用RTM版本的产品,并发布给客户
MS释放出VS SP1, 客户安装并使用了新版本的库
alan发现他的产品有问题,修复并将其再次发给客户
在这种情况下,alan不知道客户使用的是那个版本的库,他发布的产品使用RTM版本的库,如果有策略转发,则使用新版本的库

另外一种情况
jim使用RTM版本的库开发
jim发现RTM版本库中有bug,并将其提交给MS
MS修复,并发布新版本库
jim使用新版本库,工作正常。
jim的产品是否正常依赖于是否使用新版本的库,如果不能正常使用,还不如不让程序运行
因此我们提供多个版本库,并向后兼容。

四、人工指定使用那个版本的库

在编译时,使用编译选项
1. MFC使用 _BIND_TO_CURRENT_MFC_VERSION=1
见afx.h

2. CRT使用 _BIND_TO_CURRENT_CRT_VERSION=1
见 crtdefs.h 和 crtassem.h

总共有如下的设置
#define _BIND_TO_CURRENT_CRT_VERSION 1
#define _BIND_TO_CURRENT_ATL_VERSION 1
#define _BIND_TO_CURRENT_MFC_VERSION 1
#define _BIND_TO_CURRENT_OPENMP_VERSION 1

其实,我们可以用_BIND_TO_CURRENT_VCLIBS_VERSION=1 来代替以上四项。
但这个参数在可能会有问题

五、链接多种库的情况
http://www.codeguru.com/cpp/v-s/devstudio_macros/visualstudionet/print.php/c15611

在现实中,一个应用程序需要链接多个库,这要求所有的库都使用同样版本的VC DLL,否则会这样

 

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>



应用程序即依赖于9.0.30729.1的DLL,还依赖于9.0.21022.8版本的
如何判断库函数的版本呢,使用VC自带的dumpbin

dumpbin /directives <name>.lib
会输出如下信息,
 /manifestdependency:"type='win32'
 name='Microsoft.VC90.CRT'
 version='9.0.30729.1'
 processorArchitecture='x86'
 publicKeyToken='1fc8b3b9a1e18e3b'"
 /manifestdependency:"type='win32'

综述,请确保应用程序,所有库函数都使用相同版本的VC DLL



比较潦草,从不同的地方整理而来。不明白请看原文

 

 

 

 

说明本程序需要引用版本号为9.0.21022.8的MFC库和运行时库。

问题一:为什么程序在运行的时候,载入的不是9.0.21022.8版本的却是9.0.30729.1版本的运行库?这是在哪里进行设置的?如何改变这个版本号?

问题二:(虽然貌似已经解决,但是我想知道的更深一点)
布署的时候,把X:/VS安装目录/VC/redist下对应的运行库和.manifest文件(都是9.0.30729.1版本的)复制到与test1.exe同一目录下。在一台完全干净的新系统中,会出现“应用程序配置不正确,应用程序未能启动”的错误。

后来,经过不断的试验,发现应用程序中的MANIFEST文件指定的是9.0.21022.8版本的运行库,然后我使用9.0.21022.8版本的Microsoft.VC90.CRT.manifest文件替换9.0.30729.1版本的Microsoft.VC90.CRT.manifest,配合上30729版本的MFC90.DLL,程序就可以正常运行了。

如果以上说的还不清楚的话,说的简单一点,就是,对于一个使用了新版本DLL功能的应用程序,有以下情况:
应用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 却指定了旧的版本号] + 新版本的运行库MANIFEST文件 + 新版本的运行库DLL --> 不可以运行
应用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 却指定了旧的版本号] + 旧版本的运行库MANIFEST文件 + 旧版本的运行库DLL --> 不可以运行
应用程序中的MANIFEST文件[使用了使用了新版本DLL的功能 却指定了旧的版本号] + 旧版本的运行库MANIFEST文件 + 新版本的运行库DLL --> 可以运行

注:运行库MANIFEST文件是指Microsoft.VC90.CRT.manifest和Microsoft.VC90.MFC.manifest,运行库DLL是指MSVCR90.DLL MFC90.DLL

 

 

又做了个实验:
程序的清单文件不要内嵌,而使用外置的MANIFEST文件,然后手工把生成的text1.exe.manifest中的version='9.0.21022.8'改成新版本号version='9.0.30729.1' ,就可以达到以下目标了:
应用程序 外置的MANIFEST文件[使用了使用了新版本DLL的功能 却指定了的版本号] + 新版本的运行库MANIFEST文件 + 新版本的运行库DLL --> 可以运行!

不过有个外置的文件,确实不爽啊。。。。。

注意:在Microsoft  Vista下编译的项目 在XP下有可能不能运行!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值