Assembly Manifest

Assembly 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文件,其是服务端的程序清单。WinSxswindows XP以上版本提供的非托管并行缓存(side-by-side catche里面安装了各种版本的经过强文件名签名的系统库,而上面这个文件<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这个文件夹到你的程序所在的目录,一起发布,就万事大吉啦!

 

 

C++/CLI中有效使用非托管并列缓存

CND8学院 C/C++教程 发布日期:20080709

 



  Visual Studio安装程序会把Visual Studio的共享库放在一个称为"并列缓存(side-by-side cache"的地方,那怎样才能有效地利用它呢?
  
  在文章开头,先看一个示例。在命令行中,创建一个C++源文件,输入例1中的代码。(虽然此处使用的是C++/CLI语法,但不管你是用C++/CLI、托管C++、或本地C++,都不影响要讲解的主题。)
  
  例1lib.cpp
  
   using namespace System;
  public ref class Test
  {
   public:
  void CallMe()
  { 
   Console::WriteLine("called me");
  }
  };
  将其编译为一个托管库程序集:
  
   cl /clr /LD lib.cpp
  在此要多留意,我们是使用了混合模式(/clr)来编译此代码,当然了,假如适当修改,也能以旧式托管C++语法(/clr:oldsyntax)来编译。
  
  下一步,创建一个调用此库的C#程序(例2),当然也可以使用Visual Basic.NET,不过C#更好一点。再与库一起编译:
  
  例2
  
   using System;
  
  class App
  {
   static void Main()
   {
  Test test = new Test();
  test.CallMe();
   }
  }
  
  csc app.cs /r:lib.dll
  运行此程序,会抛出一个异常:
  
   Unhandled Exception:
  System.IO.FileNotFoundException:
  The specified module could not be found.
  (Exception from HRESULT: 0x8007007E)
  at App.Main()
  怎么会这样呢?打开程序所在的目录,库也在那啊。HRESULT的高位字为0x8007,其代表FACILITY_WIN32,也就是说,这是一个Win32错误;低位字以十进制表示为126,在winerror.h中列明其代表ERROR_MOD_NOT_FOUND。假如LoadLibrary不能查找到某个模块,才会返回这个错误结果,因此,现在非常清楚了,这个错误表示不能查找到一个非托管的DLL
  
  为找出库所使用的模块列表,可在ILDASM中加载它,并查看MANIFEST。假如库是通过平台调用加载DLL的,那这些DLL会作为.module条目列出,然而,对这个库来说,你将会发现,它只用到了托管程序集mscorlibMicrosoft.VisualC,两者都在.NET全局程序集缓存(GAC)中。另有一种可能性,在程序集中,还存在着非托管代码,由它调用了非托管库(例如,那些使用托管C++ It Just Works的代码)。
  
  为调查清楚,从ILDASMView菜单中选项Headers,这将会列出库中的PE文件头。向下滚动直至找到导入表(IAT),会得到一份所有从非托管库引入的方法列表。因为库是以混合模式编译的,因此库用到了C运行时库(CRT),从导入表中也确认了这点--它列出了msvcr80.dllmsvcm80.dll,前者是CRTDLL多线程版本,后者是一个包含了一些CRT托管版本的混合模式库。这下非常清楚了,错误产生的原因是Windows找不到这两个库、或其一。
  
  最后,查看%systemroot%system32目录下是否有这些库--但它们不会在那的,此时,你可能会指责Visual Studio安装程序没有把最新版本的CRT安装在自己的电脑上,但实际上,安装程序已经安装了这些CRT--只是不在你原先期待的地方。
  
  并列缓存
  
  Visual Studio安装程序会把Visual Studio的共享库放在一个称为"并列缓存(side-by-side cache"的地方,目录位于%systemroot%WinSxS,且只有SYSTEMAdministrators组成员有写访问权限,其他用户只有读取和运行权限。并列缓存中包含了"程序集"--不是托管程序集,而是非托管的等价物。
  
  在WinSxS目录下,每个程序集都会有一个目录,另外,还有两个目录分别是ManifestsPolicies,其中包含了版本的相关信息。以下两个目录与CRT有关:
  
   x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd
  x86_Microsoft.VC80.DebugCRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_f75eb16c
  显而易见,一个是发布版(Release Build),而另一个是调试版(Debug Build),但重点是,版本号与一个公有密钥权标也是目录名的一部分。假如你查看前一个目录的内容,可看到有msvcm80.dll msvcp80.dll、及msvcr80.dll,它们是被称为"Microsoft.VC80.CRT"非托管程序集8.0.50727.42版本的内容。一个非托管程序集可包含一个或多个文件,而这些文件也可为包含本地代码或COM对象的DLL。一个非托管程序集通常被作为一个单独的单元部署,且其中的所有文件由一个被称为"清单(manifest"XML文件来描述。
  
  清单文件存储在Manifest目录中,且与程序集同名,但是后缀名为 .manifest。这个文件列出了程序集中的所有文件;此外,还有一个文件的文件名也与程序集同名,但是后缀名为 .cat,这是一个已签名的安全编目文件,其包含了程序集中文件的hash值,正是因为它已签名,所以可以防止被篡改,且Windows也能利用这些hash值来检查程序集的任一部分是否在部署后已被篡改。进入讨论组讨论。
  

客户清单文件
  
  一个客户文件(程序或库)能依靠于程序集中的某个文件来构建,但客户文件只会依靠于程序集的某个特定版本来构建,Windows也只会加载所需的特定版本。为标出所需共享程序集的版本,一个可执行文件(程序或库)也必须有一个清单文件(manifest)。链接器在可执行文件生成时,会为其创建一个包含清单信息的文件,因此,假如回过头来看一下前面生成的库的目录,会找到一个名为"lib.dll.manifest"的文件,例3是其的内容。
  
  例3
  
   <?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
  正如大家所看见的,它说明托管程序集lib.dll依靠于Microsoft.VC80.CRT共享并列程序集中的某些文件。尽管这个文件位于库lib.dll的同一目录中,但Windows明显不会用到它,而这与MSDN中写明的有点背道而驰:
  
  (MSDN):你可在应用程序二进制可执行头文件中包含应用程序清单文件……,作为备选方案,也可把一个单独的清单文件放在应用程序可执行文件的同一目录中,操作系统会首先从文件系统中加载此清单文件,并检查可执行文件的资源节。文件系统的版本具有优先权。
  
  然而,完全不是这么回事。对库而言,Windows会忽略清单文件,尽管如此,文档还是给出了怎样解决这个问题的一个线索,清单文件一定要以资源ID2的非托管资源RT_MANIFEST形式绑定到可执行文件。
  
  在此有两种方法:第一种方法是创建一个包含对清单文件引用的资源脚本文件:
  
   #include winuser.h
  2 RT_MANIFEST lib.dll.manifest
  它会在以后通过Windows资源编译器rc.exe编译为一个资源,并通过链接器限定为一个非托管资源。这种方法的问题之处在于,是链接器创建了这个清单文件,因此必须运行两次链接器:一次是为生成清单文件,一次是把资源链接到最终生成文件。例4是一个范例makefile,演示了如何进行操作:
  
  例4
  
   # The main target
  all: app.exe
  
  # A C# process that depends upon a Managed C++ library
  app.exe : app.cs lib.dll
  csc app.cs /r:lib.dll
  # This is the second invocation of the linker, so the object file and
  # manifest will already exist, so they do not need to be rebuilt.
  lib.dll : lib.cpp lib.res lib.obj
  link /DLL /manifest:no /machine:x86 lib.res lib.obj
  lib.res : lib.rc
  rc lib.rc
  # Create a temporary resource script that binds the manifest file to the DLL
  lib.rc : lib.dll.manifest
  type <<$@
  #include winuser.h
  2 RT_MANIFEST lib.dll.manifest
  <<KEEP
  # Create the object file, and invoke the linker to create the manifest file
  lib.dll.manifest lib.obj : lib.cpp
  cl /LD /clr lib.cpp
  另一个方法是使用mt.exe未公开的隐藏选项把资源绑定到最终生成文件上,这也是Visual Studio 2005创建加载的C++库(托管混合模式或非托管模式)时所使用的方法。两个隐藏选项分别为/manifest/outputresource:前者用于指定清单文件名,而后者用于指出将要修改的PE文件及清单资源的资源ID。一般而言,对库来说,资源ID应为2;对程序来说,应为1。请看以下示例:
  
  mt /manifest lib.dll.manifest
  /outputresource:lib.dll;#2
  
  注重此处的区别:/manifest选项后跟的参数是用空格分隔的;而/outputresource选项后跟的参数是用冒号分隔的。明显看得出,这两个选项是由不同的程序员开发的。
  
  一旦你把清单文章绑定到库,Windows就可以判定需加载程序集的正确版本。假如在作出这些修改之后,运行前面的C#程序,也会发现程序运行正常。
  
  假如你生成了一个混合模式(/clr)或纯媒介语言模式(/clr:pure)的托管C++程序,来使用这个混合模式的库,链接器也创建了一个相应的程序清单文件,当此程序运行时,Windows会查找资源ID1的清单文件,或查找名为manifest的相应文件。因为混合模式或纯媒介语言模式程序都用到了CRT,意味着将会在清单文件中提及CRT程序集,所以,在这个特例中,库不需要清单文件。然而,你不应该依靠这个机制,因为在本例中,程序使用同一个非托管程序集作为库是一个偶然情况。进入讨论组讨论。
  

版本重定向
  
  回过头来再看一下为库创建的清单文件,注重程序集所需的版本号给定为8.0.50608.0,再次提醒,Visual Studio 2005安装的程序集是8.0.50727.42,这个叫策略版本重定向。在并列缓存的同级Policies目录中,可找到下面这个文件夹:
  
   x86_policy.8.0.Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_x-ww_77c24773
  注重,除了版本部分,它有着程序集的全名。此文件夹中分别包含了一个策略及安全编目文件,文件名基于将要重定向至的版本号:
  
  8.0.50727.42.policy
  
  这是一个XML文件(见例5)。这个策略文件是针对版本8.0.50727.42的,其也是Visual Studio安装程序所安装的版本。它在<bindingRedirect>中指明了所有将要被重定向至本版本的版本号,例5中表明,对版本号8.0.41204.2568.0.50608.0程序集的所有请求,都会被重定向至8.0.50727.42这个版本。与Fusion(混淆: .NET中的程序集加载技术)不同的是,对并列共享程序集的版本重定向只能是那些生成或修订的版本值之间的变化,不能用于主、副版本值的变化。
  
  例5
  
   <?xml version="1.0" encoding="UTF-8" standalone="yes"?
  <!-- Copyright (r) 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
  那就又带出了一个问题:那为什么需要重定向呢?为什么链接器不在清单文件中直接指定由安装程序安装的程序集版本呢?原因在于,链接器是从导入静态库中获得所需的程序集版本。这又引出了另外一个问题:为什么链接器要为DLL的不同版本使用导入库,而不是安装的那个?原因是,这些安装的都是重要的库。
  
  目前为止的讨论都是针对托管C++编译器(C++/CLI及旧式语法),然而,即便本地C++开发技巧再高,也有可能被这些新"特性"所影响。假如你的代码使用了某个共享的Visual Studio本地库(MFCATLCRT),那么,必须有一个单独的.manifest清单文件,要么绑定至可执行文件,要么只绑定至一个 .exe文件。
  
  结论
  
  以前Microsoft C++编译器及链接器的各个版本所生成的库,都能被Windows加载并运行,但Visual Studio 2005中的版本14,生成的库却无法运行。
  
  此处有两个解决方法:第一种方法是运行链接器两次,一次是生成清单文件,其能编译进非托管资源,接着一次是把这个清单绑定至PE文件。这也是本文所推荐的方法,因为假如在构造一个具有"强名称"的程序集,在第二次调用时,就能提供密钥文件或容器的名称。
  
  另一个方法是,使用mt.exe未公开的选项来修改程序集,然而,假如使用链接器来生成一个"强名称"的程序集,mt.exe的动作会使强名称签名无效,且程序集也不会加载。进入讨论组讨论。

 

 

 

vc2008程序发布指南

vc2008开发的程序的发布方式可以有5种方式:

 

1. 采用静态链接到crtMFC. 只要你拥有组成程序的所有源代码,你就可以采用这种方式,

   这种方式除了程序变大一点,好处多多:

   1) 不必重新发布vc2008基础库vcredist_x86.exe(安装到WinSxS).

   2) 不必产生,嵌入manifest.

   3) 也不把vc2008基础库放在程序所在目录.

 

