生成、打包、部署和管理应用程序及类型(3):将模块合并成程序集

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清单表中添加记录项来引用独立存在的资源文件。

相关文章
相关标签/搜索