VC-"应用程序正常初始化失败"-0xc0150002

http://blog.csdn.net/wjeson/article/details/7945155参考一部分这个网页的办法。

 

最近几天被这个问题困惑了许久。 不禁感叹微软的东东真是越做越烂了,也终于明白了时隔12年大家仍然死守VC6的原因。。 

  用VC2005/VC2008编译的程序,编译时没有任何错误,但是运行时就是提示“应用程序正常初始化失败”!! 查找了各方面资料,做了各种尝试,网上说什么的都有:有让安装vc2005 sp1补丁的;有让安装vcredist_x86.exe的; 有让把CRT库的dll直接拷贝到程序目录的; 有让清理注册表的;有让装.NetFramework新版本的;有让查manifest的; 

  结果我尝试了半天,几乎都是浪费时间。上面最后一条说的还算正确,只是作者把事情描述得太繁琐了。。现在把处理的方法说一下,省得大家再走弯路: 

  1. VC2003、VC2005、VC2008及其后续版本,对底层最基本的CRT、MFC、ATL库都进行了重构,为了避免不同版本的库引起冲突,重构后的库文件一般放在 C://windows/WinSxS 文件夹中,并用特定的文件夹/文件名称进行标识; 

  2. 与VC6不同, VC2003、VC2005、VC2008及其后续版本,引入了manifest清单的概念,即应用程序编译后会同时生成对应的.manifest文件,并将该.manifest文件作为资源编译到dll或者exe中去。.manifest文件实际上是一个XML格式的文本文件,里面记录了dll或exe中要引用的CRT、MFC、ATL库的版本和名称。VC6编译的应用程序对CRT、MFC、ATL的dll都是直接调用,而VC2003、VC2005、VC2008编译的程序都是先查询编译到资源中的manifest中的记录,然后按照记录提供的版本和名称去搜寻对应的CRT、MFC、ATL库以及随库发布的.manifest文件,搜寻的路径包括当前目录、C://windows/WinSxS 等等,如果没有找到对应的库文件,则提示“应用程序正常初始化失败”; 

  3.因此解决这个问题的办法就是:(a)用文本编辑器打开exe或dll对应的.manifest文件,查看它引用的CRT、MFC、ATL库的版本;或者,用UltraEdit直接打开exe或者dll,从资源区中找到编译进去的.manifest信息,找到它引用的CRT、MFC、ATL库的版本;或者,运行程序,当程序弹出“应用程序正常初始化失败”对话框时,在桌面上右键点击“我的电脑”-“管理”-“事件查看器”-“系统”,双击查看其中的记录,可以看到出错的原因是因为缺少了某某版本的CRT、MFC、ATL库,记录下这个版本信息;(b)记录到的库的版本信息一般类似于“Microsoft.VC90.DebugCRT”,之后到C://windows/WinSxS 或者VC200X的安装文件夹中搜索包含这个字符串的文件夹和文件,将搜索到的dll和.manifest文件都拷贝到应用程序所在的文件夹中,其中,.manifest文件必须重命名为“Microsoft.VC90.DebugCRT.manifest”(这里以Microsoft.VC90.DebugCRT为例),这样应用程序就可以正常运行了;(c)注意:库的.manifest文件和dll要一同拷贝到应用程序根目录去,因为应用程序会将编译到内部的manifest信息与外部的.manifest文件进行对比,之后才会对库的dll进行调用。如果只拷贝库的dll文件是没有用的; 

  4.如果本机编译和运行程序都ok,但是将编译好的程序拿到其它机器上确无法运行,则多半也是这个原因。另外,如果提示"应用程序配置不正确",大多也是因为上面所说的CRT、MFC、ATL库版本与应用程序不匹配导致的,可以如法炮制进行解决; 

 

    我实际上解决步骤是:第3步中,A步骤只查看了事件查看器,因为用UE打开dll或者exe查不到关键字“manifest”无法确定版本。用事件查看器确定了是exe在加载哪个dll时发生的错误,因为什么错误,确实是某个dll在调用Microsoft.VC80.CRT.manifest和Microsoft.VC80.MFC.manifest上出了问题,我现在C:\Program Files\Microsoft Visual Studio 8\VC\redist路径下找到这两个文件和他们同一路径下的运行时dll都复制到崩溃的exe路径下,运行后还是崩溃,但在事件查看器中换了提示,提示Microsoft.VC80.CRT.manifest和Microsoft.VC80.MFC.manifest这两个文件有语法错误,我又查找了C://windows/WinSxS文件夹下,找到了类似上面两个文件的的文件,例如:

