轻松解决manifest problem
"manifest" 是vc8以来的一个新东西,这个东西似乎给我们带来了无尽的烦恼,本机生成的dll,放到别的机器运行时,即时带上dll,也总是弹出莫名其妙的异常:“应用程序正常初始化失败”,即便是调试启动,也发现根本就无法进入程序代码,在弹出异常对话框后断下,看Output,“'XXX.exe': Loaded 'C:\Documents and Settings\zhaiyongqiang\桌面\XXX\Debug\XXX.exe', Symbols loaded.
'XXX.exe': Loaded 'C:\WINDOWS\system32\ntdll.dll', Symbols loaded (source information stripped).
'XXX.exe': Loaded 'C:\WINDOWS\system32\kernel32.dll', Symbols loaded (source information stripped).
SXS: Unable to resolve storage root for assembly directory x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_c94a3a24 in 2 tries
SXS: RtlGetAssemblyStorageRoot() unable to resolve storage map entry. Status = 0xc0150004
SXS: Unable to resolve storage root for assembly directory x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_c94a3a24 in 2 tries
SXS: RtlGetAssemblyStorageRoot() unable to resolve storage map entry. Status = 0xc0150004”,
这说明在Kernel32.dll出了问题,根本还没有进入应用程序的执行代码。再看调用堆栈,
=》!ntdll.dll!_KeFastSystemCallRet@0()
!ntdll.dll!NtRaiseHardError@24() + 0xc bytes
!_LdrpInitializationFailure@4() + 0x2d bytes
!__LdrpInitialize@12() + 0x26b8d bytes
!_KiUserApcDispatcher@20() + 07 bytes
注意,需要安装symbols才能看到这些信息
这说明在加载dll的时候出了问题--这就是vs2005以后非常恶心的manifest问题,通常是未打SP1补丁时编译的程序,在打了SP1补丁的机器无法运行,或者是使用了Debug编译的程序,在另一台机器上即便安装了vcredist也会异常。。。
这个时候我们用depends工具查看PE文件的时候 ,会发现import dll异常,通常是 MSVCR80D.dll MFC90D.dll等。
最近有一朋友运行我给的一个vc9编译的程序,debug的,我把WinSxS下的
x86_Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_597c3456
x86_Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_f863c71f
x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_2a62a75b
x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_c94a3a24
复制给他仍然不行,后来连Manifest文件夹复制给他,就可以了。
“本以为已经完美解决了这个问题”,不曾想,3天以后,公司要外出演示的一个demo也遇到了这个问题,并且有一个dll是debug的,装了.net framework3.5,vc9 redist仍然不管用,我如法炮制,并且连Policy文件夹也复制上了,于是立竿见影解决了问题。演示回来,带来的是坏消息,那些winsxs的文件无法覆盖对方机器,被系统保护阻挡了。。。于是乎在众目睽睽之下出了洋相。。。总结的教训是,不能乱用山寨方法解决这个问题。
查阅MSDN “About Isolated Applications and Side-by-side Assemblies”一节,
“ Isolated applications and side-by-side assemblies provide a solution that reduces DLL versioning conflicts. They enable applications to safely share assemblies. For more information, see Shared Assemblies.“
这是vs2005 以后由编译器和xp 系统共同作用制作的一个防止”dll hell“的一个解决方案。也许确实可以防止错误dll带来的系统崩溃危险,但是却也造成了用户程序频频异常的噩梦。
咒骂没有用,还是接受这个现实吧,虽然牺牲了用户程序的健壮性,却也带来了系统的稳定性。好吧,现在跟我来解决这个噩梦吧(不要在用山寨方法):
最可行的方法是用 vc2005 或者vc2008 自带的“Setup and Deployment” project type 创建一个发布程序,并且附带“Merge Module”,这个Merge Module就是要安装到 WinSxS正确位置的必须的dll,比如DebugCRT,DebugMFC 等等。
创建了Setup项目后,选择菜单 Project/Add/Merge Module 会弹出一个文件打开对话框,目录指向 “C:\Program Files\Common Files\Merge Modules”,
选定 DebugCRT,DebugMFC等你所需要的那些VC RUNTIME就可以了,安装的时候就会把这些dll安装到WinSxS正确位置上,这是系统服务就不会产生那个直接copy的保护异常了。
我们会发现,安装一些游戏的时候会最后弹出一个 vc2005 redist安装来,其实这个做法很傻。。。而且看起来傻子还不少,哈哈。本来vc 安装可以自动来做这些事情。
另外,如果你要做更复杂的发布程序,也可以用Install Shield 10.5,也可以解决这些问题,不过用起来有些复杂,在Object 标签里就可以添加这些东西,并且可以自动检查依赖,添加到安装文件列表里去。
更多的细节可以查阅 MSDN 中的 “Side-by-side Assembly” 和 “Merge Module” 等相关章节。