在.NET出现之前, Windows的程序有一些非常困扰人的问题:
1.当安装一个新的应用程序,有些时候会发现,它莫名其妙地损坏了已安装的应用程序(事实上是在系统的Win32文件夹内添加了与之前重名的dll文件,导致该dll被覆盖)。这种困境被称大家称为“DLL灾难”。
2.大部分的程序安装会影响到所有的系统部件。比如:要在不同的位置拷贝文件,要在注册表中添加信息。这个问题在于不能将应用程序作为一个单一的实体,这同时也导致了程序不便于拷贝/备份/部署。
3.安全问题。我们很难去保证程序的运行不会危害用户。
.NET程序 提出的程序集(Assembly)概念很好的解决了“DLL灾难”和部署难的问题,并且也提供一些安全保障。下面就让我们来谈谈程序集,程序集并不是一个单一的物理单位,它是由一个或者多个 包含元数据(Metadata)的托管模块(Managed Module)、一些资源文件 以及 清单 逻辑组成。
一、托管模块(exe/dll)的相关概念
托管模块一般以.exe/.dll的形式出现,它们由面向CLR的编译器生成。它主要包含以下四个部分:
1、PE32/PE32+ header 。标准windows PE 头文件,主要指定了 该模块是32还是64位文件。还指定了给模块是GUI(exe),CUI(exe)或者DLL 其中的一种。
2、CLR header。CLR header 的定义在CorHdr.h文件中。它主要包含CLR版本、模块入口函数(Main函数)的 MethodDef 元数据、元数据的大小及偏移量、可选的强签名。
3、Metadata。元数据是以二进制数据组成的块(a block of binary data)。它中包含3类表,分别是:定义表(definition tables)、引用表(reference tables)、清单表(manifest tables)。由于元数据提供如此完备的信息,使得程序集具有自描述的功能,从而解决了博客开头的前两个问题。
·定义表(definition tables) 包含本模块内定义的 类型,方法,字段,参数,属性,事件。
·引用表(reference tables) 包含了本模块所引用的 数据集,模块,类型,方法,字段,属性,事件。
·清单表(manifest tables) 是描述整个程序集的表。它包含了程序集中所有文件的名称以及程序集的版本、文化、出版者、公开导出的类型。
托管模块通常包含定义表和引用表,而清单表一般给程序集使用的(下文会说到)。
我们可以用 ILDasm.exe来查看这些元数据。
4、IL code。中间代码,编译器产生的面向CLR的代码。在运行时,CLR将其转换为本地CPU指令。
二、程序集
我们把一个或者多个托管模块,资源文件 组成一个程序集,同时,它有个专门的文件来保存 清单表元数据,这个清单包含了程序集中所有文件的名称以及程序集的版本、文化、出版者、公开导出的类型。
一定得注意的是:虽然托管模块具有我们常见的exe文件形式,但CLR操作的对象是程序集,而不是托管模块。CLR总是先加载清单文件,然后通过清单文件来获得其他文件的名称。虽然程序集可以只包含一个托管模块文件(exe)但是我们要以程序集为单位来进行操作,而不是一单个托管模块(exe)作为单位。
·程序集定义了可复用的类型(供其它程序集/模块 使用)
·程序集以版本号来标识
·程序集里包含安全相关的策略信息。
CLR区分单个托管模块是否是程序集的方法是:查看它是否包含清单元数据表。
不用单个托管模块而用程序集做个一个执行单位 的好处是:
1、我们可以把不同作用的类型放在不同的文件(托管模块)中,在互联网中,可以以增量的形式下载文件。即需要用的时候再下载该部分文件。
2、我们可以添加各种的资源、数据文件通过连接器链接到程序集中,比如Excel表,然后再程序里做好读取即可。
3、我们可以用不同的语言来生成不同功能的托管模块,这样每个语言的优势就体现出来了。
由于程序集有良好的自描述性,我们在部署(或者用户拷贝)程序集时,都非常方便。只要直接将程序集的文件拷贝到用户的磁盘中就好了,CLR会通过清单文件来找到各个所需文件。这样就避免在win32,注册表中放置文件,照成风险了