VC2005再起祸端?

记得前一段时间,写过一篇文章对比了VC6和VC2005这两个IDE,在这篇文章中,虽然我尽量想保持一个公正的态度去评价这两个Windows平台最伟大的C/C++开发环境,但是最终的结论还是使得我更像是一个VC2005的卫道士,尽管使用VC2005不可避免地会遇到Manifest和VC2005分发包的问题。以前一直以为不过是在最终发布应用程序的时候附带一个VC2005分发包的安装文件就搞定了。但最近在开发过程中,遇到了令我措手不及的问题,令我完全对VC2005分发包又有了新的看法,原来是我太天真了!微软弄出来的东西果然不是盖的,非要弄得你欲仙欲死~
  事情是酱紫的:
  那一天,月黑风高,一个带着眼镜的男人打开一台刚刚安装好系统的干净的电脑,因为明天就是递交的最后期限了,所以今天必须做好模块自测。当这个男人熟练地双击EXE的图标后满心期待自己的作品即将出现在屏幕上,这个时候,微软又出来道歉了---“应用程序非法关闭!为您带来的不便,我们十分抱歉!”。不能啊?这个男人百思不得其解,因为在他的工作机上,一切都运行的很好啊?为什么在测试机上就会Crash呢?首先猜测是因为运行环境有什么没有配置好,但是反复对比,发现测试机和工作机上的运行环境是一模一样的!太神奇了
  被这个问题折磨地不成人形的男人只有求助于同事帮忙,同事潇洒地打开VC2005,添加了这个男人的工程,二话不说,F7一路狂按下去,看见Output窗口最后蹦出Success后,直接将编译出来的DLL共享给这个男人。这个男人赶紧将同事编译出来的DLL拷贝到运行环境下,忐忑不安地再次双击EXE,哇~~更神奇的事情出现了,程序竟然能正确运行了
  为什么?Why?なぜ?难道是最近的RP值太低了???这个男人心想。
  最开始,这个男人还以为自己使用的VC2005是不是和同事用的不一样,毕竟才刚刚重新安装过系统,而同事们也说很有可能,因为他们都用的是VC2005 SP1,说可能是没有打SP1补丁的原因。但是对比后发现,VS2005的版本都是一样的,按理说,自己的机器上也应该已经打好了SP1补丁了啊:

  无奈之下,这个男人只好再将之前安装的VC2005 SP1补丁再重新装了一遍。但是结果还是没有用!折腾了一下午,最后,男人只好使出华丽的必杀技--远程调试!又是一番折腾,一路F9、F5、F11、F10,终于来到了最关键的地方,因为在调试中,男人发现自己的DLL根本没有被应用程序加载,连DllMain都没有进来,那就说明EXE中的LoadLibrary就失败了。果然,LoadLibrary下断,发现返回值为0x0000000,而GetLastErr得到是14001,赶快打开MSDN,嗯,查到14001对应的错误是:ERROR_SXS_CANT_GEN_ACTCTX,其解释是:
The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log for more detail.

  嗯!?Side-by-Side?这个可恶的词儿!莫非是与Manifest有关系,用ResHacker打开DLL一看,是有Manifest信息的啊?唔,有点不对,那个CRT的版本号怎么这么眼生啊!

<assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50727.4053' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />

记得之前的DLL都是8.0.50727.762的啊,而且使用vcredist_x86.exe安装后,本地WinSxS目录生成都是8.0.50727.762的CRT目录,这里怎么会是4053?是不是本地系统上也“恰巧”没有8.0.50727.4053的CRT目录?马上验证!果然啊,果然啊,木有~正式因为WinSxS下没有这个4053版本的CRT目录,所以加载DLL才会失败,因为此时Windows根本没有办法根据Mainfest提供的信息来将CRT库加载进程中,实际上就相当于没有安装VC2005分发包一样。但是很不凑巧的是,此处出问题的是DLL,而不是EXE,EXE在因为Manifest问题而启动失败的时候,至少还会提示“由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题”,看到这个的话,会让人立即意识到是Manifest出了问题而不会走那么多弯路
  悲剧啊,为什么这个男人的VC2005编译出来的Manifest会使用8.050727.4053版本呢?先不管了,先弄一个8.0.50727.4053的VC2005分发包安装一下试试程序能不能正常跑起来。但是怎么能区分不同版本的VC2005分发包呢?微软有说明么?好像以前都没有看到啊?
  狗狗一番,首先发现一篇很有意思的文章:http://www.hc51.com/article/zhzl/135.htm。没有相当有黑客遇到同样的问题啊!这个男人心中窃喜。从这篇文章中,我们可以总结出以下几点:
