Program.exe并不是只是含有元数据的PE文件,它仍是程序集。程序集是一个或多个类型定义文件及资源文件的集合。在程序集的全部文件中,有一个文件容纳了清单(manifest)。清单也是一个元数据表集合。表中主要包含做为程序集组成部分的那些文件名称。此外,还描述了程序集的版本、语言文化、发布者、公开导出的类型以及构成程序集的全部文件。编程
CLR操做的是程序集。也就是说,CLR老是首先加载包含“清单”元数据表的文件,再根据“清单”获取程序集中的其余文件的没名称。程序集的特色以下:安全
●定义了可重用的类型编程语言
●用一个版本号标记函数
●能够关联安全信息工具
这些特色是包含清单的元数据表的文件才具有的。spa
使用程序集,可重用类型的逻辑表示和物理表示就能够分开。例如,程序集可能包含多个类型,能够将经常使用类型放到一个文件中,不经常使用类型放到另外一个文件中。若是程序集要从Internet下载而且部署,那么对于含有不经常使用类型的文件,加入客户端永远不适用那些类型,该文件就永远不会下载到客户端。.net
总结一下使用多文件程序集的理由:命令行
●不一样的类型用不一样的文件,使文件以“增量”下载。另外,将类型划分到不一样文件中,能够对购买和安装的应用程序部分或分批打包、部署。code
●可在程序集中添加资源或数据文件。例如,假定一个类型的做用是计算保险信息,须要访问精度表才能完成计算。在这种状况下,没必要在本身的源码中嵌入精度表。相反,可使用一个工具,使数据文件成为程序集的一部分。BTW,数据文件能够是任意格式,只要程序知道如何解析。blog
●程序集包含的各个类型能够用不一样的编程语言来实现。编译不一样语言类型的时候会生成不一样的模块,而后能够用工具将全部模块合并成单个程序集。
生成程序集要么选择现有的PE文件做为“清单”的宿主,要么建立单独的PE文件并只在其中包含清单。因为有了清单的存在,程序集的用户没必要关心程序集的划分细节。此外,清单也使程序集具备了自描述性。另外,在包含清单的文件中,一些元数据信息描述了哪些文件是程序集的一部分。可是,那些文件自己并不包含元数据来指出他们是程序集的一部分。
能够经过不少方式将模块添加到程序集。这里使用/addmodule开关。如今有两个源代码:
Program.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 8 namespace Project_1 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 System.Console.WriteLine("HaHa"); 15 Class1 C1 = new Class1(); 16 C1.printHaha(); 17 } 18 } 19 }
Class1.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Class1 10 { 11 public void printHaha (){ 12 System.Console.WriteLine("Class1 Haha"); 13 } 14 } 15 }
先将不经常使用的类型都编译到一个模块:
此时生成一个Class1.netmodule文件。这是一个PE文件,可是不能被CLR加载。
接着将Program.cs编译到另外一个模块。该模块将成为程序集清单的宿主。将该模块命名为MultiFileLibrary.dll:
上述命令行指示C#编译器编译Program.cs来生成MultiFileLibrary.dll。因为制定了/t:library开关,因此生成的是含有清单元数据表的DLL PE文件。/addmodule:Class1.netmodule 告诉编译器将文件添加到FIleD二分清单源数据表,并将Class1.netmodule的公开导出类型添加到ExportedTypes Def清单元数据表。
Class1.netmodule文件包含编译Class1.cs所生成的IL代码。该文件还包含一些定义源数据表,描述了Class1.cs定义的类型、方法、字段、属性、事件等。还包含一些引用元数据表,描述了Class1.cs引用的类型、方法等。
MultiFileLibrary.dll是一个单独的文件。和Class1.netmodule类似,MultiFileLibrary.dll包含编译Program.cs所生成的IL代码以及相似的定义与引用元数据表。此外,MultiFileLibrary.dll还包含额外的清单元数据表,这使得MultiFileLibrary.dll成为程序集。清单元数据表描述了程序集的全部文件(MultiFileLibrary.dll自己和Class1.netmodule)。清单元数据表还包含从Class1.netmodule和MultiFileLibrary.dll导出的全部公共类型。
可用ILDasm检查元数据清单表(摘取部分):
File #1 (26000001) ------------------------------------------------------- Token: 0x26000001 Name : Class1.netmodule HashValue Blob : 06 f0 35 e1 e5 70 9e a4 6c 9d 4c dc 6b 40 6f c0 9e b5 09 56 Flags : [ContainsMetaData] (00000000) ExportedType #1 (27000001) ------------------------------------------------------- Token: 0x27000001 Name: Project_1.Class1 Implementation token: 0x26000001 TypeDef token: 0x02000002 Flags : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100001) User Strings -------------------------------------------------------
从中能够看出,Class1.netmodule文件已经被当作是程序集的一部分。token是0x26000001。在ExportedType 能够看到一个公开导出的类型(只有public会显示在这个数据表中)
客户端执行代码时会调用方法。一个方法首次被调用时,CLR检测做为参数、返回值或者局部变量而被方法引用的类型。而后,CLR尝试加载所引用程序及中含有的清单文件。若是要访问的类型刚好出如今这个文件中,CLR会执行内部登记工做,容许使用该类型。若是清淡指出被引用的类型在不一样文件中,CLR会尝试加载须要的文件,一样执行内部登记,并容许使用该类型。只有在调用方法确实引用了未加载程序集中的类型时,才会加载程序集。
使用VS IDE将程序集添加到项目中
这个路径要与生成MultiFileLibrary.dll的路径一致。
使用程序集连接器
除了使用C#编译器,还可使用“程序集连接器”实用程序AL.exe来建立程序集。若是程序集要包含由不一样编译器生成的模块,或者生成时不清楚程序集的打包要求,程序集连接器就显得至关有用。还可使用AL.exe生成只含资源的程序集,也就是附属程序集,一般用于本地化。
首先,有三个源代码:
Program.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Program 10 { 11 public static void Main(string[] args) 12 { 13 System.Console.WriteLine("HaHa"); 14 Class1 C1 = new Class1(); 15 C1.printHaha(); 16 Class2 C2 = new Class2(); 17 C2.printHaha(); 18 } 19 } 20 }
Class1.cs:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Project_1 8 { 9 public class Class1 10 { 11 public void printHaha (){ 12 System.Console.WriteLine("Class1 Haha"); 13 } 14 } 15 }
Class2.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Project_1 { public class Class2 { public void printHaha() { System.Console.WriteLine("Class2 Haha"); } } }
为了理解AL.exe的工做原理,先改变MultiFileLibrary.dll的生成方式:
生成MultiFileLibrary.dll:
指定入口并获得EXE PE文件:
(这里在操做的时候,出现了一个问题:指定main函数的时候报错“找不到Program.main”,后来才发现缘由有两个:第一个是没有加命名空间Project_1,以及代码中main函数没有写错public,固然如今都加上了。须要指定做用域是由于程序集有可能有多个做用域,因此要指定是哪一个做用域下的。可是public这个到如今都没有搞清楚具体缘由,不知道有没有懂的大佬解释一下,我只知道和.exe的执行过程有关,可是main是在哪里发生了“外部调用”这个具体的过程还有些模糊)
能够执行Program.exe:
为程序集添加资源文件:
AL.exe建立程序集时,能够用/embed将文件做为资源添加到程序集。该开关获取任意文件,并将文件内容嵌入最终的PE文件。清单中的ManifestResourceDef表会更新以反映资源的存在。
与AL.exe类似,C#编译器CSC.exe也容许将资源合并到编译器生成的程序集中。/resource开关将指定的资源文件嵌入最终生成的程序集PE中,并更新ManifestResourceDef表。/linkresource开关ManifestResourceDef和FileDef清单表中添加记录项来引用独立存在的资源文件。