1. 程式人生 > >《.NET之美》之程序集

《.NET之美》之程序集

使用 解決 秘鑰 private 另存為 輸入 gui 但是 dong

一、什麽是程序集(Assembly)?

經由編譯器編譯得到的,供CLR進一步編譯執行的那個中間產物,在WINDOWS系統中,它一般表現為·dll或者是·exe的格式,但是要註意,它們跟普通意義上的WIN32可執行程序是完全不同的東西,程序集必須依靠CLR才能順利執行。 ----百度百科之程序集

程序集可分為兩種類型:
(1)、可執行程序,後綴為.exe(GUI,圖形用戶接口;或CUI,命令行用戶接口)
(2)、類庫,後綴為.dll
其結構如下圖:
技術分享圖片

在其構成中,只有PE頭、CLR頭、清單是必須的。其他均為可選的。

二、程序集結構解析

1、清單(Manifest)

我們要如何去查看一個程序集的清單呢?
此時我們就要借助微軟自帶的強大的工具ILDASM,此程序如果你要裝Visual Studio就會自動幫你裝上去,路徑在:
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\ildasm.exe.
這裏為了演示方便,我新建了一個程序集AssemblyLib作為演示。我們點擊此工具的文件按鈕,選擇我們那個程序集文件,界面就如下圖所示:
技術分享圖片

我們點擊視圖-元信息-顯示! 滾動到Assembly就可看到AssemblyDef元數據表
技術分享圖片

裏面包含了程序集的名稱、公鑰、版本、程序集特性等信息。我們接著往下翻,就可以看到FileDef表、ExportedTypeDef表、ManifestResourceDef表。FileDef表描述了構成程序集的模塊信息、ExportedTypeDef表描述了外部模塊中存在的類型信息、ManifestResourceDef表包含的是嵌入到程序集的資源信息。
除了主要的這四張表以外,清單中還包含了AssemblyRef表,此表定義了該程序集所引用的其他程序集信息。
從上面可以看出,清單描述了程序集的幾乎一切信息,回答了這樣幾個問題:“程序集是什麽(名稱、版本、特性等)”“由什麽構成(模塊、資源)”和“外部依賴是什麽(引用了其他哪些程序集)”。

2、元數據

所謂元數據就是描述數據的數據,這樣看來,清單也是屬於元數據的一種。
元數據與清單類似,也包含了幾張表:
ModuleDef表,包含了當前模塊的名稱和後綴等信息。
TypeDef表,包含了每個類型的信息,這些信息包括類型名稱、類型的基類、標記等信息(public、private等)。
MethodDef表,包含了每個方法的信息,這些信息包括方法名稱、簽名、標記等信息(public、static、virtual等)。
類似地,還有FieldDef、ParamDef、PropertyDef、EventDef幾張表。
類型元數據中除了包含模塊中定義的類型以外,還包含外部類型的引用,這些信息包含在另外一組表中:TypeRef、MemberRef。
我們只要知道,類型元數據,定義了程序集中所有類型的信息。

3、程序集資源

程序集中還可以包含資源(Resource),資源可以是字符串,也可以是任何格式的文件,比如圖片、Excel文檔等。
現在假設我們需要在項目中得到一張圖片的資源,我們通常有三種做法:
<1>、將圖片保存在程序根目錄的文件夾下,然後通過路徑獲得。
第一種方法我們都很熟悉,這裏就不作介紹了。
<2>、將圖片作為資源嵌入程序集內。
第二種方法我們平常是比較少遇到的,我們看一下如何去做。
程序集的資源(Resource)是一段具有名稱的字節數組。可以將資源想象成一個Dictionay<string,byte[]>,即一個以string為鍵,以byte[]
為值的字典。因為字節數組是二進制形式,所以資源可以是任何文件。
將資源嵌入程序集內也有兩種方法:
①、將文件直接嵌入程序集
這種方法只要將文件添加到項目中,然後查看文件的"屬性",將"生成操作"的值設為嵌入的資源。這裏需要註意兩點,一是資源加到程序集以後,資源的名稱並不等於文件名,VS會自動在文件名前面加上程序集的默認命名空間、文件所在的文件夾名。二是
資源的名稱是大小寫敏感的。
那我們在程序中如何獲取資源呢?
可以在調用Assembly類型的實例方法GetManifestResourceNames()中獲得程序集的所有資源名稱,接下來調用GetManifestResourceStream()方法獲得資源的字節流 。

