找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 7422|回复: 2

“由于应用程序配置不正确,应用程序未能启动”的问题

[复制链接]
发表于 2009-9-26 15:08:29 | 显示全部楼层 |阅读模式
VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题

根据最新的消息,VS2010,已经把这个由SB设计的SB解决方案,抛弃了。还是期待2010吧。manifest带来的问题,比它能解决的问题,多10倍。


  VC9编译的程序在没有装过VC9(确切的说是.Net Framework3.5)的机器上运行时,如果提示“由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题。”这个错误,那么就说明该程序动态链接了VC9的运行时库,(如果还用到了MFC,那么可能动态链接了VC9的MFC库,同理还有ATL库),以及缺少对应的manifest文件,程序在目标机器上没有找到这些库和配置文件,因此导致了这个错误。出现这种情况的VC9编译器可能存在3个版本,接下来分别阐明:
1、没有打过任何补丁的VS2008
  该版本对应的CRT/MFC/ATL库的版本号为9.0.21022.8,这个版本号在后面会用到。这个版本的程序部署比较简单,直接把VC安装目录下的redist目录(C:\Program Files\Microsoft Visual Studio 9.0\VC\redist)中需要的库以及对应的manifest文件拷贝到执行程序同目录下,这样程序到任何机器上都能够正常运行了。
2、打过SP1补丁的VS2008
  打过该补丁后,系统中存在着两个版本的CRT/MFC/ATL库,版本号分别为9.0.21022.8和9.0.30729.1,这导致了manifest文件中记录的版本号和实际库的版本号不一致(程序要求它们的版本号一致才能运行)。这个版本的程序部署需要两个步骤,首先要使manifest文件中依赖项的版本号与实际库的版本号一致,均为9.0.30729.1,方法是在工程设置中增加一个宏定义_BIND_TO_CURRENT_VCLIBS_VERSION,该宏定义于C:\Program Files\Microsoft Visual Studio 9.0\VC\include\crtassem.h文件中,然后重新编译程序。接下来还是将VC安装目录下的redist目录(C:\Program Files\Microsoft Visual Studio 9.0\VC\redist)中需要的库以及对应的manifest文件拷贝到执行程序同目录下,然后修改manifest文件中依赖项的版本号为9.0.21022.8,这样使得程序误以为该目录下库的版本号为9.0.21022.8(实际上是9.0.30729.1版本),这样程序到任何机器上都能够正常运行了。
3、打过SP1补丁与SP1 ATL 安全更新 (KB973675)的VS2008
  这是最新的更新。在SP1补丁之后,微软又于近日发布了一个用于智能设备的 Microsoft Visual Studio 2008 Service Pack 1 ATL 安全更新 (KB973675), 该补丁又将CRT/MFC/ATL库的版本号升级,为9.0.30729.4148,这次升级比较好,manifest文件与库的版本号一致了,不像SP1一样升级的不彻底。这样只需要在工程设置中增加一个宏定义_BIND_TO_CURRENT_VCLIBS_VERSION,接下来重新编译程序,然后直接把VC安装目录下的redist目录中需要的库以及对应的manifest文件拷贝到执行程序同目录下,这样程序到任何机器上都能够正常运行了。
  顺便提一下,如果不想在发布程序时带上这些库和manifest文件(如果没有必要的话),那么可以采用静态编译CRT和MFC,然后把manifest文件添加到资源中,这样编译出的程序只要一个exe就可以在任何机器上直接运行了。
  参考文章:
1、“应用程序配置不正确,程序无法启动”的解决方法资料收集:http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html
有的时候,你在Visual C++上面经过好几个月的辛勤努力,终于将程序编写完成并且测试完毕,然而当你试图在客户的发布机上运行刚写好的程序时,有可能会碰到类似下面的错误,操作系统告诉你“由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题”:


一般情况下,这个问题都是由于程序不能找到所需要的C运行库(CRT)而引起的。

  在Windows XP SP2以后,Windows引入了Side-by-Side执行的概念,这个概念本来是.NET提出来的,但是Windows后来将这个概念集成到操作系统层面上来了。大家都应该知道Dll Hell的问题,为了解决Dll Hell的问题,Side-By-Side提出不同版本的dll文件可以同时存在于同一个系统里面,而且依赖于不同版本dll的应用程序在运行的时候可以使用到它当初被编译生成的dll。前面的话,有点绕,举个例子:
1.         假定你编写了一个C++程序A,是使用MFC 8.0(这个版本是随着Visual Studio 2005)发布的。
2.         之后你的机器升级了Visual Studio的版本,从2005升级到2008,2008的MFC库是9.0版本的,这个时候你的操作系统里面安装了两个版本的MFC,分别是8.0和9.0。
3.         你在Visual Studio 2008编写了另外一个C++程序B,B依赖与MFC 9.0。
4.         如果你运行程序A的话,操作系统会将MFC 8.0加载到A的进程里面。
5.         如果你这时同时运行程序B,操作系统会将MFC 9.0加载到B的进程里面。这就是Side-by-side的执行概念。

