CLR via C# (二)
本章導(dǎo)讀
1.將類型生成到模塊中
2.元數(shù)據(jù)描述
3.將模塊合并成程序集
4.程序集版本資源信息
前言:
長(zhǎng)久以來(lái),windows應(yīng)用程序一直因?yàn)椴环€(wěn)定和過(guò)于復(fù)雜而口碑不佳;究其原因:
首先:所有的應(yīng)用程序都要使用來(lái)自windows和其他廠商的DLL,這樣一來(lái),由于各個(gè)廠商之間類型的更新升級(jí),很容易造成“向后兼容”的問(wèn)題;很多人會(huì)發(fā)現(xiàn),當(dāng)安裝一個(gè)新應(yīng)用時(shí),他可能會(huì)莫名其妙的破壞另一個(gè)已經(jīng)安裝好的程序,這個(gè)問(wèn)題稱作:“DLL hell”;
第二:應(yīng)用程序安裝的復(fù)雜性,如今,大多數(shù)應(yīng)用程序在安裝時(shí)都會(huì)影響到系統(tǒng)的全部組建(比如:文件復(fù)制到多個(gè)目錄,更改注冊(cè)表等等);
第三:應(yīng)用程序在安裝時(shí)會(huì)涉及安全性;
(可是這些問(wèn)題什么時(shí)候能夠解決呢???)
.net framework 正在嘗試徹底解決DLl hell問(wèn)題,以及安全性問(wèn)題,不過(guò)看起來(lái)還有很長(zhǎng)的路要走......
將類型生成到模塊中
通常來(lái)說(shuō),.net開(kāi)發(fā)中,我們寫(xiě)的代碼可以直接通過(guò)[宇宙第一IDE:Visual Studio]生成dll或是exe文件,很少關(guān)心VS是怎么做到的,下面咱就簡(jiǎn)單說(shuō)一說(shuō)從源代碼到一個(gè)可部署文件的另外一種方法:直接使用CSC.exe來(lái)編譯我們的源代碼;
假如有如下代碼:
public class Method {public static void Print(System.String message){System.Console.WriteLine(message);} }public class MyClass {public static void Main(){Method.Print("-:你好,我來(lái)自Method類型!");System.Console.WriteLine("-:你好,我來(lái)自MyClass類型!");} }這兩個(gè)類分別放置于Method.cs和MyClass.cs文件中,現(xiàn)在,打開(kāi)Visual Studio自帶的命令行提示工具,定位到cs文件所在文件夾,然后執(zhí)行如下命令:
csc.exe /out:myclass.exe /t:exe /r:MSCorLib.dll Method.cs MyClass.cs
這個(gè)命令指示C#編譯器生成一個(gè)名為myclass.exe的可執(zhí)行文件(/out:myclass.exe),生成的文件屬于Win32控制臺(tái)應(yīng)用程序類型(/t:exe);注意,在代碼中,我們使用System.Console這個(gè)類型的WriteLine方法,這個(gè)類型存在于MSCorLib.dll,所以,我們要添加對(duì)該程序集的引用:/r:MSCorLib.dll;
此外,由于/out:myclass.exe和/t:exe開(kāi)關(guān)是C#編譯器默認(rèn)設(shè)定的,而且編譯器會(huì)自動(dòng)引用MSCorLib.dll,所以命令可以簡(jiǎn)化成:
csc.exe?Method.cs MyClass.cs
windows支持兩種類型的應(yīng)用程序:具有控制臺(tái)用戶界面(CUI)的和具有圖形用戶界面(GUI)的,對(duì)應(yīng)的/t:exe為CUI,/t:winexe為GUI;
此外,我們?cè)俳榻B一個(gè)特別的文件:響應(yīng)文件(response file),它就是一個(gè)文本文件,不過(guò)擴(kuò)展名為(.rsp),我們可以把要在命令行添加的各種開(kāi)關(guān)寫(xiě)到這個(gè)文件中,然后在命令行中引用這個(gè)文件:
/out:myclass.exe /target:exe然后將這個(gè)文件存為:myresponse.rsp(名字可以自定義),在命令行中這樣寫(xiě):
csc.exe @myresponse.rsp Method.cs MyClass.cs
C#編譯器允許同時(shí)指定多個(gè)響應(yīng)文件,其實(shí),編譯器會(huì)自動(dòng)查找兩個(gè)名為CSC.rsp的文件:先是在當(dāng)前目錄查找本地CSC.rsp文件,然后在csc.exe所在的目錄查找全局CSC.rsp文件,在這個(gè)全局文件中會(huì)引用多個(gè)程序集(具體自己可以打開(kāi)來(lái)看);使用/noconfig命令開(kāi)關(guān)可以告訴編譯器忽略局部和全局CSC.rsp文件;
元數(shù)據(jù)描述
通過(guò)制定/t開(kāi)關(guān),我們可以知道自己創(chuàng)建的是什么類型的PE文件,但是myclass.exe文件中到底包含什么內(nèi)容呢?由上一篇文章可知,一個(gè)托管PE文件由4個(gè)部分構(gòu)成:
1.PE(+)頭:這個(gè)是windows要求的標(biāo)準(zhǔn)信息;
2.CLR頭:包含模塊在生成時(shí)所面向的CLR的major和minor版本號(hào),一些標(biāo)志(flag),一個(gè)MethodDef token,(如果是CUI或是GUI程序,還包括入口方法),以及一 個(gè)可選的強(qiáng)名稱數(shù)字簽名(可以查看CorHdr.h頭文件中定義的IMAGE_COR20_HEADER,了解CLR頭);
3.元數(shù)據(jù):是一個(gè)二進(jìn)制的數(shù)據(jù)塊,它是一組數(shù)據(jù)表的集合,這些表分為三種---1.定義表(definition table)2.引用表(reference table)3.清單表(manifest table);
4.IL;
下面我們簡(jiǎn)單的介紹一些主要的表:
常用的元數(shù)據(jù)定義表有:
| 表名稱 | 說(shuō)明 |
| ModuleDef | 總是包含一個(gè)用于標(biāo)識(shí)模塊的記錄項(xiàng),在這個(gè)記錄項(xiàng)中主要包含:模塊的文件名和擴(kuò)展名(不含路徑),模塊的版本ID(GUID形式,由編譯器創(chuàng)建) |
| TypeDef | 模塊中定義的每個(gè)類型都在這個(gè)定義表中有一個(gè)對(duì)應(yīng)的記錄項(xiàng),每個(gè)記錄項(xiàng)主要包含:名稱,基類型,標(biāo)志(public,private等),索引(這些索引指向該類型在MethodDef表中定義的方法,在FieldDef中定義的字段,在PropertyDef中定義的屬性以及在EventDef中定義的事件) |
| MethodDef | 模塊中定義的每個(gè)方法都在這個(gè)定義表中有一個(gè)記錄項(xiàng),這個(gè)記錄項(xiàng)主要包含:名稱,標(biāo)志(public,private,virtual,abstract,static,final等),簽名以及該方法的IL代碼在模塊中的偏移量,還包括對(duì)ParamDef表中的一個(gè)記錄項(xiàng)的引用,這個(gè)表中包含與方法參數(shù)有關(guān)的信息 |
| FieldDef | 模塊中定義的每個(gè)字段在這個(gè)表中都有一個(gè)對(duì)應(yīng)的記錄項(xiàng),這個(gè)記錄項(xiàng)包含:標(biāo)志(public,private等),類型和名稱 |
| ParamDef | 模塊中定義的每個(gè)參數(shù)都在這個(gè)表中有一個(gè)相應(yīng)的記錄項(xiàng),這個(gè)記錄項(xiàng)包含:標(biāo)志(in,out,retval等),類型和名稱 |
| PropertyDef | 模塊中定義的每個(gè)屬性都在這個(gè)表中有一個(gè)相應(yīng)的記錄項(xiàng),這個(gè)記錄項(xiàng)包含:標(biāo)志,類型和名稱 |
| EventDef | 模塊中定義的每個(gè)事件都在這個(gè)表中有一個(gè)相應(yīng)的記錄項(xiàng),這個(gè)記錄項(xiàng)包含:標(biāo)志和名稱 |
編譯器在編譯源代碼時(shí),代碼定義的任何一樣?xùn)|西都會(huì)導(dǎo)致在上述表中的某個(gè)表創(chuàng)建一個(gè)記錄項(xiàng);編譯器還會(huì)檢測(cè)源代碼中引用的類型,字段,方法,屬性和事件,并在以下表中的某個(gè)表創(chuàng)建相應(yīng)的記錄項(xiàng);
常用的元數(shù)據(jù)引用表有:
| 表名稱 | 說(shuō)明 |
| AssemblyRef | 模塊中引用的每個(gè)程序集在這個(gè)表中都有一個(gè)對(duì)應(yīng)的記錄項(xiàng),每個(gè)記錄項(xiàng)包含的信息有:程序集名稱(不含路徑和擴(kuò)展名),版本號(hào),語(yǔ)言文化以及公鑰標(biāo)記(一個(gè)根據(jù)發(fā)布者的公鑰生成的小哈希值,標(biāo)記了程序集的發(fā)布者),還包含有標(biāo)志(flag)和一個(gè)哈希值 |
| ModuleRef | 當(dāng)前模塊引用的類型是由別的PE模塊實(shí)現(xiàn)的,所有的這些模塊都會(huì)在這個(gè)表中有一個(gè)記錄項(xiàng),這個(gè)記錄項(xiàng)包含:文件名和擴(kuò)展名(不含路徑) |
| TypeRef | 模塊引用的每個(gè)類型在這個(gè)表中都有一個(gè)記錄項(xiàng),這個(gè)記錄項(xiàng)包含:名稱和一個(gè)引用(指向類型的位置:如果類型是在另外一個(gè)類型中實(shí)現(xiàn)的,則引用指向的是TypeRef記錄;如果類型在同一個(gè)模塊中實(shí)現(xiàn),引用指向的就是一個(gè)ModuleDef記錄項(xiàng),如果類型是在調(diào)用程序集內(nèi)部的另外一個(gè)模塊中實(shí)現(xiàn)的,引用指向的就是一個(gè)ModuleRef記錄項(xiàng),如果類型是在一個(gè)不同的程序集中實(shí)現(xiàn)的,引用指向的就是一個(gè)AssemblyRef記錄項(xiàng)) |
| MemberRef | 模塊中引用的每個(gè)成員(字段,方法,屬性和事件方法)都在這個(gè)表中有一個(gè)記錄項(xiàng),這個(gè)記錄項(xiàng)包含:名稱和簽名,并指向?qū)Τ蓡T進(jìn)行定義的那個(gè)類型的TypeRef記錄項(xiàng) |
為了查看元數(shù)據(jù)的信息,我們可以使用IL反匯編器:ILDasm.exe;在VisualStudio的命令行中執(zhí)行:
ILDasm myclass.exe
就可以查看myclass.exe這個(gè)程序集的元數(shù)據(jù)信息了,具體的使用方法就自行g(shù)oogle吧!!!
?將模塊合并成程序集
之前簡(jiǎn)單的介紹過(guò)程序集,再來(lái)復(fù)習(xí)一下:
程序集是進(jìn)行重用,版本控制和應(yīng)用安全性設(shè)置的一個(gè)基本單元;使用程序集可以區(qū)分可重用類型的邏輯表示和物理表示;程序集是一個(gè)或多個(gè)類型定義文件以及資源文件的集合,在這些文件中,有一個(gè)文件容納了清單數(shù)據(jù),清單也是一組數(shù)據(jù)表的集合,常用的清單數(shù)據(jù)表有:
| 清單元數(shù)據(jù)表名稱 | 說(shuō)明 |
| AssemblyDef | 如果該模塊標(biāo)記的是一個(gè)程序集,就在這個(gè)元數(shù)據(jù)表中包含一個(gè)記錄項(xiàng),這個(gè)記錄項(xiàng)包含:程序集名稱(不含路徑和擴(kuò)展名),版本,語(yǔ)言文化,標(biāo)志,哈希算法以及發(fā)布者公鑰 |
| FileDef | 作為程序集一部分的每個(gè)PE文件和資源文件在這個(gè)表中都有一個(gè)記錄項(xiàng),其中包含:文件名,擴(kuò)展名(不含路徑),哈希值,標(biāo)志 |
| ManifestResourceDef | 作為程序集一部分的每個(gè)資源文件在這個(gè)表中都有一個(gè)對(duì)應(yīng)的記錄項(xiàng),其中包含:名稱,標(biāo)志,FileDef表的一個(gè)索引(指出資源包含在哪個(gè)文件中) |
| ExportedTypesDef | 從程序集的所有PE模塊中導(dǎo)出的每個(gè)public類型在這個(gè)表中都由一個(gè)記錄項(xiàng),其中包含:名稱,FileDef表的一個(gè)索引以及TypeDef表的一個(gè)索引 |
包含清單的程序集文件還有一個(gè)AssemblyRef表,程序集的所有文件引用的所有程序集在這個(gè)表中都有一個(gè)對(duì)應(yīng)的記錄項(xiàng);
程序集是一個(gè)抽象的概念,是一個(gè)或多個(gè)模塊文件和資源文件組成的邏輯單元,其中必然包含且只有一個(gè)后綴為.exe或者.dll的主模塊文件;
CLR操作的是程序集,所以CLR總是首先加載包含“清單元數(shù)據(jù)”的那個(gè)文件。
大多數(shù)時(shí)候,程序集只由一個(gè)文件構(gòu)成,但是也可以由多個(gè)文件構(gòu)成(一些是含有元數(shù)據(jù)的PE文件,一些是.gif或.jpg這樣的資源文件),而且,VisualStudio不能創(chuàng)建多文件程序集(好悲劇.....),使用多文件程序集有以下好處:
1.可用單獨(dú)的文件對(duì)類型進(jìn)行劃分,允許文件以增量的方式下載;
2.可在自己的程序集中添加資源或是數(shù)據(jù)文件;
3.程序集包含的各個(gè)類型可用不同的語(yǔ)言來(lái)實(shí)現(xiàn);
注意:假如多個(gè)類型能共享相同的版本號(hào)和安全性設(shè)置,那么出于性能的考慮,建議將這些類型放到一個(gè)文件中;
客戶端代碼執(zhí)行時(shí)會(huì)調(diào)用方法,一個(gè)方法被首次調(diào)用時(shí),CLR會(huì)檢測(cè)作為參數(shù),返回值或是局部變量而被方法引用的類型,然后,CLR嘗試加載所引用的程序集中包含了清單的那個(gè)文件,如果要訪問(wèn)的類型在這個(gè)文件中,CLR會(huì)執(zhí)行內(nèi)部登記工作,允許使用這個(gè)類型;如果不在,CLR會(huì)嘗試加載需要的文件,然后同樣執(zhí)行內(nèi)部登記工作,允許使用這個(gè)類型;
為了生成一個(gè)新的程序集,所有引用的程序集的所有文件都必須存在;但是,為了讓一個(gè)程序運(yùn)行起來(lái),并不要求被引用的程序集的所有文件都存在;
接下來(lái)咱們玩點(diǎn)有意思的,還用之前定義的兩個(gè)類:Method.cs和MyClass.cs
我們先用C#編譯器:
?
> csc /t:library MyClass.cs> csc /t:library /r:MyClass.dll MyClass.cs?
這樣能生成一個(gè)MyClass.dll,如果換成/t:exe就能生成MyClass.exe;在這里要注意的是:一定要加上
/r:MyClass.dll?
因?yàn)樵贛yClass.cs中引用了Method.cs中的方法,所以在單獨(dú)編譯MyClass.cs的時(shí)候一定要添加對(duì)Method.cs(或是Method.netmodule)的引用;
接下來(lái)我們用另外一種工具:AL.exe(程序集鏈接器);
用AL.exe可以生成只含資源文件的程序集(也稱為附屬程序集),這種程序集在本地化的時(shí)候非常有用,現(xiàn)在執(zhí)行如下命令:
?
> csc /t:module Method.cs > csc /t:module /addmodule:Method.netmodule MyClass.cs > al /out:MyClass.dll /t:library Method.netmodule MyClass.netmodule?
通過(guò)上面的命令,我們同樣生成了MyClass.dll,不同的是我們這次用的是AL.exe,這是第一點(diǎn)不同,第二點(diǎn)不同之處在于:這個(gè)dll是由三個(gè)文件構(gòu)成的,MyClass.dll,Method.netmodule和MyClass.netmodule;AL.exe不能將多個(gè)文件合并成一個(gè)文件;
通過(guò)C#編譯器和AL.exe都能夠?yàn)槌绦蚣砑淤Y源文件,執(zhí)行如下命令:
> al /out:MyClass.exe /t:exe /main:MyClass.Main /win32icon:F:\temp\MyClass.ico MyClass.netmodule通過(guò)這個(gè)命令,我們生成了一個(gè)MyClass.exe執(zhí)行程序,而且,我們還為這個(gè)執(zhí)行程序分配了一個(gè)新的圖標(biāo);
csc.exe和al.exe這兩個(gè)程序非常有用,通過(guò)它們我們可以了解到程序編譯的一些底層知識(shí),如果可能的話,我們最好都試著用一下這個(gè)工具!
?程序集版本資源信息
使用csc.exe或是al.exe生成一個(gè)PE文件程序集時(shí),還會(huì)在PE文件中嵌入一個(gè)標(biāo)準(zhǔn)的win32版本資源,我們?cè)诖a中通過(guò)System.Diagnostics.FileVersionInfo.GetVersionInfo方法可以查看這些信息;在生成程序集的時(shí)候,我們可以在assembly級(jí)別應(yīng)用定制attribute來(lái)定義這些信息,如果使用al.exe來(lái)生成程序集,還可以使用一些命令開(kāi)關(guān)來(lái)指定這些信息;
程序集版本號(hào)都具有相同的格式:每個(gè)都由4個(gè)句點(diǎn)分隔的部分構(gòu)成,比如:
2.5.719.2,從前往后依次代表:主版本號(hào),次版本號(hào),內(nèi)部版本號(hào),修訂號(hào);
一個(gè)程序集有三個(gè)版本號(hào)與之關(guān)聯(lián):
1.AssemblyFileVersion:存儲(chǔ)在Win32版本資源中,僅供參考;
2.AssemblyInformationalVersion:存儲(chǔ)在Win32版本資源中,僅供參考;
3.AssemblyVersion:存儲(chǔ)在AssemblyDef清單元數(shù)據(jù)表中,唯一的標(biāo)識(shí)了一個(gè)程序集,CLR在綁定強(qiáng)命名程序集時(shí),使用這個(gè)版本號(hào);
《未完待續(xù)》
?
轉(zhuǎn)載于:https://www.cnblogs.com/Pure-Land/p/4065156.html
總結(jié)
以上是生活随笔為你收集整理的CLR via C# (二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 运算符的计算机制和原则
- 下一篇: 在网页中加入代码