程序集是 .NET Framework 應用程序的構造塊;程序集構成了部署、版本控制、重復使用、激活范圍控制和安全權限的基本單元。程序集是為協(xié)同工作而生成的類型和資源的集合,這些類型和資源構成了一個邏輯功能單元。程序集為公共語言運行庫提供它識別類型實現(xiàn)所需的信息。對于運行庫,類型不存在于程序集上下文之外。
程序集是 .NET Framework 編程的基本組成部分。程序集執(zhí)行以下功能:
- 包含公共語言運行庫執(zhí)行的代碼。如果可移植可執(zhí)行 (PE) 文件沒有相關聯(lián)的程序集清單,則將不執(zhí)行該文件中的 Microsoft 中間語言 (MSIL) 代碼。請注意,每個程序集只能有一個入口點(即 DllMain、WinMain 或 Main)。
- 程序集形成安全邊界。程序集就是在其中請求和授予權限的單元。有關應用于程序集的安全邊界的更多信息,請參見程序集安全注意事項。
- 程序集形成類型邊界。每一類型的標識均包括該類型所駐留的程序集的名稱。在一個程序集范圍內(nèi)加載的 MyType 類型不同于在其他程序集范圍內(nèi)加載的 MyType 類型。
- 程序集形成引用范圍邊界。程序集的清單包含用于解析類型和滿足資源請求的程序集元數(shù)據(jù)。它指定在該程序集之外公開的類型和資源。該清單還枚舉它所依賴的其他程序集。
- 程序集形成版本邊界。程序集是公共語言運行庫中最小的可版本化單元,同一程序集中的所有類型和資源均會被版本化為一個單元。程序集的清單描述您為任何依賴項程序集所指定的版本依賴性。有關版本控制的更多信息,請參見程序集版本控制。
- 程序集形成部署單元。當一個應用程序啟動時,只有該應用程序最初調(diào)用的程序集必須存在。其他程序集(例如本地化資源和包含實用工具類的程序集)可以按需檢索。這就使應用程序在第一次下載時保持精簡。有關部署程序集的更多信息,請參見部署應用程序。
- 程序集是支持并行執(zhí)行的單元。有關運行多個程序集版本的更多信息,請參見程序集和并行 (side-by-side) 執(zhí)行。
程序集可以是靜態(tài)的或動態(tài)的。靜態(tài)程序集可以包括 .NET Framework 類型(接口和類),以及該程序集的資源(位圖、JPEG 文件、資源文件等)。靜態(tài)程序集存儲在磁盤上的可移植可執(zhí)行 (PE) 文件中。您還可以使用 .NET Framework 來創(chuàng)建動態(tài)程序集,動態(tài)程序集直接從內(nèi)存運行并且在執(zhí)行前不存儲到磁盤上。您可以在執(zhí)行動態(tài)程序集后將它們保存在磁盤上。
有幾種創(chuàng)建程序集的方法。您可以使用過去用來創(chuàng)建 .dll 或 .exe 文件的開發(fā)工具,例如 Visual Studio .NET。您可以使用在 .NET Framework SDK 中提供的工具來創(chuàng)建帶有在其他開發(fā)環(huán)境中創(chuàng)建的模塊的程序集。您還可以使用公共語言運行庫 API(例如 Reflection.Emit)來創(chuàng)建動態(tài)程序集。
程序集旨在簡化應用程序部署并解決在基于組件的應用程序中可能出現(xiàn)的版本控制問題。
最終用戶和開發(fā)人員比較熟悉當今基于組件的系統(tǒng)所產(chǎn)生的版本控制和部署問題。一些最終用戶曾經(jīng)歷過在計算機上安裝新應用程序失敗的事情,發(fā)現(xiàn)已有應用程序突然停止工作。許多開發(fā)人員花費了大量的時間來使所有必需的注冊表項保持一致,以便激活 COM 類。
通過在 .NET Framework 中使用程序集,許多開發(fā)問題得以解決。因為程序集是不依賴于注冊表項的自述組件,所以程序集使無相互影響的應用程序安裝成為可能。程序集還使應用程序的卸載和復制得以簡化。
版本控制問題
目前,Win32 應用程序存在兩類版本控制問題:
- 版本控制規(guī)則不能在應用程序的各段之間表達,并且不能由操作系統(tǒng)強制實施。目前的辦法依賴于向后兼容,而這通常很難保證。接口定義一經(jīng)發(fā)布就必須是靜態(tài)的,并且單段代碼必須保持與以前版本向后兼容。此外,通常要對代碼進行設計,以便在任意給定時間在計算機上只能出現(xiàn)和執(zhí)行代碼的一個版本。
- 沒有辦法在創(chuàng)建到一起的多套組件集與運行時提供的那套組件之間保持一致。
這兩類版本控制問題結合在一起產(chǎn)生了 DLL 沖突,在這些沖突中,安裝一個應用程序可能會無意間破壞現(xiàn)有的應用程序,因為所安裝的某個軟件組件或 DLL 與以前的版本不完全向后兼容。出現(xiàn)此情況后,系統(tǒng)不支持診斷和解決此問題。
最終解決 DLL 沖突
Microsoft? Windows? 2000 開始致力于解決這些問題。它所提供的兩個功能可以部分地解決 DLL 沖突:
- Windows 2000 使您能夠創(chuàng)建這樣的客戶端應用程序,其中的 .dll 依賴文件與該應用程序的 .exe 文件位于相同的目錄中。Windows 2000 經(jīng)過配置,能夠在檢查完全限定的路徑或搜索常規(guī)路徑前,檢查 .exe 文件所在目錄中的組件。這使組件可以獨立于其他應用程序所安裝和使用的組件。
- Windows 2000 鎖定 System32 目錄中隨操作系統(tǒng)提供的文件,使這些文件不會在安裝應用程序時被無意替換。
公共語言運行庫使用程序集來繼續(xù)致力于 DLL 沖突的徹底解決。
程序集解決方案
為了解決版本控制問題以及導致 DLL 沖突的其余問題,運行庫使用程序集來執(zhí)行以下功能:
- 使開發(fā)人員能夠指定不同軟件組件之間的版本規(guī)則。
- 提供強制實施版本控制規(guī)則的結構。
- 提供允許同時運行多個版本的軟件組件(稱作并行執(zhí)行)的基本結構。
通常,靜態(tài)程序集可能由以下四個元素組成:
- 程序集清單,包含程序集元數(shù)據(jù)。
- 類型元數(shù)據(jù)。
- 實現(xiàn)這些類型的 Microsoft 中間語言 (MSIL) 代碼。
- 資源集。
只有程序集清單是必需的,但也需要類型或資源來向程序集提供任何有意義的功能。
程序集中的這些元素有分組幾種方法。您可以將所有元素分組到單個物理文件中,如下圖所示。
單文件程序集