x86_Microsoft.VC80.MFC_1fc8b3b9a1e18e3b_8.0.50727.762_x-ww_3bf8fa05.manifest

x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.762_x-ww_6b128700.manifest

文件大小都比redist路径下的大,我把这两个文件重命名为Microsoft.VC80.CRT.manifest和Microsoft.VC80.MFC.manifest后重新复制到崩溃的exe中,exe即可正常运行!

       此时我复制到崩溃程序目录下的文件有如下五个:

    mfc80.dll,msvcm80.dll,msvcr80.dll,Microsoft.VC80.CRT.manifest,Microsoft.VC80.MFC.manifest

 后续补充(2014-05-23)

我使用resource hacker程序加载了那个有问题的dll,在资源“24”中找到了manifest信息,是一段xml,如下:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.MFC" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

 需要的两个manifest正是我在上面找到的manifest文件,对应版本后按上面的步骤改名字即可。

*****************************************

注意

主要的原因是没有vs开发环境的运行环境CRT、MFC、ATL库版本的问题

安装相应vs版本运行库即可

方法二:


        写了一个VC6下的MFC程序,结果拷贝到另一台机子上出现了应用程序正常初始化(0xc0150002)失败的问题。然后打开事件查看器,

找到了如下信息:

找不到附属汇编 Microsoft.VC80.DebugCRT,上一个错误是 参照的汇编没有安装在系统上。

Resolve Partial Assembly 为 Microsoft.VC80.DebugCRT 失败。 参考错误消息: 参照的汇编没有安装在系统上。
Generate Activation Context 为 C:\Documents and Settings\jerry\桌面\ProConfigure\Debug\MXML1.dll 失败。 参考错误消息: 操作成功完成。



        可以判断是dll加载的时候出了点问题了,看了下它的manifest文件,其依赖库是Microsoft.VC80.DebugCRT ,这样问题就很明显了,
这个dll是在VS2005下编译的,而别人的机子上没有这个环境,我们所编译生成的应用程序由于缺少必需的Debug版本的VC运行库而发生错误。

        解决方法:到第一台机子上的vs2005的安装目录下,搜索名字中包含串Microsoft.VC80.DebugCRT的文件,共有3个dll文件 msvcm80d.dllmsvcp80d.dllmsvcr80d.dll和一个manifest文件( Microsoft.VC80.DebugCRT.manifest),拷贝到另一台机
子上的工程可执行文件目录下,问题就解决了。


方法其他:

vs2005做的c++,在本机都能运行,然后到虚拟机(.net framework2.0已经安装,还安装了vcredist)

控制台程序--正常;
MFC应用程序(使用静态库)--正常;

MFC应用程序(使用共享DLL)--报错;
窗体应用程序--报错;

错误星信息一样:“由于应用程序不正确,程序不能启动.重新安装应用程序可能会解决这个问题.”

如果是c#做的窗体应用程序的话,在这个虚拟机里是能运行的。
哪位大哥能讲讲是为什么啊?


解答:

首先以及再次感谢Avoid兄,乃素好人呐~ ^-^
终于解决问题了,把经验贴出来,方便以后有人遇到此类问题时解决