2. exe(嵌入manifest) + vcredist_x86.exe

 

   确保程序正确产生并嵌入manifest文件,然后把程序和vcredist_x86.exe一起发布.用户先安装

   vcredist_x86.exe(安装到WinSxS),然后程序就能正常运行了.

 

3. exe(嵌入manifest) + 用到的基础库文件放到程序目录(包括库文件本身的manifest文件)

 

   确保程序正确产生并嵌入manifest文件,然后把程序用到的vc2008基础库相关文件复制到程序

   所在目录,这种方式适用于用户没有安装过vcredist_x86.exe,一旦用户安装过vcredist_x86.exe,

   WinSxS中的相关文件遭到破坏,那么即使在程序目录放上所有用到的vc2008基础库,程序也跑

   不起来;WinSxS中的相关文件正常,那么程序目录下的相关文件就是多余的了,删掉它们程序也能

   正常运行.

 

4. exe(自行编写manifest) + vcredist_x86.exe

 

5. exe(自行编写manifest) + 用到的基础库文件放到程序目录(包括库文件本身的manifest文件)

 

 

另外,C:/Program Files/Common Files/Merge Modules 目录下有相应库的集成模块可以直接集成到安装包中去.

 

 

附录:

 

A. 自行编写的manifest文件命名: abc.exe 对应abc.exe.manifest

 

