http://blog.163.com/henan_lujun/blog/static/19538333200611485511640/
问题
我们的上个项目是在Win2003下,用VS2005开发的一个MFC的SDI程序。在系统开发完毕后,为了验证各个平台的适应性,首先提取了所有相关的DLL,然后到Windows2000下运行,一切正常;然而到XP下,就出现系统错误,提示“由于应用程序的配置不正确,应用程序无法启动。重新安装应用程序可能会解决这个问题。”如下图;在干净的2003系统上(除了操作系统外,什么都没有),也出现同样的情况。
图1 在XP中的错误提示
解决办法:
这到底是什么问题呢?
解决这个问题的过程,说起来有些漫长,找了不少的资料,最后才在C++饼子堂一网友的指点下,找到问题的原因,原来是是Manifest在作怪,后来又在自己的努力下,摸索出了解决办法,如下:
① 把开发机器上的Windows WinSxS 目录下和VC相关的目录(或者是你用到的目录)拷贝到目标机器对应位置下
② 把开发机器上的Windows WinSxS Manifests 目录下和VC相关的文件(或者是你用到的目录)拷贝到目标机器对应位置下
③ 把开发机器上的Windows WinSxS Policys 目录下和VC相关的目录(或者是你用到的目录)拷贝到目标机器对应位置下
如此以来,就可以在目标机器上运行你的应用程序了。
< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
参考资料
1 版本问题:DLL Hell
从客户的角度,最常见的版本问题就是我们所说的 DLL Hell 问题。简单地讲, DLL Hell 是指当多个应用程序试图共享一个公用组件(如某个动态连接库(DLL)或某个组件对象模型(COM)类)时所引发的一系列问题。最典型的情况是,某个应用程序将要安装一个新版本的共享组件,而该组件与机器上的现有版本不向后兼容。虽然刚安装的应用程序运行正常,但原来依赖前一版本共享组件的应用程序也许已无法再工作。在某些情况下,问题的起因更加难以预料。比如,当用户浏览某些 Web 站点时会同时下载某个 Microsoft ActiveX® 控件。如果下载该控件,它将替换机器上原有的任何版本的控件。如果机器上的某个应用程序恰好使用该控件,则很可能也会停止工作。
在许多情况下,用户需要很长时间才会发现应用程序已停止工作。结果往往很难记起是何时的机器变化影响到了该应用程序。用户可能会回忆起一周前安装了一些东西,但安装与目前看到的状态并没有任何明显的关联。 更糟的是,现在很少有诊断工具帮助用户(或帮助他们的技术支持人员)确定有什么问题。
这些问题的原因是应用程序不同组件的版本信息没有由系统记录或加强。而且,系统为某个应用程序所做的改变会影响机器上的所有应用程序—现在建立完全从变化中隔离出来的应用程序并不容易。
很难建立一个隔离应用程序的一个原因是当前运行时环境只允许单独版本组件或应用程序的安装。这个限制意味着组件的编写者必须以向后兼容的方式编写他们的代码,否则当他们安装新组件的时候会有终止已有应用程序的风险。实际上,如果可能的话,编写永远向后兼容的代码是非常难的。在 .NET 中,side by side 概念是版本问题的核心。"Side by side" 是在同一台机器上同时运行不同版本的相同组件的能力。使用支持并列的组件,编程人员不必努力维护严格的向后兼容,因为不同的应用程序自由使用某个共享组件的不同版本。
2 Windows中的WinSxS目录
随着系统资源越来越丰富,硬盘不那么紧张,为了解决不同程序的版本问题,微软在XP以后的操作系统中引入了一种“支持COM和共享DLL的隔离 ”机制,使用这种新的机制来管理DLL 。这种机制,不仅仅被.NET平台采用,而且也被Native程序采用。
如此一来,在Windows XP以后的系统中,在“Windows”目录下就多了一个名为“WinSxS”(Windows Side-by-Side)的文件夹。系统使用这个文件夹存储各个版本的Windows XP组件,减少因为动态链接库(Dynamic Link Libraries,DLL)引起的配置问题(DLL hell)。组件的多个版本都存储在这个文件夹中。Windows XP允许Win32 API组件和应用程序使用与这些程序在测试时所使用的版本完全一致的Microsoft 组件版本,并且不受其它程序或者操作系统升级的影响。Windows XP通过XML文件来实现这一功能,这些XML文件保存了有关应用程序配置的元数据,例如COM类、接口和类型库。
3什么是Manifest
“Windows XP通过XML文件来实现这一功能,这些XML文件保存了有关应用程序配置的元数据”,这里所说的XML文件,就是Manifest清单文件。
Manifest事实上就是一个以.manifest为后缀的XML文件,用于组织和描述隔离应用程序及并行组件,其内部的信息如<assemblyIdentity>元素则标识着一个唯一的程序集,和其他信息一起,他们用于COM类、接口及库的绑定和激活,而这些信息,以往都是存储在注册表中的。另外,Manifests也制定了组成程序集的文件及Windows类。
4 Manifest的分类
在Windows XP中,事实上是在.NET中,把Manifests分类为如下几种类型:
l 程序集Manifests(Assembly Manifests):主要用于描述程序集,管理程序集的名字、版本、资源、依赖程序集。其中共享程序集的Manifests存储在Windows的WinSxS目录中。私有的程序集Manifests则存可以作为一个资源存储在DLL中,也可以存储在应用程序目录下。
l 应用程序Manifests(Application Manifests):这类Manifests则用于描述隔离应用程序,它管理着此应用程序在运行时要绑定的共享的并行组件的名字、版本。该Manifests可以作为一个文件(.manifest文件)存储在应用程序相同的目录下,也可以作为一种资源嵌入在可执行文件内部(Embed Manifest)。
l 应用程序配置文件(Application Configuration Files):对于并行组件及隔离应用程序来说,使用这种Manifests来“Override and Redirect”所依赖程序集的版本。
l 发行配置文件(Publisher Configuration Files):用于重定向并行组件的版本倒另外一个合适的版本的Manifests。此时,被重定向的新程序集应该和原来的旧程序集具有相同的主.次(majou.minor)版本号。
5 Windows对于Manifest的处理
XP以前版本的windows,会像以前那样执行这个exe文件,寻找相应的dll,没有分别,Manifest只是个多余的文件或资源,dll文件会直接到system32的目录下查找,并且调用。
而XP及其以后的操作系统,则会首先读取Manifest,获得exe文件需要调用的DLL列表(此时获得的,并不直接是DLL文件的本身的位置,而是DLL的manifest)操作系统再根据DLL的Manifest提供的信息去寻找对应的DLL ,这样就可能区别不同版本的同一个DLL文件。
这就说明了为什么我的程序可以在2000下面运行,而在XP及2003上无法运行。
6 VS2005种的Manifest配置
使用Visual Studio 2005以后的一个新问题是,VS2005带的8.0新版的C运行库(VC 8.0 CRT)文件在XP以后支持manifest的Windows版本中被调用时,将会check一下Application自身的Manifest,否则将会拒绝被调用,这也就是说,使用Visual Studio开发的Application,Manifest将是必不可少的(搞不懂MS为啥要这样设置,反正与VS2003.NET不同了,也许除了MS自己说的哪些冠冕堂皇的原因,至少这样一来Linux的Wine模拟要麻烦多了)
不过,如果你的程序是静态链接的,没有使用dll,且只使用了操作系统核心的 Kernel32.dll, User32.dll, Ole32.dll,或ShDocVW.dll 等,那么你可以不需要考虑Manifest, 可以关掉它。此时,在VS2005种中,project的设置必须是Use Standard Windows Libraries、Not Using ATL、No Common Language Runtime support
下面是VS2005中和Manifest相关的设置:
l Project/Properties/Linker/Manifest File/Generate Manifest : 决定是否生成Manifest,如上情况才可以关闭
l Project/Properties/Linker/Manifest File/Allow Isolation : 这个是设置DLL的调用的,并不能决定Manifest是否还是必须的
l Project/Properties/Manifest Tool/Input and Output/Embed Manifest 决定Manifest是个单独的文件还是嵌入到exe内的资源
7 其他
与上面内容相应的是关于如何发布软件的问题,事实上只有VC6.0的CRT库是绑定作为Windows的一部分的,之后从VS.net开始,VC程序制作安装包也是要考虑库文件的,只不过VC6.0时推荐拷入System32,VC.net时推荐放在exe文件local目录,而现在VS2005则还需要考虑Manifest的问题了,看似麻烦,其实也还好,VS自带工具打包,下面一些网址也有讲如何手工做redistribution
参考网址:
http://msdn2.microsoft.com/en-us/library/ms235342.aspx
http://msdn2.microsoft.com/en-us/library/ms235265.aspx
http://msdn2.microsoft.com/en-us/library/8kche8ah.aspx
http://blog.kalmbachnet.de/?postid=54
http://blogs.msdn.com/nikolad/archive/2005/03/18/398720.aspx
示例
如下是我们程序中嵌入到EXE文件内部的manifest清单:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <dependency> <dependentAssembly> <assemblyIdentity type="win32" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b< xmlnamespace prefix ="st1" ns ="urn:schemas-microsoft-com:office:smarttags" />9a1e18e3b"></assemblyIdentity> </dependentAssembly> </dependency> <dependency> <dependentAssembly> <assemblyIdentity type="win32" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity> </dependentAssembly> </dependency> </assembly> |
下图是使用eXeScope看到的EXE文件内部,Manifest文件作为资源的存在事实:
下图,则是Windows WinSxS 下,相应的程序集的存在情况: