码迷,mamicode.com
首页 > 其他好文 > 详细

C#编译基础知识(三)

时间:2014-07-14 14:30:53      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   使用   strong   

本文章我们将来重点介绍强命名程序集,强命名程序集的出现其实是为解决版本控制问题,比如说,在新版程序集发布后,我们希望在系统中对旧程序集的引用继续保留,而有些地方又可以引用新的程序集,再比如说不同的公司提供了不同功能的程序集,这些类库存放在一个公共目录,有时候可能会出现名称相同的情况。使用强命名程序集可以解决这些问题,一个强命名的程序集是靠公钥标示、程序集版本号、区域属性、程序集名称这四个属性来唯一标识的,这样一来,新发布的库文件版本与前面发布的不同,不同的版本引用可以在元数据里面标识,相互不会受到影响,而且在99.99%的情况下不同公司生成的程序集这四个属性不会全都一样,可以大大降低应用程序部署的风险。

一 强命名程序集的各个部分


版本号

[assembly: AssemblyVersion("1.0.0.2")]

众所周知,程序集的版本是通过程序集级别的定制Attribute来标识的,一个版本号总是包含4个部分,分别是 Major Version(主版本号),MinorVersion(次版本号),Build(构建),Revision(修订),可以按照自己的版本号规划自行指定。构建版本号或修订版本号可以自动生成,此时这里的AssemblyVersion属性值可以是1.0.0.*或1.0.*,当使用这种方式时,自动生成的Build为从2000-01-01起的天数,Revision为从凌晨00:00:00 起的秒数/2。我们举个简单的例子。

using System;
using System.Reflection;

[assembly: AssemblyVersion("1.0.*")]
public class a{
    public static void Main()
    {
        DateTime dtBase=new DateTime(2000,1,1);
        DateTime dtWeeHours=new DateTime(DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day,0,0,0);  
          
        Console.WriteLine("Build版本号:" + DateTime.Now.Subtract(dtBase).Days);        
        Console.WriteLine("Revision版本号:" + (int)DateTime.Now.Subtract(dtWeeHours).TotalSeconds / 2);                        
    }
}

运行之后的结果如下

bubuko.com,布布扣

dll的版本号如下(修订号相隔1是因为编译时间和运行时间不同,如果用版本号去反算生成时间,就不会有这个问题)

  bubuko.com,布布扣

这样一来就很明确了,Build版本号对应到生成的日期,Revision对应生成的时间,只要两台机器的时间不出问题,自动生成的dll版本永不会重复。在实际项目中我们肯定是把版本信息(上面那段属性声明)放到一个单独的文件中,vs新建项目时会默认生成一个AssemblyInfo.cs文件,这个文件里面就包含这个属性,当然还包含了很多其他的内容,像上面截图中的产品版本,内部名称,文件版本等等,大家可以去了解下。

 区域性

[assembly: AssemblyCultureAttribute("en-Us")]

程序集的区域性是通过上面的定制Attribute来标识的,区域性可以用来标识程序集的语言,如果不指定,则认为是语言中性。为了让应用程序支持多语言,按照微软的官方提倡的做法,应该是把与语言文化有关的内容放到一个单独的资源文件中,分不同的语言提供多个不同的dll,不同的语言之间用区域性来标识,在主程序调用时通过反射的方式来加载(注意不是直接引用,这样一来可以更加方便的部署和发布)。正因为如此,如果某个程序集直接引用了一个区域性不为中性的dll,则会抛出一个警告“CS1607: 程序集生成 -- 引用的程序集“××××”是本地化附属程序集”。

区域性不为中性的程序集被称作附属程序集,可执行文件不能为附属程序集,如果标识的区域性不为空,则编译时会报错“ error CS0647: 发出“System.Reflection.AssemblyCultureAttribute”属性时出错 -- “可执行文件不能是附属程序集,区域性应始终为空””。 除此之外,在运行时加载附属程序集也会与语言中性程序集有所不同,这一点我们在后面应用程序配置中再做深入说明。

  公钥标记

<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

以上内容是从Web.Config中截取到的,最后的那一部分,PublicKeyToken=B77A5C561934E089,等号后面的那部分就是公钥标识,它其实是嵌入在程序集中公钥经过SHA-1散列运算得到的密文的最后8个字节。在继续讲公钥标记前,我们先来了解一下程序集生成时使用公钥/私钥对进行签名的一个过程。

1 生成一个公钥私钥对,这个公钥的大小可以指定

  sn -k d:\liuzh.snk