操作系统之所以能够这样做,是因为它在加载程序A和B之前,除了查看PE格式里面A和B所依赖的Dll信息,都会查看A和B的manifest文件。Manifest文件保存了Windows可执行文件(包括exe和dll文件)要运行起来的环境设置信息,文件名一般是可执行文件的文件全名加上.manifest。例如notepad.exe的manifest文件就应该是notepad.exe.manifest。例外有的程序将manifest文件直接嵌入到可执行文件的资源里面了,这也就是为什么有的时候你看不到程序的manifest文件的原因。通常来说,一个manifest文件的内容如下(test.exe.manifest文件):

<?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='Microsoft.VC90.DebugCRT' version='9.0.21022.8'
                        processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
</dependency>
</assembly>

上面的例子里面,就说明这个程序依赖于CRT 9.0,而且是调试版的,CPU架构是32位的CPU。对于将manifest文件嵌入到资源文件的程序我们也有办法看到manifest的信息。
1.         一种是使用mt.exe(Visual Studio自带的manifest处理程序):
mt -inputresource:test.exe;#1 /out:test.manifest
2.         另外一种是使用dumpbin程序将整个exe的内容打印到一个文件,然后用文本编辑器打开,搜索Assem字符串样式就能找到manifest信息:

解决方案
  知道了程序依赖于具体哪一个dll以后,你可以将所依赖的dll拷贝到程序的安装文件夹里面,以CRT库绑定失败为例,介绍解决步骤:
1.         从上例中我们知道程序依赖的Microsoft.VC90.DebugCRT库,版本号是9.0.21022.8,需要32位机器版本的CRT。这个依赖项一般是因为你的程序是调试版,所以Visual Studio在编译的时候,将调试版的CRT加入程序的依赖项。
2.         从Visual Studio的安装文件夹里面将D:"Program Files"Microsoft Visual Studio 9.0"VC"redist"Debug_NonRedist"x86中的Microsoft.VC90.DebugCRT整个文件夹拷贝到应用程序所在的文件夹里面,注意:
a)         如果你的程序依赖的是32位的CRT,则要拷贝x86文件夹里面的Microsoft.VC90.DebugCRT文件夹,如果是先x64程序,则要拷贝x64文件夹里面。
b)         你需要确定Microsoft.VC90.DebugCRT文件夹里面的Microsoft.VC90.DebugCRT.manifest文件里面保存的版本信息而你程序依赖的版本信息匹配,Microsoft.VC90.DebugCRT.manifest里面的版本信息大版本号一定要一致,小版本号一定要等于或者大于你程序依赖的CRT的小版本号。比如上例中,我们的程序是依赖于CRT 9.0.21022.8,而我们的Microsoft.VC90.DebugCRT.manifest的版本是9.0.30729.1,这样是可以的;而8.0.30729.1就会有问题。如果大版本号一样,小版本号不一致的话,一个比较简单的方案就是修改程序的manifest文件,使其互相匹配就可以了。
3.         如果你的程序不是依赖调试版本的CRT,而是release版本的CRT,直接去微软的官方网站下载一个crt redist包安装上就可以了。
 楼主| 发表于 2009-9-26 15:13:21 | 显示全部楼层

Visual Studio中manifest问题

Windows XP 以后系统采用一种新的方式来链接dll。就是manifest.  manifest其实是一段dll。它可以单独存在也可以嵌入到dll/exe中。如果单独存在的时候,就是exe名字后加一个.manifest. 比如Test.exe。那么可以生成一个Test.exe.manifest和它放在一起。我们可以使用工具来生成一个exe需要的manifest。系统会根据Manifest来加载所需要的dll。Windows目录下也多了一个目录叫 WinSXS,这里放了不同版本的系统dll。比如各种不同版本的VC/MFC运行库。这个不同版本不会互相干扰,总的来说。Manifest解决了dll hell问题。
不过随之而来的问题是,大部分的vs.net 2005的用户在新建“win32项目-windows应用程序”的时候,新建的工程都通不过去,出现如下提示: Solution to “MSVCR80D.dll not found”“,没有找到MSVCR80D.dll,因此这个应用程序未能启动。重新安装应用程序可能会修复此问题。”明明是装了VC的。系统也明明有这个MSVRD80D的。为啥呢?因为VCRDXX.dll现在有manefiset检查功能,如果manifest有问题。产生原因有多种:
  • 其一是用户链接了静态库,这个静态库却链接了其他版本的MSVCRT库,检查一下。
  • 其二是VS的问题,编译生成的manifest有问题。 可以从菜单里选择:? Project/Properties/Linker/Manifest File/Generate Manifest?? 改成Yes,让VC生成manifest.
