2023年11月27日发(作者:)
Microsoft Visual C++ 2008 发布程序的部署问题
VS2008编译的程序在Windows2008环境上运行时经常出现如下问题:
应用程序事件日志如下:
上述两个问题都解决后则可能还出现如下问题:
注意:此时是调用dll出现问题了,而不是运行主程序出错。
主要问题是“找不到从属程序集”,
解决办法如下:将整个“C:ApplicationMicrosoft Visual Studio 9.0VC
”拷贝到exe文件所在目录下,如果exe程序
所涉及调用的dll与该exe文件不在同一目录,那么须要在dll所在目录也拷
贝一份。
详细描述如下:
这个问题有好多Blog和Forum已经讨论过了,但都不详尽,在具体的操作过程
中还是有许多疑问。我摘录并整合了许多网络文章,希望能够做个最终了结。
一、VC2005和VC2008编译出来的程序如何发布
/lf426/archive/2008/04/12/
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: , ,
VC9: , ,
4:如何部署文件?
首先,请选择release版本;在生成可执行文件(exe文件)的时候,会得到相
应的部署文件(manifest文件)。比如,得到文件,就会同时生成
st文件。请将这2个文件放在同一文件夹下。然后,
你需要VC8和VC9的部署文件:st和
st。请到你的VC安装目录下寻找,比如:
C:Program FilesMicrosoft Visual Studio
我这里也把2个部署文件直接贴出来,没装的直接用就是了:
st
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=""
version="8.0.50727.762" processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b">assemblyIdentity>
<file name="" /> <file name="" /> <file
name="" />
assembly>
st
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=""
version="9.0.21022.8"
processorArchitecture="x86"
publicKeyToken="1fc8b3b9a1e18e3b"
/>
<file name="" /> <file name="" /> <file
name="" />
assembly>
然后将VC8的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名
为。
同样将VC9的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名
为。
将这2个文件夹放到与exe文件(及其部署文件)所在目录下就OK了。
至于为什么VC9编译的程序要用VC8的dll,大家可以看看我例程部署文件:
xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<security> <requestedPrivileges> <requestedExecutionLevel level='asInvoker' uiAccess='false' /> requestedPrivileges> security> trustInfo> <dependency> <dependentAssembly> <assemblyIdentity type='win32' name='' version='9.0.21022. 8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> dependentAssembly> dependency> <dependency> <dependentAssembly> <assemblyIdentity type='win32' name='' version='8.0.50727. 762' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> dependentAssembly> dependency> assembly> VC 2008生成出来就需要VC90和VC80的CRT,我们能有什么脾气呢…… 也就是说,还别管你exe文件多大,要保证正常运行,我们需要首先拷贝这8个文件…… 二、“应用程序配置不正确,程序无法启动”的原因 /fairysky/blog/item/ 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目录下,我们能够 看到一个st清单文件(根据编译选项, 见上,vc2008将manifest清单文件嵌入到了exe程序中, st清单文件是一个临时文件,但它的内 容与嵌入到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='' 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文件查找相应的依赖项,在这个 程序中,它依赖于vc90 CRT,因此它会在“C:WINDOWSWinSxS” 和“当前目录”下查找相应的dll库以及manifest文件,(这里指的是xp系统, 不考虑vista系统,具体的参见:程序集搜索顺序) 在我的机器上有2个版本的vc90 CRT(由于安装了vc2008 express sp1) vc90 CRT的dll库位于(9.0.21022.8版本) “C:WINDOWSWinSxSx86__1fc8b3b9a1e18e3b_9.0.21022 .8_x-ww_d08d0375” 相应的manifest文件则位于 “C:WINDOWSWinSxSManifestsx86__1fc8b3b9a1e18e3b _9.0.21022.8_x-ww_st” vc90 CRT的dll库位于(9.0.30729版本) “C:WINDOWSWinSxSx86__1fc8b3b9a1e18e3b_9.0.30729 .1_x-ww_6f74963e” 相应的manifest文件则位于 “C:WINDOWSWinSxSManifestsx86__1fc8b3b9a1e18e3b _9.0.30729.1_x-ww_st” 在这里我们就有一个疑问了,我们的开发环境是vc2008 express sp1,那么我 们的程序链接的CRT版本应该是9.0.30729版本的啊?(这个不是我瞎说的,大 家可以用dependency walker来查看程序实际链接的DLL版本),为什么在 manifest文件中依赖的CRT却是9.0.21022.8版本的? 这里就涉及到一个新的 名词“policy ",操作系统会根据C:WINDOWSWinSxSPolicies x86__1fc8b3b9a1e18e3b_x-ww_b7353f75 文件的内容,进行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="" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="" 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> 如果我们将这个拷贝到其它的机器上(没有安装vc2008 sp1 或Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)),则程 序因为没能找到vc90 CRT,而不能运行,弹出“应用程序配置不正确,程序无 法启动”对话框。 根据参考资料的文章中的内容,对于release版程序,有一个简单的办法就是安 装“vcredist_”,文件大小4M左右,自动安装在“C:WINDOWSWinSxS” 目录下,包含了CRT、MFC、ATL等库的dll和manifest清单文件;整个安装时 间不到1分钟。 如果机器上安装了vc2005/vc2008,则会自动的安装vcredist_程序, 安装后在“控制面板”->“添加删除程序”中有一项“Microsoft Visual c++ 2008 Redistributable - x86 9.0.3.729”(我安装的是Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) 版本) 注意:要根据编译器版本以及vc2005/vc2008是否安装了sp1补丁进行选择对应 的版本 上述的解决办法我称之为共享程序集部署方法,同样的我们也可以采用私有程序 集的部署方式来发布程序。 Helloworld例子的私有程序集的部署方法:(针对release版本,仍然是采用上 面的例子 ,采用参考资料中提到的第2中私有程序集部署方法:将 目录下的manifest文件的版本号修改为9.0.21022.8) 1、将编译后的程序拷贝到一个目录下,假定为d:helloworld 2、将vc安装目录下的redistx86目录下的目录拷贝到 d:helloworld(假定vs安装在C:Program FilesMicrosoft Visual Studio 9.0, 则vc安装目录为C:Program FilesMicrosoft Visual Studio 9.0VC) 3、将目录下的manifest文件的版本号修改为9.0.21022.8 (用记事本打开修改) 最终发布程序的目录结构 D:helloworld |-- |-- |--st |-- |-- |-- 这个时候,程序的manifest文件(已经内嵌到exe中了)依赖的vc90 CRT的版 本号和st文件的版本号对应一致,都是 9.0.21022.8(但是要注意的是,我们的helloworld程序实际上依赖的vc90 CRT 版本是9.0.30729版本,这里只是采用了一种欺骗的方法,因为我们编译时链接 的CRT的版本是9.0.30729版本) 三、发生二个DLL版本错乱的原因 /vcblog/archive/2008/05/15/ px 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 来代替以上四项。 但这个参数在可能会有问题 五、链接多种库的情况 /cpp/v-s/devstudio_macros/visualstudionet/prin /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="" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b">assemblyIdentity> dependentAssembly> dependency> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*">assemblyIdentity> dependentAssembly> dependency> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="" 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 会输出如下信息, /manifestdependency:"type='win32' name='' version='9.0.30729.1' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'" /manifestdependency:"type='win32' 综述,请确保应用程序,所有库函数都使用相同版本的VC DLL
发布评论