或者,可以將一個程序集的元素包含在幾個文件中。這些文件可能是編譯代碼的模塊 (.netmodule)、資源(例如 .bmp 或 .jpg 文件)或應用程序所需的其他文件。在您希望組合以不同語言編寫的模塊并優(yōu)化應用程序的下載過程時,可創(chuàng)建一個多文件程序集,優(yōu)化下載過程的方法是將很少使用的類型放入只在需要時才下載的模塊中。
在下圖中,一個假想應用程序的開發(fā)人員已選擇將一些實用工具代碼單獨放入另一個模塊中,同時在其原文件中保留一個較大的資源文件(在此例中為一個 .bmp 圖像)。.NET Framework 只在文件被引用時下載該文件;通過將很少引用的代碼保留在獨立于應用程序的文件中來優(yōu)化代碼下載。
多文件程序集

注意 構成多文件程序集的那些文件實際上并非由文件系統(tǒng)來鏈接。它們而是通過程序集清單進行鏈接,公共語言運行庫將這些文件作為一個單元來管理。
在此插圖中,所有三個文件均屬于一個程序集,如 MyAssembly.dll 所包含的程序集清單文件中所述。對于該文件系統(tǒng),這三個文件是三個獨立的文件。請注意,文件 Util.netmodule 被編譯為一個模塊,因為它不包含任何程序集信息。在創(chuàng)建了程序集后,該程序集清單就被添加到 MyAssembly.dll,指示程序集與 Util.net 模塊和 Graphic.bmp 的關系。
現(xiàn)在設計源代碼時,您會作出有關如何將應用程序的功能劃分到一個或多個文件的明確的決定。在設計 .NET Framework 代碼時,您也將作出類似的決定,即如何將應用程序的功能劃分到一個或多個程序集中。
安裝有公共語言運行庫的每臺計算機都具有稱為全局程序集緩存的計算機范圍內(nèi)的代碼緩存。全局程序集緩存中存儲了專門指定給由計算機中若干應用程序共享的程序集。
應當僅在需要時才將程序集安裝到全局程序集緩存中以進行共享。一般原則是:程序集依賴項保持專用,并在應用程序目錄中定位程序集,除非明確要求共享程序集。另外,不必為了使 COM interop 或非托管代碼可以訪問程序集而將程序集安裝到全局程序集緩存。
注意 在有些情況下,您顯然不希望將程序集安裝到全局程序集緩存中。如果您將組成應用程序的某個程序集置于全局程序集緩存中,則將不再能夠通過使用 xcopy 命令復制應用程序目錄來復制或安裝該應用程序。您還必須在全局程序集緩存中移動該程序集。
有若干方法可以將程序集部署到全局程序集緩存中:
- 使用專用于全局程序集緩存的安裝程序。該方法是將程序集安裝到全局程序集緩存的首選方法。
- 使用 .NET Framework SDK 所提供的名為全局程序集緩存工具 (Gacutil.exe) 的開發(fā)人員工具。
- 使用 Windows 資源管理器將程序集拖到緩存中。
注意 在部署方案中,應該使用 Windows 安裝程序 2.0 將程序集安裝到全局程序集緩存中。我們一般只在開發(fā)方案中使用 Windows 資源管理器或全局程序集緩存工具,這是因為它們不提供使用 Windows 安裝程序時可以提供的程序集引用計數(shù)功能和其他功能。
管理員通常使用訪問控制列表 (ACL) 來保護 WINNT 目錄,以控制寫入和執(zhí)行訪問。因為全局程序集緩存安裝在 WINNT 目錄中,它繼承了目錄的 ACL。建議只允許具有“管理員”權限的用戶從全局程序集緩存中刪除文件。
在全局程序集緩存中部署的程序集必須具有強名稱。將一個程序集添加到全局程序集緩存時,必須對構成該程序集的所有文件執(zhí)行完整性檢查。緩存執(zhí)行這些完整性檢查以確保程序集未被篡改(例如,當文件已更改但清單未反映此更改時)。
在您生成程序集時,您可以指定該程序集運行所需的一組權限。是否將特定的權限授予程序集是基于證據(jù)的。
使用證據(jù)有兩種截然不同的方式:
- 將輸入證據(jù)與加載程序所收集的證據(jù)合并,以創(chuàng)建用于策略決策的最終證據(jù)集。使用這種語義的方法包括 Assembly.Load、Assembly.LoadFrom 和 Activator.CreateInstance。
- 原封不動地使用輸入證據(jù)作為用于策略決策的最終證據(jù)集。使用這種語義的方法包括 Assembly.Load(byte[]) 和 AppDomain.DefineDynamicAssembly()。
通過在將運行程序集的計算機上設置安全策略,您可以授予一些可選的權限。如果您希望代碼可以處理所有潛在的安全異常,可以執(zhí)行以下操作之一:
- 為您的代碼必須具有的所有權限插入權限請求,并預先處理在未授予權限時發(fā)生的加載時錯誤。
- 不要使用權限請求來獲取您的代碼可能需要的權限,但一定要準備處理在未授予權限時發(fā)生的安全異常。
注意 安全性是一個較為復雜的領域,您將要作出很多選擇。有關更多信息,請參見安全性的基礎概念。
在加載時,程序集的證據(jù)用作安全策略的輸入。安全策略是由企業(yè)和計算機的管理員以及用戶策略設置建立的,它在執(zhí)行時確定向所有托管代碼授予的權限組。可以為程序集的發(fā)行者建立(如果該程序集具有符號代碼簽名)安全策略,或者為該程序集的下載 Web 站點和區(qū)域(就 Internet Explorer 而言)建立安全策略,也可以為該程序集的強名稱建立該策略。例如,一臺計算機的管理員可以建立這樣一種安全策略:它允許從某一 Web 站點下載由指定軟件公司簽發(fā)用以訪問計算機上的數(shù)據(jù)庫的所有代碼,但不授予對該計算機磁盤的寫訪問權。
具有強名稱的程序集和符號代碼
您可以通過兩種不同但互為補充的方式簽發(fā)程序集:使用強名稱或使用 Signcode.exe。使用強名稱對程序集簽名的過程將向包含程序集清單的文件添加公鑰加密。強名稱簽名幫助驗證名稱的唯一性,避免名稱哄騙,并在解析引用時向調(diào)用方提供某些標識。
但是,沒有任何信任級別與一個強名稱相關聯(lián),這使符號代碼十分重要。符號代碼要求發(fā)行者向第三方證書頒發(fā)機構證實其標識并獲取證書。然后此證書將嵌入到您的文件中,并且管理員能夠使用該證書來決定是否相信這些代碼的真實性。
您可以將強名稱和符號代碼數(shù)字簽名一起提供給程序集,或者您可以單獨使用其中之一。符號代碼一次只能簽發(fā)一個文件,對于多文件程序集,您可以簽發(fā)包含程序集清單的文件。強名稱存儲在包含程序集清單的文件中,但符號代碼簽名存儲在該程序集清單所在的可移植可執(zhí)行 (PE) 文件中專門保留的槽中。當您已經(jīng)具有依賴于符號代碼簽名的信任層次結構或者當您的策略只使用密鑰部分并且不檢查信任鏈時,就可以使用程序集的符號代碼簽發(fā)功能(帶或不帶強名稱)。
注意 在一個程序集上同時使用強名稱和符號代碼簽名時,必須首先分配強名稱。
公共語言運行庫還將執(zhí)行哈希驗證;程序集清單包含構成該程序集的所有文件的列表,包括當生成清單時存在的每一文件的散列。在加載每一文件時,其內(nèi)容被散列化并與清單中存儲的哈希值進行比較。 如果兩個哈希值不匹配,則無法加載該程序集。
因為強名稱和符號代碼確保了完整性,因此您可以將代碼訪問安全策略建立在這兩種形式的程序集證據(jù)的基礎上。強名稱和符號代碼簽發(fā)通過數(shù)字簽名和證書確保完整性。上面提到的所有技術(哈希驗證、強名稱和符號代碼)共同作用,確保程序集沒有做過任何方式的改動。
使用公共語言運行庫的程序集的所有版本控制都在程序集級別上進行。一個程序集的特定版本和依賴程序集的版本在該程序集的清單中記錄下來。除非被配置文件(應用程序配置文件、發(fā)行者策略文件和計算機的管理員配置文件)中的顯式版本策略重寫,否則運行庫的默認版本策略是,應用程序只與它們生成和測試時所用的程序集版本一起運行。
注意 僅對具有強名稱的程序集進行版本控制。
運行庫執(zhí)行以下幾步來解析程序集綁定請求:
- 檢查原程序集引用,以確定該程序集的版本是否被綁定。
- 檢查所有適用的配置文件以應用版本策略。
- 通過原程序集引用和配置文件中指定的任何重定向來確定正確的程序集,并且確定應綁定到調(diào)用程序集的版本。
- 檢查全局程序集緩存和在配置文件中指定的基本代碼,然后使用在運行庫如何定位程序集中解釋的探測規(guī)則檢查該應用程序的目錄和子目錄。
下圖說明了這些步驟。
解析程序集綁定請求

