在咱們進入章節以前,咱們討論一下生成、打包和部署你的應用程序和應用程序類型必須的步驟。在這章裏,我關注的是如何爲你的應用程序的用途生成程序集。在第三章,「共享程序集合和強命名程序集」,我會涉及你須要瞭解的高級概念,包括如何生成和使用包含類型的程序集,這些程序集將被多個應用程序共享。在這兩章中,我也會討論管理員能夠影響執行應用程序和它的類型的方式。編程
現在,應用程序由幾個類型組成,一般由你和微軟建立。此外,有不少組件供應商建立和出售類型,其它公司購買能夠減小軟件項目開發時間。假如這些類型是用以CLR爲目標的語言開發的,那麼它們能夠所有一塊兒無縫的工做;一種語言寫的一個類型可使用另外一個類型作爲它基類而且不關心基礎類型是用什麼語言開發的。緩存
在這一章中,我將解釋這些類型爲了部署怎麼生成和打包到文件。在這個過程當中,我將帶你回顧一些.NET Framework解決的問題。安全
.NET Framework Depoyment Goals(.NET Framework部署目標)app
多年以來,Windows獲得了一個不穩定和繁複的名聲。這個名聲是否名副其實要看不少不一樣的因素。首先,全部的應用程序使用微軟或其餘供應商的動態連接庫(DLLs)。由於一個應用程序從各類各樣的供應商執行代碼,開發者的任何一段代碼都不能100%的肯定別人是怎麼使用它的。即便這種類型的交互存在各類各樣潛在的麻煩,事實上,這些問題通常不會出現由於應用程序在部署前都會進行測試和調試。less
不管如何,當一家公司決定更新它的代碼和裝載新的文件時用戶運行常常會遇到問題。這些新文件被假設能向後兼容以前的文件,可是誰能夠肯定?事實上,當一家供運商更新它的代碼時,它通常發現它不可能從新測試和調試全部已裝載的應用程序以確保有不但願的影響。ide
我相信每一個讀這本書的人都經歷過這個問題的衍生:當安裝一個新的應用程序,你發現它以某種方式破壞了一個已經安裝好的應用程序。這種困境是衆所周知的「DLL地獄」。這種類型不穩定在通常的電腦使用者內心和大腦中都留下了恐懼的烙印。結果是用戶不得不當心考慮是否在他們的機器上安裝一個新的軟件。就我本身來講,我決定不嘗試安裝應用程序走出恐懼,它可能會對我依賴的應用程序產生不利的影響。工具
第二個緣由,給前面說起的Windows名聲作出貢獻的是安裝的複雜性。現在,當安裝了大多數應用程序,它們影響系統全部的部分。例如,安裝一個應用程序引發文件被拷貝到不一樣目錄,更新註冊表,在你的桌面和開始菜單/桌面安裝快捷方式。這帶來的問題是應用程序沒有分離爲一個單獨的實體。你不能容易的備份應用程序由於你必須拷貝應用程序的文件和註冊的相關部分。此外,你不能容易的從一個機器移動應用程序到另外一個機器。你必須在運行一次安裝程序,全部文件和註冊表才能設置正確。最終,你不能容易的卸載或者移除應用程序而沒有這種討厭的感受,應用程序的一些部分還潛伏在你的機器上。性能
第三個緣由和安全有關。當應用程序安裝好了,伴隨着它們的是各類各樣的文件,其中不少是經過不一樣的公司寫的。此外,網頁應用程序常常有代碼(就像ActiveX控件)這樣下載,用戶甚至沒有察覺代碼已經安裝在他們的機器上了。現在,這份代碼能夠執行任何操做,包括刪除文件或發郵件。用戶懼怕安裝新的應用程序是正確的,由於它們會引發潛在的損害。爲使用戶舒服,系統中必須創建安全,用戶能夠明確地容許或者不容許不一樣的公司開發的代碼訪問他們的系統資源。測試
.NET Framework把DLL地獄問題放在重要位置,就像你將在這章和第三章裏看到的。.NET Framework在修復應用程序狀態分散在用戶硬盤的各個角落問題走了很遠。例如,不像COM,類型不須要在在註冊表中設置。不幸的是,應用程序依然須要快捷方式連接。至於安全,.NET Framework包含一個叫作代碼訪問安全(code access security)的安全模型。鑑於Windows安全是以用戶身份爲基礎的,代碼訪問安全容許機主設置權限,從而控制下載的組件能夠作什麼。一個主機應用程序像Microsoft SQL Server只給代碼少許受權,然而一個本地安裝(自我寄宿)的應用程序運行時擁有全部權限。正如你將看到的,.NET Framework使用戶能夠控制安裝什麼和運行什麼,總之,對於控制機器,Windows從未作的這麼好。ui
Building Types into a Module(類型生成一個模塊)
在這個場景,我將給你看怎麼把你源文件(包含不一樣的類型)轉換成一個能夠部署的文件。讓咱們從仔細查看下面的簡單應用程序開始。
public sealed class Program { public static void Main(string[] args) { System.Console.WriteLine("Hi"); } }
這個應用程序定義了一個類型,叫作Program。這個類型有一個單獨的公共的、靜態的叫作Main的方法。在Main中應用了一個由微軟實現的叫作System.Console.System.Console的類型,而且在MSCorLib.dll文件中實現了這個類型的方法的中間語言(IL)代碼。因此咱們的應用程序定義了一個類型同時也使用了另外一個公司的類型。
生成這個簡單的應用程序,把以前的代碼放到一個源代碼文件中,叫作,Program.cs,而後執行如下命令行:
csc.exe /out: Program.exe /t:exe /r:MSCorLib.dll Program.cs
這個命令行告訴C#編譯器發佈一個叫作Program.exe(/out: Program.exe)的可執行文件。這個類型文件產生一個Win32控制檯應用程序(/t[arget]:exe)。
當C#編譯器處理源文件,它看到代碼引用System.Console類型的WriteLine方法。這時候,編譯器要肯定這個類型在哪裏存在,它有一個WriteLine方法,而且參數被傳遞給這個方法,且匹配方法指望的參數。由於這個類型不是在C#中定義的,爲了使C#編譯器開心,你必須給它一個程序集使它可以解決外部類型引用。在以前提到的命令行,我已經包括了/r[eference]:MSCoreLib.dll開關,它告訴編譯器經過MSCorLib.dll文件定義的程序集查找外部類型。
MSCorLib.dll是一個特殊的文件,它包含全部的核心類型:Byte,Char,String,Int32等等。事實上,這些類型是如此頻繁的被使用,因此C#編譯器自動引用MSCorLib.dll程序集。換句話說,下面的命令行(省略/r 開關)和上面的命令行結果是同樣。
csc.exe /out: Program.exe /t:exe Program.cs
此外,由於/out: Program.exe和/t:exe命令行開關也匹配C#編譯器的默認選擇,下面的命令行也會給出相同的結果。
csc.exe Program.cs
假如,爲了一些緣由,你真的不想要C#編譯器引用MSCorLib.dll程序集,你可使用/nostdlib開關。當創建MSCorLib.dll程序集時微軟使用這個開關。例如,當CSC.exe嘗試編譯Program.cs文件時如下的命令行將會產生一個錯誤由於System.Console類型定義在MSCorLib.dll中。
csc.exe /out: Program.exe /t:exe /nostdlib Program.cs
如今,讓咱們近一點看C#編譯器生成的Program.exe文件。這個文件其實是什麼?好吧,首先,它是一個標準的可移植執行體(portable executable,PE)文件。這意味着一個運行32位或者64位版本Windows的機器能夠加載這個文件並用它作點什麼。Windows支持三種類型的應用程序。創建一個控制檯用戶界面(console user interface,CUI)應用程序,指定/t:exe開關;創建一個圖形用戶界面(graphical user interface,GUI)應用程序,指定/t:winexe開關;創建一個Windows商店App,指定/t:appcontainerexe開關。
Response Files(響應文檔)
在擱置關於編譯的開關的討論前,我要花一點時間談談響應文檔。一個響應文件是一個包含一個編譯器命令行開關集合的文本文件。當你執行CSC.exe,編譯器打開響應文檔並使用詳細定義的開關就像命令行傳遞給CSC.exe的同樣。你命令編譯器經過在命令行前加@符號指定響應文件的名字使用指定的響應文件。例如,你有一個叫作MyProject.rsp的響應文件包含如下文本。
/out:MyProject.exe
/target:winexe
爲了CSC.exe使用這些設置,你可使用如下內容調用。
csc.exe @MyProjext.rsp CodeFile1.cs CodeFile2.cs
這告訴C#編譯器輸出文件時什麼名字和要建立什麼目標類型。就你看見的,響應文檔很方便由於你不須要每次編譯你的項目時手動給命令行參數表達你的意願。
C#編譯器支持多個響應文檔。除了文檔外你顯式指定在命令行的,編譯器自動在CSC.rsp文檔中查找。當你運行CSC.exe,它查找包含CSC.exe文件的目錄以獲得全局CSC.rsp文件。你須要應用到你的項目的設置應該都放在這個文件中了。編譯器集合和使用在這些響應文檔中的設置。假如你在本地和全局響應文檔中有衝突設置,本地的設置重載在全局響應文件中的設置。一樣的,任何在命令行顯式傳遞的設置從本地響應文件重載設置。
當你安裝.NET Framework,它在%SystemRoot%\Microsoft.NET\Framework(64)\vX.X.X (X.X.X是你安裝的.NET Framework的版本)目錄默認安裝了全局CSC.rsp文件。最近的版本文件包含如下開關。
# This file contains command-line options that the C# # command line compiler (CSC) will process as part # of every compilation, unless the "/noconfig" option # is specified. # Reference the common Framework libraries /r:Accessibility.dll /r:Microsoft.CSharp.dll /r:System.Configuration.dll /r:System.Configuration.Install.dll /r:System.Core.dll /r:System.Data.dll /r:System.Data.DataSetExtensions.dll /r:System.Data.Linq.dll /r:System.Data.OracleClient.dll /r:System.Deployment.dll /r:System.Design.dll /r:System.DirectoryServices.dll /r:System.dll /r:System.Drawing.Design.dll /r:System.Drawing.dll /r:System.EnterpriseServices.dll /r:System.Management.dll /r:System.Messaging.dll /r:System.Runtime.Remoting.dll /r:System.Runtime.Serialization.dll /r:System.Runtime.Serialization.Formatters.Soap.dll /r:System.Security.dll /r:System.ServiceModel.dll /r:System.ServiceModel.Web.dll /r:System.ServiceProcess.dll /r:System.Transactions.dll /r:System.Web.dll /r:System.Web.Extensions.Design.dll /r:System.Web.Extensions.dll /r:System.Web.Mobile.dll /r:System.Web.RegularExpressions.dll /r:System.Web.Services.dll /r:System.Windows.Forms.Dll /r:System.Workflow.Activities.dll /r:System.Workflow.ComponentModel.dll /r:System.Workflow.Runtime.dll /r:System.Xml.dll /r:System.Xml.Linq.dll
由於全局CSC.rsp文件引用了全部列出的程序集,你不須要經過C#編譯器的/reference開關顯式的引用這些程序集。這個響應文件給開發者提供了很大的便利由於它容許開發者使用定義在各類微軟發佈程序集中的類型和命名空間而不須要每次編譯時指定一個/reference編譯器開關。
引用全部程序集編譯器會慢一點。可是若是你的源代碼沒有引用定義在這些程序集中的類型和成員,對於生成程序集文件沒有任何影響,也不會影響運行時的執行性能。
注意 當你使用/reference編譯器開關引用一個程序集時,你能夠給特殊的文件指定一個完整的路徑。可是,假如你沒有指定路徑,編譯器會在如下列出的地方搜索文件(按列出的順序): *工做目錄。 *包含CSC.exe文件的目錄。MSCorLib.dll就是從這個目錄中得到的。路徑看起來有點像這個:%SystemRoot%\Microsoft.Net\Framework\v4.0.#####。 *使用/lib編譯器開關指定的任何名錄。 *使用LIB環境變量指定的任何目錄。 |
固然,歡迎你在全局CSC.rsp文件中添加你本身的開關是你的生活更簡單,可是這麼作,當複製生成環境到不一樣的機器時會變的更困難——你不得不記住在每一個生成機器上以相同的方式更新CSC.rsp。另外,你能夠告訴編譯器忽略本地和全局CSC.rsp文件經過指定/noconfig命令行開關。
A Brief Look at Metadata(簡單查看元數據)
如今咱們知道咱們建立了什麼類型的PE文件。可是實際在Program.exe文件中是什麼?一個託管的PE文件有4個主要的部分:PE32(+)頭,CLR頭,元數據和IL。PE32(+)是Windows指望的標準信息。CLR頭是一小塊信息指定模塊須要CLR(託管模塊)。頭文件裏包括CLR的主要和次要版本號,模塊須要構建的:一些標記,一個MethodDef口令(之後會描述)指出模塊的入口點假如模塊是一個CUI,GUI,或者Windows Store可執行文件,和一個可選的強名稱的數字信號(在第三章討論)。最後,頭文件包含構成模塊的特定元數據表的大小和偏移量。你能夠經過檢測定義在CorHdr.h頭文件中的IMAGE_COR20_HEADER查看CLR的確切格式。
元數據是由幾個表組成的一個二進制塊。表有三個分類:定義表,引用表,和載貨單表。下表描述了一些在模塊的元數據塊中較爲常見的定義表。
Common Definition Metadata Tables(常見定義元數據表)
Metadata Definition Table Name(元數據定義表名稱) | Description(描述) |
ModuleDef(模塊定義) | 是一個識別模塊的條目。條目包括模塊的文件名和擴展(無路徑)和一個模塊的版本ID(編譯器在表單中建立的一個GUID)。這容許文件重命名當使用起始名稱持續記錄的時候。然而,很是不鼓勵重命名文件,它可能會阻止CLR在運行時定位程序集,因此不要那麼作。 |
TypeDef(類型定義) | 每一個定義在模塊中的類型的條目。每一個條目包括類型的名稱,基礎類型,和標記(public,private,等等)和它在MethodDef表中的全部方、在FieldDef表中的全部字段、在PropertyDef表中的全部屬性、在EventDef表中的全部事件的索引。 |
MethodDef(方法定義) | 每一個定義在模塊中的方法的條目。每一個條目包括方法的名字,標記(private,public,virtual,abstract,static,final等等),簽名,模塊內的偏移量,在那能夠找到它的IL代碼。每一個條目也能夠參考ParamDef表條目,在那能夠找到更多關於方法參數的信息。 |
FieldDef(字段定義) | 每一個定義在模塊中的字段的條目。每一個條目包括標記(private,public,等等),類型和名稱。 |
ParamDef(參數定義) | 每一個定義在模塊中的參數的條目。每一個條目包括標記(in,out ,retval,等等),類型和名稱。 |
PropertyDef(屬性定義) | 每一個定義在模塊中的屬性的條目。每一個條目包括標記,類型和名稱。 |
EventDef(事件定義) | 每一個定義在模塊中的事件的條目。每一個條目包括標記和名字。 |
當編譯器編譯的源碼時,你代碼中定義的每件事都會引發一個條目的建立如上表所描述。元數據表也會被建立當編譯器察覺類型,字段,方法,屬性和事件是源代碼引用。元數據建立包含一個引用表集合,保存了引用項目記錄。下表顯示了一些比較常見的引用元數據表。
Common Reference Metadata Tables(經常使用引用元數據表)
Metadata Reference Table Name(元數據引用表名稱) | Description(描述) |
AssemblyRef(程序集引用) | 模塊引用的每一個程序集的條目。每一個條目包括須要綁定到的程序集的信息:程序集的名稱(沒有路徑和擴張),版本號,區域,和公共關鍵口令(通常是從發佈者公開的關鍵字生成的哈希值,識別引用程序集的發佈者)。每一個條目也是一些標記和一個哈希值。這個哈希碼傾向於做爲應用程序集比特的校驗碼。CLR徹底忽略這個哈希值未來可能會繼續這樣作。 |
ModuleRef(模塊引用) | 實現這個模塊引用的類型的每一個PE的條目。每一個條目包括模塊的文件名稱和擴張(沒有路徑)。此表用於綁定到類型實現不一樣模塊的調用匯編模塊。 |
TypeRef(類型引用) | 每一個被模塊引用的類型有一個條目。每一個條目包括類型的名字和引用類型能夠找到的地方。若是類型中實現另外一種類型,引用將會指出一個TypeRef條目。若是類型在同一個模塊中實現,引用將會指出一個ModuleRef條目。若是類型調用程序集在另外一個模塊中實現,引用將會指出一個ModuleRef條目。假如類型在不一樣的程序中實現,引用將會指出一個AssemblyRef條目。 |
MemberRef(成員引用) | 每一個被模塊引用的成員(字段和方法,和屬性和事件方法同樣)有一個條目。每一個條目包括成員的名字和簽名和指向TypeRef的條目。 |
還有不少表單我沒有在上面兩個表單中列出,而我只想給你一個編譯器發佈產生元數據信息種類的感受。以前我提到的有一個貨運單元數據表集合;我會晚一點在這章討論這些。
大量的工具容許你檢測在託管PE文件中的元數據。我常用的仍是ILDasm.exe,IL反彙編程序。要看元數據表,執行如下命令行。
ILDasm Program.exe
這樣ILDAasm.exe將會運行,加載Program.exe程序集。能夠看到元數據在一個漂亮的、可讀的表單裏,選擇View/MetaInfo/Show!菜單項(或者按Ctr+M)。如下信息將會出現。
=========================================================== ScopeName : Program.exe MVID : {42F16070-F580-468A-9925-70283A94D852} =========================================================== Global functions ------------------------------------------------------- Global fields ------------------------------------------------------- Global MemberRefs ------------------------------------------------------- TypeDef #1 (02000002) ------------------------------------------------------- TypDefName: Program.Program (02000002) Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101) Extends : 01000001 [TypeRef] System.Object Method #1 (06000001) [ENTRYPOINT] ------------------------------------------------------- MethodName: Main (06000001) Flags : [Public] [Static] [HideBySig] [ReuseSlot] (00000096) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] ReturnType: Void No arguments. Method #2 (06000002) ------------------------------------------------------- MethodName: .ctor (06000002) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x0000205e ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #1 (01000001) ------------------------------------------------------- Token: 0x01000001 ResolutionScope: 0x23000001 TypeRefName: System.Object MemberRef #1 (0a000012) ------------------------------------------------------- Member: (0a000012) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #2 (01000002) ------------------------------------------------------- Token: 0x01000002 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.Versioning.TargetFrameworkAttribute MemberRef #1 (0a000001) ------------------------------------------------------- Member: (0a000001) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #3 (01000003) ------------------------------------------------------- Token: 0x01000003 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyTitleAttribute MemberRef #1 (0a000002) ------------------------------------------------------- Member: (0a000002) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #4 (01000004) ------------------------------------------------------- Token: 0x01000004 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyDescriptionAttribute MemberRef #1 (0a000003) ------------------------------------------------------- Member: (0a000003) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #5 (01000005) ------------------------------------------------------- Token: 0x01000005 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyConfigurationAttribute MemberRef #1 (0a000004) ------------------------------------------------------- Member: (0a000004) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #6 (01000006) ------------------------------------------------------- Token: 0x01000006 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyCompanyAttribute MemberRef #1 (0a000005) ------------------------------------------------------- Member: (0a000005) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #7 (01000007) ------------------------------------------------------- Token: 0x01000007 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyProductAttribute MemberRef #1 (0a000006) ------------------------------------------------------- Member: (0a000006) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #8 (01000008) ------------------------------------------------------- Token: 0x01000008 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyCopyrightAttribute MemberRef #1 (0a000007) ------------------------------------------------------- Member: (0a000007) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #9 (01000009) ------------------------------------------------------- Token: 0x01000009 ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyTrademarkAttribute MemberRef #1 (0a000008) ------------------------------------------------------- Member: (0a000008) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #10 (0100000a) ------------------------------------------------------- Token: 0x0100000a ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyCultureAttribute MemberRef #1 (0a000009) ------------------------------------------------------- Member: (0a000009) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #11 (0100000b) ------------------------------------------------------- Token: 0x0100000b ResolutionScope: 0x23000001 TypeRefName: System.Runtime.InteropServices.ComVisibleAttribute MemberRef #1 (0a00000a) ------------------------------------------------------- Member: (0a00000a) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: Boolean TypeRef #12 (0100000c) ------------------------------------------------------- Token: 0x0100000c ResolutionScope: 0x23000001 TypeRefName: System.Runtime.InteropServices.GuidAttribute MemberRef #1 (0a00000b) ------------------------------------------------------- Member: (0a00000b) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #13 (0100000d) ------------------------------------------------------- Token: 0x0100000d ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyVersionAttribute MemberRef #1 (0a00000c) ------------------------------------------------------- Member: (0a00000c) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #14 (0100000e) ------------------------------------------------------- Token: 0x0100000e ResolutionScope: 0x23000001 TypeRefName: System.Reflection.AssemblyFileVersionAttribute MemberRef #1 (0a00000d) ------------------------------------------------------- Member: (0a00000d) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: String TypeRef #15 (0100000f) ------------------------------------------------------- Token: 0x0100000f ResolutionScope: 0x23000001 TypeRefName: System.Diagnostics.DebuggableAttribute MemberRef #1 (0a00000e) ------------------------------------------------------- Member: (0a00000e) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: ValueClass DebuggingModes TypeRef #16 (01000010) ------------------------------------------------------- Token: 0x01000010 ResolutionScope: 0x0100000f TypeRefName: DebuggingModes TypeRef #17 (01000011) ------------------------------------------------------- Token: 0x01000011 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute MemberRef #1 (0a00000f) ------------------------------------------------------- Member: (0a00000f) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: I4 TypeRef #18 (01000012) ------------------------------------------------------- Token: 0x01000012 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute MemberRef #1 (0a000010) ------------------------------------------------------- Member: (0a000010) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #19 (01000013) ------------------------------------------------------- Token: 0x01000013 ResolutionScope: 0x23000001 TypeRefName: System.Console MemberRef #1 (0a000011) ------------------------------------------------------- Member: (0a000011) WriteLine: CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: String Assembly ------------------------------------------------------- Token: 0x20000001 Name : Program Public Key : Hash Algorithm : 0x00008004 Version: 1.0.0.0 Major Version: 0x00000001 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> Flags : [none] (00000000) CustomAttribute #1 (0c000001) ------------------------------------------------------- CustomAttribute Type: 0a000001 CustomAttributeName: System.Runtime.Versioning.TargetFrameworkAttribute :: instance void .ctor(class System.String) Length: 77 Value : 01 00 1c 2e 4e 45 54 46 72 61 6d 65 77 6f 72 6b > .NETFramework< : 2c 56 65 72 73 69 6f 6e 3d 76 34 2e 35 2e 31 01 >,Version=v4.5.1 < : 00 54 0e 14 46 72 61 6d 65 77 6f 72 6b 44 69 73 > T FrameworkDis< : 70 6c 61 79 4e 61 6d 65 14 2e 4e 45 54 20 46 72 >playName .NET Fr< : 61 6d 65 77 6f 72 6b 20 34 2e 35 2e 31 >amework 4.5.1 < ctor args: (".NETFramework,Version=v4.5.1") CustomAttribute #2 (0c000002) ------------------------------------------------------- CustomAttribute Type: 0a000002 CustomAttributeName: System.Reflection.AssemblyTitleAttribute :: instance void .ctor(class System.String) Length: 12 Value : 01 00 07 50 72 6f 67 72 61 6d 00 00 > Program < ctor args: ("Program") CustomAttribute #3 (0c000003) ------------------------------------------------------- CustomAttribute Type: 0a000003 CustomAttributeName: System.Reflection.AssemblyDescriptionAttribute :: instance void .ctor(class System.String) Length: 5 Value : 01 00 00 00 00 > < ctor args: ("") CustomAttribute #4 (0c000004) ------------------------------------------------------- CustomAttribute Type: 0a000004 CustomAttributeName: System.Reflection.AssemblyConfigurationAttribute :: instance void .ctor(class System.String) Length: 5 Value : 01 00 00 00 00 > < ctor args: ("") CustomAttribute #5 (0c000005) ------------------------------------------------------- CustomAttribute Type: 0a000005 CustomAttributeName: System.Reflection.AssemblyCompanyAttribute :: instance void .ctor(class System.String) Length: 5 Value : 01 00 00 00 00 > < ctor args: ("") CustomAttribute #6 (0c000006) ------------------------------------------------------- CustomAttribute Type: 0a000006 CustomAttributeName: System.Reflection.AssemblyProductAttribute :: instance void .ctor(class System.String) Length: 12 Value : 01 00 07 50 72 6f 67 72 61 6d 00 00 > Program < ctor args: ("Program") CustomAttribute #7 (0c000007) ------------------------------------------------------- CustomAttribute Type: 0a000007 CustomAttributeName: System.Reflection.AssemblyCopyrightAttribute :: instance void .ctor(class System.String) Length: 23 Value : 01 00 12 43 6f 70 79 72 69 67 68 74 20 c2 a9 20 > Copyright < : 20 32 30 31 35 00 00 > 2015 < ctor args: ("Copyright © 2015") CustomAttribute #8 (0c000008) ------------------------------------------------------- CustomAttribute Type: 0a000008 CustomAttributeName: System.Reflection.AssemblyTrademarkAttribute :: instance void .ctor(class System.String) Length: 5 Value : 01 00 00 00 00 > < ctor args: ("") CustomAttribute #9 (0c000009) ------------------------------------------------------- CustomAttribute Type: 0a00000a CustomAttributeName: System.Runtime.InteropServices.ComVisibleAttribute :: instance void .ctor(bool) Length: 5 Value : 01 00 00 00 00 > < ctor args: ( <can not decode> ) CustomAttribute #10 (0c00000a) ------------------------------------------------------- CustomAttribute Type: 0a00000b CustomAttributeName: System.Runtime.InteropServices.GuidAttribute :: instance void .ctor(class System.String) Length: 41 Value : 01 00 24 63 34 66 35 35 64 66 63 2d 63 38 63 66 > $c4f55dfc-c8cf< : 2d 34 66 64 37 2d 39 65 63 37 2d 37 62 63 35 37 >-4fd7-9ec7-7bc57< : 31 64 65 65 65 35 63 00 00 >1deee5c < ctor args: ("c4f55dfc-c8cf-4fd7-9ec7-7bc571deee5c") CustomAttribute #11 (0c00000b) ------------------------------------------------------- CustomAttribute Type: 0a00000d CustomAttributeName: System.Reflection.AssemblyFileVersionAttribute :: instance void .ctor(class System.String) Length: 12 Value : 01 00 07 31 2e 30 2e 30 2e 30 00 00 > 1.0.0.0 < ctor args: ("1.0.0.0") CustomAttribute #12 (0c00000c) ------------------------------------------------------- CustomAttribute Type: 0a00000e CustomAttributeName: System.Diagnostics.DebuggableAttribute :: instance void .ctor(value class DebuggingModes) Length: 8 Value : 01 00 07 01 00 00 00 00 > < ctor args: ( <can not decode> ) CustomAttribute #13 (0c00000d) ------------------------------------------------------- CustomAttribute Type: 0a00000f CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32) Length: 8 Value : 01 00 08 00 00 00 00 00 > < ctor args: (8) CustomAttribute #14 (0c00000e) ------------------------------------------------------- CustomAttribute Type: 0a000010 CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute :: instance void .ctor() Length: 30 Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx< : 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ceptionThrows < ctor args: () AssemblyRef #1 (23000001) ------------------------------------------------------- Token: 0x23000001 Public Key or Token: b7 7a 5c 56 19 34 e0 89 Name: mscorlib Version: 4.0.0.0 Major Version: 0x00000004 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> HashValue Blob: Flags: [none] (00000000) User Strings ------------------------------------------------------- 70000001 : ( 2) L"Hi" Coff symbol name overhead: 0 =========================================================== =========================================================== ===========================================================
幸運的是,ILDasm在合適的地方處理元數據表和聯合信息因此你沒必要轉換表單行信息。例如,在以前提過的轉儲,你看當ILDasm顯示一個TypeDef條目,在第一個TypeRef條目顯示以前,對應的成員定義信息和它一塊兒顯示。
你不須要所有理解你看到的任何事情。重要的是記住Program.exe包含一個名字叫作Program的TypeDef。這個類型識別一個從System.Object(一個被其它程序集引用的類型)繼承的公共密封類。Program類型也定義兩個方法:Main和.ctor(一個構造器)。
Main是一個公共的、靜態的方法它的代碼是IL(相對於本地CPU代碼,好比x86)。Main沒有返回類型也沒有參數。構造器方法(老是以.ctor的名字出現)是公共的,它的代碼也是IL。構造器沒有返回類型,沒有參數,有一個this指針,當方法被調用時會構造一個對象的內存,this指針會指向它。
我強烈支持你體驗ILDasm。它會爲你展現大量的信息,你看到的你會理解的更多,你將更理解CLR和它的能力。正如你將看到的,我在這本書中使用ILDasm至關多。
只是爲了有趣,讓咱們看一些關於Program.exe程序集的統計。當你選擇ILDasm的View/Statistics菜單項,會顯示如下信息。
File size : 4608 PE header size : 512 (496 used) (11.11%) PE additional info : 1535 (33.31%) Num.of PE sections : 3 CLR header size : 72 ( 1.56%) CLR meta-data size : 1508 (32.73%) CLR additional info : 0 ( 0.00%) CLR method headers : 2 ( 0.04%) Managed code : 20 ( 0.43%) Data : 2048 (44.44%) Unaccounted : -1089 (-23.63%) Num.of PE sections : 3 .text - 2048 .rsrc - 1536 .reloc - 512 CLR meta-data size : 1508 Module - 1 (10 bytes) TypeDef - 2 (28 bytes) 0 interfaces, 0 explicit layout TypeRef - 19 (114 bytes) MethodDef - 2 (28 bytes) 0 abstract, 0 native, 2 bodies MemberRef - 18 (108 bytes) CustomAttribute- 14 (84 bytes) Assembly - 1 (22 bytes) AssemblyRef - 1 (20 bytes) Strings - 630 bytes Blobs - 272 bytes UserStrings - 8 bytes Guids - 16 bytes Uncategorized - 168 bytes CLR method headers : 2 Num.of method bodies - 2 Num.of fat headers - 0 Num.of tiny headers - 2 Managed code : 20 Ave method size - 10
這裏你能夠看到文件大小(以字節爲單位)和組成文件的各部分大小(以字節和百分比爲單位)。從這個很小的Program.cs應用程序能夠看出,文件中大部分是PE頭文件和元數據。事實上,IL代碼僅僅佔用了20字節。固然,隨着應用程序增大,它將重用它的大多數類型和引用其它類型和程序集,當和總體的文件大小相比的時候元文件和頭文件信息縮減了。
注意 順便說一下,ILDasm.exe有一個影響文件大小的漏洞。特別的,你不能信任未包含在數目中的信息。 |
Combining Modules to Form an Assembly(組合模塊造成一個程序集)
在以前章節討論過的Program.exe文件不止是帶有元數據的PE文件;它也是一個程序集。一個程序集是一個或多個文件包含類型定義、資源文件的一個集合。其中的一個程序集文件選擇持有一個載貨單。載貨單是另外一個元數據表格的集合主要包含程序集的一部分文件的名字。它們也描述程序集的版本,區域,發佈者,公共導出類型,和組成程序集的全部文件。
CLR在程序集集合上工做;CLR老是先加載包含載貨單元數據表格的文件而後使用載貨單獲取在程序集中的其它文件的名字。這裏是一些你應該記住的程序集集合特徵:
*一個程序集定義可重用類型。
*一個程序集經過版本號標記。
*一個程序集能夠有與之相關的安全信息。
一個程序集的特有文件沒有這些屬性——指望文件包含載貨單元數據表格。
程序集,版本,安全和使用類型,你必須把它們放在模塊中由於它們是程序集的一部分。在大多數案例中,一個程序集由一個單獨的文件組成,就像以前的Program.exe例子同樣。然而,一個程序集也能夠由多個文件組成:一些帶有元數據的PE文件和一些資源文件好比:.gif或.jpg文件。它能夠幫助你考慮一個程序集是合理的EXE仍是一個DLL。
我肯定大家大多數人讀到這都很疑惑爲何微軟引進程序集概念。緣由是一個程序集容許你從邏輯和物理的概念上解耦可重用類型。例如,一個程序集能夠又幾個類型組成。你能夠把經常使用的類型方法放到一個文件中不經常使用的放到另外一個文件中。假如你的程序集是經過Internet下載部署,不經常使用類型可能永遠也不會下載到客戶端若是客戶端從不訪問那個類型。例如,一個獨立的軟件供應商(independent software vendor,ISV)專攻UI控件可能選擇在一個分離的模塊(知足微軟的商標要求)中實現Active Accessibility類型。只有須要額外訪問性特徵的用戶才須要下載這個模塊。
在應用程序的配置文件中經過指定codeBase元素(在第三章討論)你能夠配置一個應用程序下載程序集文件。codeBase元素標識一個URL指向能夠找到一個程序集全部文件的地方。當嘗試下載一個程序集的文件,CLR獲取codeBase的元素的URL而後檢查機器的下載緩存文件是否是已經有了。若是是,文件會被加載。若是文件再也不緩存中,CLR從URL指向的位置下載文件到緩存。假如文件找不到,CLR在運行時拋出一個FileNotFoundException異常。
我指出三個使用多程序集的緣由:
*在分隔的文件之間直接分配你的多個類型,容許文件逐步下載就像Internet下載場景描述的那樣。把多個類型分配到隔離的文件同時容許爲你的應用程序部分或逐步打包和部署。
*你能夠添加資源或者數據文件到你的程序集。例如,你能夠有一個類型用於計算一些保險信息。這個類型可能須要訪問一些保險精算的表格來作計算。代替把保險精算表格嵌入到你的源代碼,你可使用一個工具(好比程序集連接者,AL.exe,稍後討論)使數據文件被認爲是程序集的一部分。順便提一下,着這個數據文件能夠是任何格式的——一個文本文件,一個微軟Excel電子表格,一個微軟Word表,或者你喜歡的不管什麼——只要你的應用程序知道怎麼轉換文件的內容。
*你能夠建立由不一樣語言實現的類型組成的程序集。例如,你能夠實現一些C#的類型,一些微軟Visual Basic的類型,和一些由其它語言實現的。當你編譯C#源碼類型編譯器生成一個分離的模塊。接下來你可使用一個工具把全部的模塊組合成一個程序集。對於使用程序集的開發者,程序集呈現的只是一堆類型;開發者甚至不知道程序集使用了不一樣的語言開發。順便提一下,假如你喜歡,你能夠運行ILDasm.exe並把它所有轉爲IL源碼文件。ILAsm.exe將會產生一個包含全部類型的文件。這個技術要求你的源碼編譯器只生成IL代碼。