本文是爲了學習程序集而整理的網上資料,主要包括兩個部分,概念和使用,前部分講怎樣理解程序集,後部分講述怎樣使用的細節。
程序集與託管模塊的概念
我相信現在大家對程序集和託管模塊分別是什麼以及兩者間的關係有了較好的理解。但是如果有源代碼輔助一下那就更好了。是的。對程序員來說,源代碼比什麼都親切。好的,下面就舉兩個簡單的例子(用C#語言表述)。一個是單模塊程序集,一個是多模塊程序集。兩者都是前臺程序集(後綴名是exe)。我用Visual C# 2003 集成開發環境試了一下,竟然發現它不支持多程序集的開發(希望是我沒有找到)。沒關係,我們還有DotNet FrameWork SDK呢,不用怕。它自帶的C#編譯器csc.exe很好用。至於csc的用法我就不多說了。
/* *hello.cs 在控制檯上顯示一行字符串 */ using System; namespace nsApp { public class CEnterPoint { static void Main() { Console.WriteLine("Hello, verybody!"); } } }
/* Dog.cs */ using System; namespace nsZoo { public class Dog { public void SayHello() { Console.WriteLine("I am a dog, wang wang wang"); } } } /* Cat.cs */ using System; namespace nsZoo { public class Cat { public void SayHello() { Console.WriteLine("I am a cat, miao miao miao"); } } } /* Main.cs */ using System; using nsZoo; namespace nsApp { class CEnterPoint { static void Main(string[] args) { Dog aDog = new Dog(); Cat aCat = new Cat(); aDog.SayHello(); aCat.SayHello(); Console.ReadLine(); } } }
任何時候構建一個EXE或DLL文件時必須使用/t:library編譯參數創建與該應用程序相對應的包含清單(manifest)的程序集,清單(manifest)記錄了.NET運行時程序集的相關信息。另外,使用/t:module編譯參數,創建一個以.netmodule爲擴展名的DLL 文件,這個DLL文件不包含清單。換句話說,雖然邏輯上模塊(module)依然是一個DLL文件,但是它不屬於程序集,當編譯應用程序或使用程序集生成工具(the Assembly Generation tool)時必須使用/addmodule開關將這個DLL文件加入到其他程序集中。
清單數據(Manifest Data)
程序集的清單有不同的方法存儲。若編譯一個獨立的應用程序或DLL文件,清單與程序或DLL的PE結合在一起,這稱爲單一文件的程序集(single-file assembly)。多文件程序集(multifile assembly)是指清單以另外一個獨立實體的形式作爲程序集的一部分,或者作爲程序集中的一個模塊。
同樣程序集的定義很大程度取決你如何使用它。從客戶的角度來看,程序集是模塊、導出類型、(必需、可選)資源的命名和版本化(versioned)的集合。從程序集創建者的角度來看,程序集是客戶能使用的一組相關聯的模塊、類型、資源、導出的方法的封裝包(mean of packaging)。也就是說,清單是程序集和用戶之間溝通的橋樑。下面列出程序集中清單包含的信息:
· 程序集名稱(Assembly name)
· 版本信息(Versioning information)
版本信息是由四個不同的部分組成的版本號,分別是主版號、子版號、構建序號、修訂號。
· (可選的)共享名和帶符號的程序集散列(An(Optional)shared name and signed assembly hash)
主要是關於程序集部署的相關信息。
· 文件(Files)
包含在程序集中的文件列表。
· 引用的程序集(Referenced assemblies)
直接引用的外部程序集列表。
· 類型(Types)
程序集內部的所有類型及模塊映像包含的類型的列表。(This is list of all types in the assembly with a mappig to containing the type.)
· 安全(Security)
安全權限的列表,權限已明確地被程序集拋棄。(permissions This is a list of security permissions that are explicity refused by the assembly.)
· 定製屬性(Custom attributes)
· 產品信息 包含公司、商標、產品及版權信息。
程序集的優勢
· 程序集的封裝:將多個模塊封裝到一個物理文件,起到性能優化的作用。當你創建一個應用程序並使用多文件程序集時,.NET運行時只需加載相關的模塊。這種策略起到減少應用程序的工作集。
· 程序集的部署
在.NET框架中程序集是最小的部署單元。雖然它誘人的說,程序集是部署應用程序的方法,但是技術上並不是如此。許多關於程序集部署的正確的觀點是:在.NET中程序集應該看作一個窗體的類部署(class deployment)-就像Win32中的一個DLL文件。一個單獨的應用程序是由多個程序組成的。
因爲程序集是自描述的(self-desciribing),所以部署程序集最簡單的方法就是直接把程序集複製到目的文件夾。當嘗試運行包含這個程序集的應用程序時,清單將向.NET運行時提供包含在這個程序集中的方法信息。另外,清單(manifest)也向應用程序提供該程序集所引用的外部程序集的相關信息。
許多通過公共方法部署的程序集依然是私有的程序集,即程序集被複制到文件夾,但它們不是共享的。缺省情況下程序集是私有的,除非明確地指定程序集爲共享程序集(shared assembly)。
· 程序集的版本
使用程序集另外一個主要優勢是程序集內置了版本號。程序集中止了DLL地獄。DLL地獄是指當一個應用程序重寫了一個被其他應用程序引用的DLL文件,而該應用程序引用的是低版本的同名DLL文件,這就造成了引用低版本DLL的應用程序運行出錯,這種情況。雖然Win32資源文件格式內置了版本資源類型,但是操作系統不會強制執行任何版本控制,以便依賴這個DLL文件的應用程序能正常運行。
基於這個問題,清單不僅包含程序集本身的版本信息,也包含了該程序集所有被引用的程序集和這些程序集的版本信息。因爲有了這個體系結構,.NET運行時能確保版本規則被支持,當出現新版本的程序集時,版本不兼容的共享DLLs由操作系統自動安裝,使得應用程序可以繼續正常運行。
用多個模塊創建程序集(Creating Assembies with Multiple Modules)
// NetModuleTestServer.cs // Build with the following command-line switches: // csc /t:library NetModuleTestServer.cs public class NetModuleTestServer { public static void Bar() { System.Console.WriteLine("NetModuleTestServer.Bar(NetModuleTestServer.netmodule)"); } } // NetModuleTestClientApp.cs // Build with the following command-line switches: // // csc addmodule:NetModuleServer.netmodule NetModuleTestClientApp.cs using System; using System.Diagnostics; using System.Reflection; class NetModuleTestClientApp { public static void Main() { Assembly DLLAssembly = Assembly.GetAssembly( typeof(NetModuleTestServer)); Console.WriteLine(""nNetModuleTestServer.dll Assembly " + "Information"); Console.WriteLine(""t" + DLLAssembly); Process p = Process.GetCurrentProcess(); string AssemblyName = p.ProcessName + ".exe"; Assembly ThisAssembly = Assembly.LoadFrom(AssemblyName); Console.WriteLine("NetModuleTestClient.exe Assembly " + "Information"); Console.WriteLine(""t" + ThisAssembly + ""n"); Console.WriteLine("Calling NetModuleTestServer.Bar"); NetModuleTestServer.Bar(); } }
若要創建一個在多個應用程序使用的程序集,並且版本信息相當重要,則必須共享程序集。若要共享程序集,必須給程序集指定強名稱。可以通過.NET SDK提供強名稱工具(the Strong Name tool)創建強名稱。使用強名稱有四個主要理由:
· 它是.NET生成唯一的全局名稱的機制
· 因爲強名稱工具生成的密鑰對包含了一個簽名,你可以判斷程序集創建之後是否被篡改。(Because the generated key pair includes a singature,you can tell whether an assembly has been tampered with after its original creation.)
· 強名稱能防止第三方在你構建的程序集的基礎上發表新的版本。這是因爲密鑰對包含簽名起了作用,因爲第三方不知道你的私鑰。
· 當.NET加載程序集時,運行時會自動驗證調用者是否合法。(When .NET load an assembly,the runtime can verify that the assembly came from the publisher that the caller is expecting.)
創建強名稱的步驟:
· 使用強名稱工具爲程序集創建一個強名稱。示例:
sn -k InsideCSharp.key
· 在客戶端源代碼文件中添加屬性AssemblyKeyFile,把強名稱指定給程序集。
使用全局程序集緩存工作
.NET每次加載程序集時都會創建一個代碼緩存區,通常成爲全局程序集緩存。使用全局程序集緩存有三個作用:
· 存儲從Internet或文件服務器下載的代碼。注:從一個特定的應用程序加載的代碼存儲在緩衝中的私有區域,以防止其他程序集訪問。
· 存儲被多個.NET應用程序共享的組件數據。利用全局緩存工具將程序集加載到緩存全局區域,使得該程序集可以被本地機器的所有應用程序訪問。
· 程序集的本地代碼是預運行時編譯的,同時存儲在全局程序集緩存區。(native code versions of assemblies that have been preJITted are stored in the cache.)
緩存視圖
1、在windows"assembly文件夾中可以查看程序集的相關信息,如版本號、語言(culture)、公鑰標記等等信息。
// DllTestServer.cs // Build with the following command-line switches: // csc /t:library DllTestServer.cs public class DllTestServer { public static void Foo() { System.Console.WriteLine("DllTestServer.Foo " + "(DllTestServer.DLL)"); } } // DllTestClient.cs // Build with the following command-line switches: // csc DllTestClient.cs /r:DllTestServer.dll using System; using System.Diagnostics; using System.Reflection; class DllTestClientApp { public static void Main() { Assembly DLLAssembly = Assembly.GetAssembly(typeof(DllTestServer)); Console.WriteLine(""nDllTestServer.dll Assembly " + "Information"); Console.WriteLine(""t" + DLLAssembly); Process p = Process.GetCurrentProcess(); string AssemblyName = p.ProcessName + ".exe"; Assembly ThisAssembly = Assembly.LoadFrom(AssemblyName); Console.WriteLine("DllTestClient.exe Assembly " + "Information"); Console.WriteLine(""t" + ThisAssembly + ""n"); Console.WriteLine("Calling DllTestServer.Foo"); DllTestServer.Foo(); } }