Avoid兄给出的链接里的四种方法的可行性说明以及详细运行过程:
关于“由于应用程序不正确,应用程序不能启动......”
方法一基本可行,方法二和方法三也可行,方法四估计不可行。
方法一:
在X:\Microsoft Visual Studio 8\VC\redi
st\Debug_NonRedist\x86\Microsoft.VC80.DebugCRT 下找到了下列文件:
msvcm80d.dll
msvcp80d.dll
msvcr80d.dll
Microsoft.VC80.DebugCRT.manifest //其实就是所有文件-_-!
把这几个文件拷贝到目标机器上,与运行程序同一文件夹,就可以运行那个程序了。

之所以说基本可行是因为那文章中所提到的“或者拷贝到SYSTEM32文件夹下”是不可行的,在这里我去掉了。
还有就是,未必一定得是Microsoft.VC80.DebugCRT,如果你建立的是“windows窗体应用程序”项目就用Microsoft.VC80.DebugCRT,如果你建立的是“MFC应用程序”项目那就用Microsoft.VC80.DebugMFC

其实这个方法以前看到过,不过我搞错文件夹了,一直拖到今天才弄清楚,各位注意了在和Debug_NonRedist同级的文件夹里也有个X86,不是它,别进错了!

方法二和方法三其实是联系在一起的:
方法二:
工程-》属性-》配置属性-》代码生成-》运行时库,将/MD或/MDd 改为 /MT或/MTd,这样就实现了对VC运行时库的静态链接,在运行时就不再需要VC的dll了。注:MDd和MTd是DEBUG版本的,不带小d的是RELEASE版本.

方法三:
工程-》属性-》配置属性-》常规-》MFC的使用,选择"在静态库中使用mfc"
这样生成的exe文件应该就可以在其他机器上跑了。
而这两个方法就是我在主贴里提到的“MFC应用程序(使用静态库)--正常; ”,也就是当你在建立“MFC应用程序”项目时在向导的第二步中选择了“在静态库中使用MFC”,得到的就是方法二和方法三联合起来的效果。

方法四:
在主贴里已经说明了,我虚拟机里是装了vcredist的。
vcredist是微软官方出的一个东西,关于它的说明“Microsoft Visual C++ 2005 SP1 Redistributable Package (x86) 安装在未安装 Visual C++ 2005 的计算机上运行使用 Visual C++ 开发的应用程序所需的 Visual C++ 库的运行时组件。”
但是编的程序仍然无法运行,后来我卸载了它,今天为了验证这四种方法,又重装时,竟然无法安装了!!!无论是最初的版本还是这个月15号出的SP1版!!!微软出它来到底是干什么的...-_-! 希望诸位看官能给个答案...

===========================================================================

http://blog.sina.com.cn/s/blog_4ac0a0d30100vtsz.html

什么是.manifest 文件

[现象]
对这个问题的研究是起源于这么一个现象:当你用VC++2005(或者其它.NET)写程序后,在自己的计算机上能毫无问题地运行,但是当把此exe文件拷贝到别人电脑上时,便不能运行了,大致的错误提示如下:应用程序配置不正确,请重新安装程序……或者是MSVCR80D.dll 没有找到什么的(我记得不是很清楚,不过大致是这样的)

[分析]
看到这样的提示,当然不会傻到重装咯。第一反应应该是什么配置有问题、或者是缺少了什么依赖的库文件;于是我就根据以前Windows缺少库文件的经验,把所有库文件(××.DLL)统统一股脑地复制到当前文件夹下来,满心欢喜以为可以运行了,以运行……@#¥@#%¥……还是挂了。

[探索]
于是开始网上搜索,我Google,我摆渡;渐渐我发现,这一切都和一个叫做***.manifest 类型的文件发生关系,那么到底什么是 .manifest 文件呢?他有什么用,以前为什么没有?