(2009.10.2下午继续....)
1.VC2005库SP1不止一个版本,至少目前遇到的有8.0.50727.762和8.0.50727.4053两个版本
2.上述两个版本在微软的官网上的日期不一样,特别是8.0.50727.4053是2009年的,而762的则是2007年,下面是截图

8.0.50727.762


8.0.50727.4053
此外,还可以直接将VC2005分发包的EXE中的文件提取出来,来看到到底其是那个版本。具体来说,就是将EXE的后缀名改为.zip(或者.rar),用WinRAR解压后,如果有需要(例如得到*.msi)的话,再进行下一步解压,就可能看到版本信息:




  好的,分析到此告一段落~那么这个问题该怎么解决呢?当然,我们可以使用资源修改工具,例如ResHacker来将编译出来的DLL中的Manifest信息进行修改,改成合适的版本,使得这个版本号与发布产品时使用的VC2005的版本一致。这个办法是肯定可行的,但是每次递交的时候,都要去修改,那岂不是很麻烦?有没有其它简单的办法呢?
  在这个男人的VC2005中搜索8.0.50727.4053的字符串,会发现在以下三个文件有这个字符串:
Find all "8.0.50727.4053", Match case, Subfolders, Find Results 2, "Visual C++ Include Directories"
Cannot access E:\Tools\Microsoft Visual Studio 8\VC\PlatformSDK\common\include
  E:\Tools\Microsoft Visual Studio 8\VC\include\crtassem.h(23):#define _CRT_ASSEMBLY_VERSION "8.0.50727.4053"
  E:\Tools\Microsoft Visual Studio 8\VC\atlmfc\include\atlassem.h(22):#define _ATL_ASSEMBLY_VERSION "8.0.50727.4053"
  E:\Tools\Microsoft Visual Studio 8\VC\atlmfc\include\MFCassem.h(22):#define _MFC_ASSEMBLY_VERSION "8.0.50727.4053"
Matching lines: 3    Matching files: 3    Total files searched: 3423
  可以看到这三个文件分别对应了微软的CRT库/ATL库/MFC库。
  打开其中一个文件E:\Tools\Microsoft Visual Studio 8\VC\include\crtassem.h
////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#ifndef _VC_ASSEMBLY_PUBLICKEYTOKEN
#define _VC_ASSEMBLY_PUBLICKEYTOKEN "1fc8b3b9a1e18e3b"
#endif

#ifndef _CRT_ASSEMBLY_VERSION
#if defined _USE_RTM_VERSION
#define _CRT_ASSEMBLY_VERSION "8.0.50608.0"
#else
#define _CRT_ASSEMBLY_VERSION "8.0.50727.4053"
#endif
#endif

#ifndef __LIBRARIES_ASSEMBLY_NAME_PREFIX
#define __LIBRARIES_ASSEMBLY_NAME_PREFIX "Microsoft.VC80"
#endif
/////////////////////////////////////////////////////////////////////////////////
尝试一下将#define _CRT_ASSEMBLY_VERSION "8.0.50727.4053"改为#define _CRT_ASSEMBLY_VERSION "8.0.50727.762",是不是就可以了呢?但是事实却无情地摧毁了这个男人的信心。因为实际编译出来的DLL中除了8.0.50727.4053这个Manifest信息之外,又多了一个新8.0.5.727.762的节点,但是似乎这样并不能让Windows去按照Manifest策略加载DLL(不是按照“或”的逻辑加载,而是按照“与”的逻辑),只能将4053这个节点删除,只保留762这个节点,才能正确加载DLL。

  后来再狗狗的时候,发现了国外的一篇讨论帖,貌似对这个问题有很好的解释(http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-vc/11078/),其中有一个回复:
the problem was the latest security update for Visual C++ 2005 SP1.
It forces your app to use a newer version of CRT and MFC (8.0.50727.4053
instead of 8.0.50727.762)

To fix this, (my recommended approach) go to add/remove programs (with show
updates checked) and uninstalll KB971090, or (will work, but is a pain) send
them the newest vcredist_x86.exe from the bootstrapper folder on your hard
drive.
基本也说明了本篇文章的目的。但是在这个男人的系统上,并没有这个补丁啊?只有一个KB973923,不知道会不会是因为这个原因:


备注:
1.VC2005SP1的官网:
http://support.microsoft.com/kb/973923/zh-cn
2.VC2005的下的分发包路径:
X:\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86
posted @ 2009-10-02 16:32  芈希有  阅读(3514)  评论(2编辑  收藏  举报