有關配置應用程序的更多信息,請參見配置文件。有關綁定策略的更多信息,請參見運行庫如何定位程序集。
版本信息
每一程序集都用兩種截然不同的方法來表示版本信息:
- 程序集的版本號,該版本號與程序集名稱及區(qū)域性信息都是程序集標識的組成部分。該號碼將由運行庫用來強制實施版本策略,它在運行時的類型解析進程中起著重要的作用。
- 信息性版本,這是一個字符串,表示僅為提醒的目的而包括的附加版本信息。
程序集版本號
每一程序集都有一個版本號作為其標識的一部分。因此,如果兩個程序集具有不同的版本號,運行庫就會將它們視作完全不同的程序集。此版本號實際表示為具有以下格式的四部分號碼:
<major version>.<minor version>.<build number>.<revision>
例如,版本 1.5.1254.0 中的 1 表示主版本,5 表示次版本,1254 表示內(nèi)部版本號,而 0 則表示修訂號。
版本號與其他標識信息(包括程序集名稱和公鑰,以及與該應用程序所連接的其他程序集的關系和標識有關的信息)一起存儲在程序集清單中。
在生成程序集時,開發(fā)工具將把每一個被引用程序集的依賴項信息記錄在程序集清單中。運行庫將這些版本號與管理員、應用程序或發(fā)行者設置的配置信息結合使用,以加載被引用程序集的正確版本。
為進行版本控制,運行庫會區(qū)分常規(guī)程序集和具有強名稱的程序集。只對具有強名稱的程序集執(zhí)行版本檢查。
有關指定版本綁定策略的信息,請參見配置文件。有關運行庫如何使用版本信息查找特定程序集的信息,請參見運行庫如何定位程序集。
程序集信息性版本
信息性版本是一個字符串,它僅出于提醒的目的將附加的版本信息附加到一個程序集;此信息不在運行時使用。基于文本的信息性版本相當于產(chǎn)品的營銷廣告、包裝或產(chǎn)品名稱,不被運行庫使用。例如,信息性版本可以是“公共語言運行庫版本 1.0”或“NET Control SP 2”。
信息性版本使用自定義屬性 System.Reflection.AssemblyInformationalVersionAttribute 來表示。有關信息性版本屬性的更多信息,請參見設置程序集屬性。
對于大多數(shù) .NET Framework 應用程序而言,您可以在以下位置找到構成該應用程序的程序集,這些位置包括:該應用程序的目錄中,該應用程序目錄的子目錄中,或全局程序集緩存中(如果該程序集是共享的話)。可以通過在配置文件中使用 <codeBase> 元素重寫公共語言運行庫查找某一程序集的位置。如果該程序集沒有強名稱,則使用 <codeBase> 元素指定的位置將被限制在應用程序目錄或子目錄中。如果程序集具有強名稱,則 <codeBase> 能夠指定計算機或網(wǎng)絡上的任意位置。
當在使用非托管代碼或 COM interop 應用程序的過程中查找程序集的位置時,類似的規(guī)則同樣適用:如果該程序集將由多個應用程序共享,則此程序集應被安裝到全局程序集緩存中。和非托管代碼一起使用的程序集必須作為類型庫導出并注冊。由 COM interop 使用的程序集必須在目錄中進行注冊,盡管有些情況下會自動進行此注冊。
程序集和并行 (side-by-side) 執(zhí)行
并行執(zhí)行是在同一臺計算機上存儲和執(zhí)行應用程序或組件的多個版本的能力。這意味著在同一臺計算機上可以同時有運行庫的多個版本,并且可以有使用其中某個運行庫版本的應用程序和組件的多個版本。并行執(zhí)行使您能夠更多地控制應用程序綁定到的組件版本和應用程序使用的運行庫版本。
支持并行存儲和執(zhí)行同一程序集的不同版本是強命名中不可缺少的部分,這種支持內(nèi)置于運行庫基礎結構中。因為強名稱程序集的版本號是其標識的一部分,所以運行庫能夠在全局程序集緩存中存儲同一程序集的多個版本,并且在運行時加載這些程序集。
盡管運行庫使您能夠創(chuàng)建并行應用程序,但并行執(zhí)行并不是自動進行的。有關創(chuàng)建并行執(zhí)行的應用程序的更多信息,請參見并行執(zhí)行的編程暗示。
-----------------------------------------
相關章節(jié)
- 使用程序集編程
- 描述程序集上的屬性的創(chuàng)建、簽名和設置過程。
- 發(fā)出動態(tài)程序集
- 描述如何創(chuàng)建動態(tài)程序集。