如上情况不起作用。可以参考我从网上找到的一些方法。或者改用静态链接到C++运行库和MFC库。另外,有人说重新编译CRT的库,这个我个人太不推荐。:)
以下是我从网上招的资料,也比较有效:
解决方案一微软对于这个问题应该也有处理,不过感觉不是很人性化。在“属性->配置属性->清单工具->常规“下有一个”使用FAT32解决办法,把它选成是,就可以了。(注意:一定要先配置这个选项,然后再编译工程,要不然还是不好用:)
解决方案二找到你的工程的文件夹,如(myproject),找到其下的myproject\myproject\Debug\ myproject.rec,把它删掉(删掉整个Debug目录也可以),重新编译,搞定!
解决方案三首先找到你的vs.net安装目录(如我的是E:\Program Files\Microsoft Visual Studio 8),定位到Microsoft Visual Studio 8\VC\VCWizards\AppWiz\Generic\Application文件夹,备份这个Application文件夹)
打开html\2052,看到两个文件了吧,就那个AppSettings.htm了,打开,在266行“ ”后回车,然后插入一下内容:
选择你所使用的文件系统:
<INPUT TYPE="radio" CLASS="Radio" checked  NAME="filesystem" ID="FAT32" ACCESSKEY="F" TITLE="FAT32">FAT32(F) <INPUT TYPE="radio" CLASS="Radio"  NAME="filesystem" ID="NTFS" ACCESSKEY="N" TITLE="NTFS">NTFS(N)好,保存关闭,这个改完了,准备下一个。
打开scripts\2052,这里就一个文件,ue打开它,找到138行 var bATL = wizard.FindSymbol(”SUPPORT_ATL”) 其后回车,插入如下内容:
var MFTool = config.Tools("VCManifestTool"); MFTool.UseFAT32Workaround = true; 好,继续找到210行(源文件的210,你加了上边的语句就不是210了)config = proj.Object.Configurations.Item(”Release”)注意这次要在这行“前边”加如下内容:
if(bFAT32) { var MFTool = config.Tools("VCManifestTool"); MFTool.UseFAT32Workaround = true; } 好了,终于都改完了,打开你的vs.net 2005新建一个win32应用程序看看吧,效果还不错吧
http://forums.microsoft.com/MSDN/default.aspx?SiteID=1
manifest原理和用途dll是被动态调用的,所以会被若干个程序共享使用的 但是如果dll在应用程序不知道的情况下升级了、或是被另一个程序更改了,就可能会出现问题,即”DLL Hell”
随着系统资源越来越丰富,硬盘不那么紧张,所以在XP以后的操作系统中,用新的机制来管理DLL
(这种机制,这不仅仅是对于.NET而言,对于普通的Native程序也是一样的)
Madifest是个XML的描述文件,对于每个DLL有DLL的Manifest文件,对于每个应用程序Application也有自己的Manifest
对于应用程序而言,Manifest可以是一个和exe文件同一目录下的.manifest文件,也可以是作为一个资源嵌入在exe文件内部的(Embed Manifest)
XP以前版本的windows,会像以前那样执行这个exe文件,寻找相应的dll,没有分别Manifest只是个多余的文件或资源,dll文件会直接到system32的目录下查找,并且调用
而XP以后的操作系统,则会首先读取Manifest,获得exe文件需要调用的DLL列表(此时获得的,并不直接是DLL文件的本身的位置,而是DLL的manifest)操作系统再根据DLL的Manifest去寻找对应的DLL<因此就可能区别不同版本的同一个DLL文件,或是指定一个程序本身Isolated的DLL>
不过使用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 等。project的设置必须是Use Standard Windows Libraries、Not Using ATL、No Common Language Runtime support
那么你可以不需要考虑Manifest 可以关掉它
VS2005中Menifest相关的设置
  • Project/Properties/Linker/Manifest File/Generate Manifest 决定是否生成Manifest,如上情况才可以关闭
  • Project/Properties/Linker/Manifest File/Allow Isolation 这个是设置DLL的调用的,并不能决定Manifest是否还是必须的
  • Project/Properties/Manifest Tool/Input and Output/Embed Manifest 决定Manifest是个单独的文件还是嵌入到exe内的资源
对于我来说,使用SDL必须是启用DLL的动态链接方式,所以必须开启Manifest,并且让Manifest Embed进入exe比较方便
与上面内容相应的 是关于如何发布软件的问题,事实上只有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
发表于 2011-2-24 17:40:13 | 显示全部楼层
好,终于知道为什么了,
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-11-23 17:37 , Processed in 0.021006 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表