2 生成时使用keyfile开关指定密钥文件

  csc /out:d:\apple.dll /t:library /keyfile:d:\liuzh.snk d:\apple.cs

在使用/keyfile:开关后,在生成时编译器会做如下事情:
  1 对程序集FileDef清单中的各个文件进行hash运算(默认是SHA-1,使用AL.exe生成时可以用/algid来覆盖),再使用私钥(私钥来自于liuzh.snk)对该散列值进行加密,得到密文。

  2 将公钥(公钥也来自于liuzh.snk)和1中的密文嵌入到程序集中。

下图展示了这一过程

 bubuko.com,布布扣

 

微软保证了任何情况下sn生成的公钥/私钥的内容都不会相同,也就是说,嵌入到程序集中的公钥都是唯一的,在引用该程序集时,假如在引用的程序集中记录下被引用的程序集的完整的公钥会使得文件特别的大(比如说,任何一个 程序集都包会含了对mscorlib.dll的引用,因为任何类都继承于Object)。下图展示了生成的程序集中的公钥

bubuko.com,布布扣

由于引用的程序集只能记录被引用程序集公钥散列运算后的部分内容,这样一来就存在一个问题,就是可能有两个程序集最后的公钥标识一致,但是概率会很低(小于1/264),加上程序集的唯一性还有其他内容来区分,所以这不会出现什么问题。

顺便提一下,进行过公钥/私钥签名的程序集可以从一定程序上防止篡改,在运行时CLR会用公钥将使用私钥加密后的密文进行解密,将解密后的密文与程序集中各模块hash后的散列进行对比,如果不一致,则可以判定被篡改了。之所以说是一定程序上,是因为这种保护机制很脆弱,这点我们在后面进行说明。

程序集的名称

程序集的名称默认就是/out开关指定的文件的名称(不带扩展名)。使用vs时,这个名称可以在项目属性中指定,正因为在生成时程序集名称与文件名保持一致,所以你在项目属性中指定的是什么名称,最后生成的dll,exe就会使什么名称 

2 全局程序集缓存


 如果一个程序集需要被多个应用程序访问(如微软提供的类库),那么必须把它放到一个已知的目录,而且CLR在检测到对该程序集的一个引用时,需要知道自动检查目录。这个已知的目录就叫做全局程序集缓存(Global Assembly Cache,GAC),放到GAC中的程序集称为共享程序集,因为它可以被其他应用程序共享,仅在程序运行目录下的,我们称之为私有程序集。对于.net 3.5 以前版本,这个目录位于C:\Windows\Assembly,对于.net4.0,它位于C:\Windows\Microsoft.NET\Assembly。

GAC的目录是结构化的,其中包含了许多子目录,并有一个算法来生成这些子目录,永远不要手动将程序集文件手动复制到GAC,相反,应该使用工具来完成,这个工具是GACUtil.exe,可以在命令行查看它的用法。它包含了比较常用的功能如安装,卸载,删除,重新安装,筛选等。注意,只能将一个强命名的程序集安装到GAC中,如果不是强命名(没有进行过签名),在安装时就会提示“将程序集添加到缓存失败:试图安装没有强名称的程序集”,在server2003下,这个文件夹是这样子的。

bubuko.com,布布扣

这个目录里面几乎包含了应用程序在运行时的所需要的所有程序集,在安装.net framework时,实际会安装程序集文件的两个拷贝,一套安装在编译目录,另一套安装在GAC中。之所以要安装两套,是因为在编译时CSC.exe并不会去GAC中寻找你引用的dll,为什么编译器不去GAC中寻找呢,是因为在编译时/reference必须要知道具体的路径,而GAC的目录是不公开的,一个可能的替代方案是在编译时指定程序集的强名称,如System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089,但是这种方案很明显都没有直接部署两套dll来得方便。

将程序集部署到GAC中是对程序集注册的一种手段,虽然没有记录到注册表中,但是很明显破坏了我们的一个基本目标,简单安装,备份,还原,移动和卸载,所以如果我们不是提供类库供他人使用的话,还是应该直接把dll放到程序运行目录下

3 小结 


进一步了解程序集相关信息可以加强我们理解各种dll的引用方式及项目属性中的配置原理,.net安装目录下各种dll及实用程序的功能,以在程序编译或运行时,快速定位和解决问题。

C#编译基础知识(三),布布扣,bubuko.com

C#编译基础知识(三)

标签:style   blog   http   color   使用   strong   

原文地址:http://www.cnblogs.com/Haiz/p/3828557.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!