后来,经过艰苦努力,终于得知,原来这一切都是Windows 的Assembly Manifest搞的鬼。这个东东的作用就是为了解决 以前windows上的“Dll 地狱” 问题才产生的新的DLL管理解决方案。大家知道,Dll是动态加载共享库,同一个Dll可能被多个程序所使用,而所谓“Dll 地狱”就是当不通程序依赖的Dll相同,但版本不同时,由于系统不能分辨到底哪个是哪个,所以加载错了Dll版本,然后就挂了。于是盖茨就吸取了教训,搞了一个程序集清单的东东,每个程序都要有一个清单,这个清单存再和自己应用程序同名的.manifest文件中,里面列出其所需要的所有依赖,这儿所列出的依赖可不是简单地靠文件明来区分的,而是根据一种叫做“强文件名”的东西区分的,那么什么是强文件明呢?我们来看一下这个.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' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
</dependentAssembly>
</dependency>
</assembly>

我们发现原来这是一个XML格式的文件,其中<dependency>这一部分指明了其依赖于一个名字叫做Microsoft.VC80.CRT的库。但是我们发现,<assemblyIdentity>属性里面还有其它的东东,分别是
type系统类型,version版本号,processorArchitecture平台环境,publicKeyToken公匙(一般用来标示一个公司)……把他们加在一起便成了“强文件名”了,有了这种“强文件名”,我们就可以根据其区分不同的版本、不同的平台……总之,有了这种强文件名,系统中可以有多个不同版本的相同的库共存而不会发生冲突。

[深入]

恩,那么现在,我们就来具体了解一下这一套机制。
首先是强弱文件名的问题。正如上面提到的那样,为了区分不同版本或不同厂商生成的相同的程序集,必须用一个Assembly Manifest程序清单来列出我这个程序集的强文件名--慢着,到这里你可能会问:刚才不是说Assembly Manifest程序清单是列出其所依赖的程序集的强文件名呢,怎么这里变成了当前文件的强文件明了呢?其实,Assembly Manifest程序清单有两部分功能,上面这个实例之所以标注了其所依赖的文件的强文件名是因为其是客户端的Assembly Manifest,在服务端有另外一个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.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
<file name="msvcr80.dll" hash="2a0d797a8c5eac76e54e98db9682e0938c614b45" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>phRUExlAeZ8BwmlD8VlO5udAnRE=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcp80.dll" hash="cc4ca55fb6aa6b7bb8577ab4b649ab77e42f8f91" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>7AY1JqoUvK3u/6bYWbOagGgAFbc=</dsig:DigestValue></asmv2:hash></file>
<file name="msvcm80.dll" hash="55e8e87bbde00d1d96cc119ccd94e0c02c9a2768" hashalg="SHA1"><asmv2:hash xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms><dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity"></dsig:Transform></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></dsig:DigestMethod><dsig:DigestValue>hWq8zazTsMeKVxWFBa6bnv4hEOw=</dsig:DigestValue></asmv2:hash></file>
</assembly>


这个便是从WINDOWS\WinSxS\Manifests目录下取出来的一个manifest文件,再这个文件夹下有一陀子这种XML格式的manifest文件,其是服务端的程序清单。WinSxs是windows XP以上版本提供的[blue]非托管并行缓存(side-by-side catche)[/blue]里面安装了各种版本的经过强文件名签名的系统库,而上面这个文件<assemblyIdentity>正是标注了系统中Microsoft.VC80.CRT的一个版本的强文件名签名,如果其和客户端。.manifest 清单里面<dependentAssembly>所列出的依赖项对上的话,就会被加载。刚才说的side-by-side 是指各种不同的版本并行运行。
上面这个服务端manifest文件中<file>标签具体指明了当前强文件名签名的到底是哪一个文件,其中还有这个文件的Hash签名,以确保文件的完整性。