Assembly asm = Assembly.GetExecutingAssembly(); // 獲得當前執行的程序集
string[] nameArray = asm.GetManifestResourceNames(); // 獲得資源名稱
foreach (string name in nameArray) {
Console.WriteLine(name);
using (Stream s = asm.GetManifestResourceStream(name)) {// 獲得字節流
}

②、將.resources資源文件嵌入程序集
在第一種方法,資源在程序集中是零散的,我們為了集中管理資源,可以使用.resx文件將資源嵌入到程序集當中。.resx文件是一個XML格式的文本文件,記錄了程序集中包含的資源名稱和路徑,它是程序開發時的設計工具,通過可視化的方式來對程序集中的資源進行分類和管理。註意.resx只是一個XML文本文件,類似一個資源清單,本身並不是程序集資源。在生
成程序集時,.resx會被自動轉換為.resources文件,.resources文件包含了實際的資源文件(例如圖片或者音頻),並嵌入到程序集當中,但習慣上仍將.resx稱作資源文件。
<3>、將資源作為獨立程序集
在第二種方法中我們將資源直接嵌入到程序集中,會迅速增大程序集的體積,因此,我們可以將資源單獨放在一個單獨的程序集中,然後再由主程序集引用它。這樣做的好處就是如果主程序沒有
用到資源,那麽就不用去加載這個程序集。我們可以先引用資源程序集的.dll(假設為res.dll),然後用如下的代碼去訪問資源程序集的資源:

Assembly asm = Assembly.Load("res");
ResourceManager r = new ResourceManager("Resource", asm);
...

在多語言的應用程序中,通常會將各個不同地區的語言文本作為資源,單獨放到各自獨立的程序集中,使得應用程序可以根據計算機的本地語言來顯示相應資源中的文本。
此時我們只要在項目中新建一個資源文件,Resource.en.resx,在這個資源文件中添加字符串資源,名稱為"address",值為"China,GuangDong,Shenzhen"。
重新生成項目,在bin\debug文件夾下,會看多多了一個子文件夾en,其中包含了ConsoleApp.resources.dll文件,該程序集包含了英文版本的資源。類似地,可以創建包含了德
語、日語等其他國家語言的程序集,這種程序集有一個形象的名字,叫做衛星程序集(Satellite Assembly)

Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en");
ResourceManager r = new ResourceManager("ConsoleApp.Resource",
Assembly.GetExecutingAssembly());
string address = r.GetString("address");
Console.WriteLine("address:" + address);

其中的"Thread.CurrentThread.CurrentUICulture=new System.Globalization.CultureInfo("en");"語句,將當前的UI區域性改為了en,即英語地區國家。之後,運行ConsoleApp.exe,會看到顯示的輸出為:

address:China, Guang Dong, Shenzhen

三、強名稱程序集

1、強名稱的定義

我們在新建程序集時,對程序集進行命名,那此時怎麽命名比較好呢?
比如在上面我們建的程序集Assemblylib,別人也可以建這個名稱的程序集,我們如何去比較好的劃分呢。首先,我們先對同一個程序集不同版本進行劃分,我們給程序集加上版本號以及區域性。 如下圖:
技術分享圖片

這樣之後,我們可以很好地分清自己的程序集之間不會發生沖突,但還是無法區分別人與自己的程序集。
為了解決這個問題,我們可以繼續加入公司名、公司的URL以及GUID,這樣,這個程序集的規則就有了唯一標識,這個時候就會衍生了另一個問題別人拿到你這個程序集後就可以看到你這個程序集的信息,從而進行仿冒。出於這些考慮,微軟選擇了使用公鑰/私鑰非對稱加密(RSA)的方式,並結合使用了散列函數(SHA1)來保證:程序集的唯一性、防仿冒性、防篡改性。

2、為程序集賦予強名稱

接下來我們看下如何實現防偽冒性:
我們需要用到一個工具SN.exe ,這個工具在你裝VS的時候就會自動幫你裝好,這個文件筆者的目錄為:C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools
我們可以使用vs自帶的開發人員命令提示符,此工具筆者在:C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2017\Visual Studio Tools下 打開命令提示符輸入sn.exe,我們可以看到如下圖的幫助頁面: 技術分享圖片

技術分享圖片

再輸入 sn -k D:\Assemblysnk.snk 回車後就會將秘鑰寫入D盤根目錄下
技術分享圖片

我們打開D盤根目錄即可看到這個秘鑰文件。
技術分享圖片

上面的文件中包含公鑰及私鑰,接下來我們將公鑰提取出來並另存為另一個文件。
技術分享圖片

從上面的結果可以看出,公鑰的字節數很長,有128字節,操作起來很不方便。因此,對公鑰進行了散列運算,獲得了一個它的8字節的哈希值,也就是公鑰標記。由於公鑰標記是公鑰的摘要,或者“指紋”,它們是對等的,此時我們只要關註公鑰標記即可。 我們再將公鑰加入程序集規則即可。 單純加公鑰別人可以仿冒,並無多大意義,公私鑰對就顯出了效果。
我們使用VS,將公私鑰對加入簽名中。 這樣就做到了程序集的防偽冒。(加密部分筆者這裏就不一一贅述,有興趣的請自行了解)。
技術分享圖片

總結

在這一部分的閱讀學習中,筆者先解釋了什麽是程序集,分析了程序集的結構,還介紹了在程序集中嵌入資源的幾種方法,接下來講了強名稱程序集如何一步步去做到唯一性與防偽。在我們日常的開發過程中,程序集是我們接觸最多的文件,平時我們只懂得如何去生成與引用,並不知其具體的原理,這樣進一步的了解,對我們以後的開發有著奠定基石的作用!

《.NET之美》之程序集