B. 与程序对应的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>                                // VC9 CRT, 基本上所有用vc2008的程序都需要下面一段

    <dependentAssembly>

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

    </dependentAssembly>

</dependency>

 

                                              //用到 VC9MFC,需要加下面一段

<dependency>

    <dependentAssembly>

      <assemblyIdentity type='win32' name='Microsoft.VC90.MFC' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />

    </dependentAssembly>

</dependency>

 

<dependency>                                //想使用windows xp 6.0版本的通用控件,加需要下面一段

    <dependentAssembly>

      <assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*' />

    </dependentAssembly>

</dependency>

 

</assembly>

 

C. 如何确保程序正确产生并嵌入manifest文件?

   - xxxproject > properties > Configuration Properties > Generate Manifest: 确保为Yes

     这个与Configuration Properties >Linker > Manifest File >Generate Manifest都是指同一个设置.

   - Project > Tool Build Order > Manifest Tool确保打勾.

     release版本可以看到有: xxx.exe.intermediate.manifest 生成, 它是由linker生成的,manifest tool嵌入程序的.

     debug版本manifest toolxxx.exe.intermediate.manifest嵌入程序后还会输出一个xxx.exe.embed.manifest,供检查内容是否一样

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/czbever/archive/2009/05/14/4182422.aspx

 

Vista操作系统中通过manifest文件使VC应用程序获得管理员权限

VC编译出来的应用程序在vista下运行,有可能因为权限问题,不能成功运行。
用以下办法,给应用程序添加一个manifest文件,程序运行时系统就会跳出UAC对话框,获得管理权限。

1.打开应用程序的源代码工程

2.添加一个“custom”资源,"resource type"24,把资源ID改为1,然后把以下内容复制到资源内容中保存

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="X86"
    name="mulitray.exe.manifest"
    type="win32"
/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
        <requestedPrivileges>
            <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
        </requestedPrivileges>
    </security>
</trustInfo>
</assembly> 

3.重新编译应用程序,此时会发现,广用程序的图标在vista下会多出一个小盾标志。

 

 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页