生成、打包、部署和管理應用程序及類型(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清單表中添加記錄項來引用獨立存在的資源文件。

相關文章
相關標籤/搜索