好了,有了这一套机制,就可以非常非常安全地进行库文件关联了,但是、但是貌似还有一个一直困扰我们的问题:这套机制安全是安全了,但是却失去了以前良好的前后版本兼容性,即如果你的系统库发生了升级,那么服务端的版本号发生了变化,那岂不是所有服务端程序都不能使用了吗?其实,windows还使用一个policy的策略文件来确认映射关系。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright ? 1981-2001 Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity type="win32-policy" name="policy.8.0.Microsoft.VC80.CRT" version="8.0.50727.42" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC80.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"/>
<bindingRedirect oldVersion="8.0.41204.256-8.0.50608.0" newVersion="8.0.50727.42"/>

</dependentAssembly>
</dependency>

</assembly>
 

这便是在WINDOWS\WinSxS\Policies目录下的一个Policy文件,其中<bindingRedirect>标签便指定了所有8.0.41204.256-8.0.50608.0变本的客户需求映射到8.0.50727.42这个我现在系统中安装的比较新的版本的库。当然我们也能对别的字段进行映射,这样便能很好解决系统升级带来的问题。


[应用]
经过以上的讲解,大家对整个依赖查找过程都有了一个整体的认识,那么在实际中问题就好解决了。
让我们回到实际问题中,我之前说了,把一个程序编译连接成可执行程序后,在别人的电脑上发现找不到其所依赖的库了,那么怎么办呢?聪明的你自然想到把其所依赖的库相应的版本拷贝到目标计算机上面,可是……当你在拼命寻找那个可执行文件的assembly manifests文件的时候,却突然发现找不到了,在执行目录下面明明只有一个exe文件嘛。是不是没有生成呢?显然不会,原来是资源连接器把那个assembly manifests文件连接到了可执行文件里面了;不信,你可以用你的vc++打开一个可执行文件看看,在其资源项里面就有一个叫做RT_MANIFEST的项目。这个里面就是二进制标示的manifests文件。那么根据这里面提供的要求,将相应版本的依赖文件(一般就是CRT运行库)拷贝到系统目录Windows\WinSxS\,记住一般会是连带着一个特殊命名的目录一起拷贝到那个文件夹下,比如CRT的运行库就是WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50608.0_x-ww_b7acac55有这样一个目录,其标注了此库的版本号以及签名等信息,以防止多个版本重名时不能复制到同一WinSxS目录下。

这样就搞定了么?如果是以前,那么一切都解决了,系统会在这个目录下面找到这个运行库,可是现在单单这样可不行,系统可是要找到这个运行库的assembly manifests文件,并且对比强文件名之后才能加载,所以所以千万别忘了把相应的manifests文件拷贝到\WinSxS\Manifests目录下面。

当然,这样在目标的系统文件夹下面打动干戈,自然有些过于暴动了,还好,Windows还为我们提供了一种私有查找方式。这种方式会在前面的位置找不到合适库的时候在本地文件夹下面找。所以你只要把之前的库以及那个manifests文件一起拷贝到你的应用程序的路径下面,就可以使用啦。

根据MSDN的说明,在本地查找并加载遵循一下规则:

在应用程序本地文件夹中查找名为 <assemblyName>.manifest 的清单文件。在此示例中,加载程序试图在 appl.exe 所在的文件夹中查找 Microsoft.VC80.CRT.manifest。如果找到该清单,加载程序将从应用程序文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。

尝试在 appl.exe 本地文件夹中打开文件夹 <assemblyName>,如果存在此文件夹,则从中加载清单文件 <assemblyName>.manifest。如果找到该清单,加载程序将从 <assemblyName> 文件夹中加载 CRT DLL。如果未找到 CRT DLL,加载将失败。


最后,我想补充的一点是,在你的VC++安装目录下面的“Microsoft Visual Studio 8\VC\redist”目录下,有着所有的提供发布的已经配备相应.manifest的库文件。所以你想要发布一个程序最简单最安全的做法(不用担心用户电脑是否包含你所需要的库)就是把这个目录下面的相应的库的文件夹和你的可执行文件放在一起发布。
比如在X86平台下如果你的可执行文件用到了CRT库(废话么),那么就拷贝Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT这个文件夹到你的程序所在的目录,一起发布,就万事大吉啦!


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值