使用.NET框架簡化公布與處理DLL Hell問題(2)
發表時間:2024-02-10 來源:明輝站整理相關軟件相關文章人氣:
[摘要]版本與共享 DLL Hell 一個主要目的就是共享當前在基于組件的系統中使用的模型。默認情況下,單獨的軟件組件由機器上的多個應用程序共享。例如,每次一個安裝程序復制一個 DLL 到系統目錄或在 COM 注冊表中注冊一個類,該代碼將潛在地影響其他運行在機器上的應用程序。實際上,如果一個已存在的應用程...
版本與共享
DLL Hell 一個主要目的就是共享當前在基于組件的系統中使用的模型。默認情況下,單獨的軟件組件由機器上的多個應用程序共享。例如,每次一個安裝程序復制一個 DLL 到系統目錄或在 COM 注冊表中注冊一個類,該代碼將潛在地影響其他運行在機器上的應用程序。實際上,如果一個已存在的應用程序使用共享組件的前一個版本,那么該應用程序將自動使用新版本。如果共享組件是嚴格向后兼容的這當然更好,但如果不可能,在許多情況下維護向后兼容是很困難的。如果沒有維持向后兼容或不能維持,作為其他應用程序安裝時的側面影響經常導致應用程序中斷。
.NET 設計方針的一個原則就是隔離組件(或匯編)。隔離一個匯編的意思是一個匯編只能由一個應用程序訪問—不是由機器上的多個應用程序共享并且不可能因其他應用程序對系統的改變而影響。隔離賦予開發者對應用程序所用代碼的絕對控制。隔離,或應用程序專用匯編期望在 .NET 應用程序中是默認的。隔離組件的趨勢在 Microsoft Windows 2000 中隨著 .local 文件的引入已經開始。該文件用于努力定位所需組件時使 OS Loader 和 COM 首先從應用程序目錄查找。(請參閱 MSDN Library 中的相關文檔,Implementing Side-by-Side Component Sharing in Applications(英文)。)
然而,有些情況下在應用程序之間共享匯編是必要的。很明顯每個應用程序都有自己的 System.Winforms、System.ASP 或公用的 Web 表格控件的副本是沒有意義的。
在 .NET 中,在應用程序之間共享代碼是明確的決定。共享匯編需要一些附加的需求。特別是,共享匯編應該支持相同的匯編并排多個版本安裝和運行在相同的機器上,或者甚至在相同的進程中,在相同的時間。另外,共享匯編有更嚴格的命名需要。例如,一個共享的匯編必須有一個全局唯一的名稱。
隔離和共享的需要導致我們考慮兩種匯編。這是個相當松散的集合,在這兩種匯編之間沒有實際的結構,但它們如何使用是不同的:專用于某個應用程序或與許多應用程序共享。
應用程序專用匯編
應用程序專用匯編是只對某個應用程序可視的匯編。我們期望這是 .NET 應用程序最普通的情況,因為 .NET 框架幫助建立從其它應用程序引起的系統變化中隔離的應用程序。
專用匯編的命名需求很簡單:匯編名稱必須在應用程序中是唯一的。沒必要起全局唯一的名稱。保持名稱唯一不是問題因為應用程序開發者完全控制哪個匯編與應用程序隔離。
應用程序專用匯編部署在使用它們的應用程序目錄結構中。專用匯編可以直接放在應用程序的目錄或它的子目錄中。通用語言運行時間通過稱為 probing 的進程查找這些匯編。"Probing" 是匯編名稱到包含清單的文件名稱之間的簡單映射。
特別地,通用語言運行時間把匯編的名稱記錄在匯編引用中,追加“.dll” 并在應用程序的目錄中查找該文件。該方案中有一些變量,在那里運行時間會訪問匯編命名的子目錄中或匯編的風格命名的子目錄。例如,某個開發者會選擇將包含定位于德國的資源的匯編部署在稱為“de”的子目錄中,并將西班牙的資源部署在稱為“es”的子目錄中。
如前所述,每個匯編清單包括有關其關系的版本信息。該版本信息沒有為專用匯編而加強,因為開發人員完全控制了部署到應用程序目錄的匯編。
共享匯編
.NET 框架還支持共享匯編的概念。共享匯編是在機器上由多個應用程序使用的。使用 .NET,共享應用程序之間的代碼是明確的決定。共享匯編有些額外的需求用于解決現在我們經歷的共享問題。除了支持早先描述的并列之外,共享匯編還有許多嚴格的命名需求。例如,共享匯編必須有一個全局唯一的名稱。而且系統必須提供“名稱保護”—更確切的說,防止有人再使用編寫者的匯編名稱。例如,假設您是一個網格控件的廠家,并且發布了您的匯編版本 1。做為編寫您需要確信沒有其他人能發布聲稱為版本 2 的匯編或您的網格控件。.NET 框架支持通過稱為共享名的技術支持支持這些命名需求。(在下一節詳細說明)。
通常,應用程序編寫者不對應用程序使用的共享匯編有同等程度的控制。結果,在每次引用共享匯編時都檢查版本信息。另外,.NET 框架允許應用程序和管理員通過指定版本策略重載應用程序使用的共享匯編版本。
共享匯編通常部署到全局匯編庫。全局匯編庫是供多個應用程序使用的機器范圍的匯編庫。使用該庫不是必要條件,但這樣做有很多好處。例如,自動提供多個版本的匯編并行存儲。而且,管理員能使用該庫部署他們需要的每個機器上的應用程序要使用的缺陷修復或安全補丁。在該方案中,配置匯編到全局匯編存儲能影響機器上的多個應用程序。.NET 框架利用版本政策(稍候描述)的概念解決了現在出現在系統中共享區域的問題,例如 %windir%\system32。
在庫中添加匯編需要明確的管理員操作—實際上,安裝過程必須有“管理員權限”。匯編從不在存儲結束作為運行一個應用程序的側面影響,也不是當前工作的共享匯編的任何存儲。在 Visual Studo .NET 時間框架中,Windows 安裝程序將更新為理解匯編和匯編庫。這意味著可以使用 Windows 安裝程序的所有功能,例如使用 .NET 應用程序選擇安裝和應用程序恢復。
.NET SDK 包括兩個用于匯編庫的工具。第一個是稱為 AL 的工具,它允許在庫中添加匯編。AL 使開發和測試方案變得方便,它不需要創建整個 Windows 安裝程序包在庫中添加一個匯編。使用 /install 開關在庫中添加匯編:
Al /install:myassembly.dll
第二個工具是 Windows Shell Extension,它允許您使用 Windows Explorer 操作庫。圖 4 表示全局匯編庫的視圖。
<img src=/article/UploadPic/20067195382254.gif>
圖 4. 全局匯編庫
共享名
共享名用于使嚴格的命名需求與共享匯編結合起來。共享名有三個目標:
名稱唯一:共享匯編必須具有全局唯一的名稱。
防止名稱冒充:作為開發人員,不希望有人發布您的匯編的后繼版本并假稱它是您發布的,無論是意外還是故意的。
提供引用標識:當涉及引用一個匯編時,共享名用于保證載入的匯編來自所期望的發行者。
共享名使用公共密鑰加密實現。通常,過程如下所示:匯編的編寫者產生一對密鑰(或使用已有的),標記包含專有密鑰清單的文件,并給調用者提供公共密鑰。當引用匯編時,調用者記錄生成強名稱專有密鑰相應的公共密鑰。圖 5 略述了該過程在開發期間如何工作,包括密鑰如何存儲在元數據中及如何生成簽名。
該方案是稱為“Main”的匯編,它引用一個稱為“MyLib”的匯編。MyLib 具有共享名。重要的步驟如下所述。
<img src=/article/UploadPic/20067195382415.gif>
圖 5. 實現共享名的過程
開發者調用在密鑰對中傳遞的編譯器和一組匯編的源文件。密鑰對通常由稱為 SN 的 SDK 工具生成的。例如,下面的命令生成一個新的密鑰對并保存的文件中:
Sn 杒 MyKey.snk
多數的編譯器將匯編作為編輯步驟的一部分。下面是一個 C# 命令的例子,它接受密鑰對并給匯編簽名:
Csc /t:library math.cs /a.keyfile:MyKey.snk /a.version:1.0.0.0
當編譯器生成匯編時,公共密鑰作為匯編標識的一部分保存在清單中。包括公共密鑰作為標識的一部分給匯編提供了全局唯一的名稱。
匯編生成后,包含清單的文件由專有密鑰標記。結果簽名保存在文件中。
當編譯器生成 Main 匯編時,MyLib 匯編的公共密鑰就作為引用 MyLib 的一部分保存在 Main 的清單中。
在運行時,.NET 框架中有兩個步驟保證共享名給予開發人員所需的利益。首先,在匯編安裝到全局匯編庫時,驗證 MyLib 的共享名簽名。(沒有配置到庫中的驗證簽名的選項也是可用的。)驗證簽名保證 MyLib 的內容從匯編建立以來沒有改變。第二步是驗證作為 Main 引用 MyLib 的一部分保存的公共密鑰與 MyLib 身份的一部分的公共密鑰相匹配。如果這些密鑰相同,Main 的作者就能保證載入的 MyLib 版本來自同一個的發布者,該發布者編寫了建立 Main 的 MyLib 版本。當涉及 Main 引用 MyLib 時,該密鑰等效檢查在運行時完成。
術語“簽名”常常聯想到 Microsoft Authenticode。理解共享名和 Authenticode 沒有任何關系是非常重要的。這兩個技術有不同的目標。實際上,Authenticode 意味著對發行者信任的水平,而共享名不是。沒有與強名稱相關聯的授權許可或第三方簽名授權。另外,共享名簽名通常由編譯器本身作為編譯過程的一部分進行。但是,也有實用程序用于在 SDK 中簽名。
另一個值得考慮的是“測試簽名”工程。匯編的編寫者經常不能訪問需要完全簽名的專有密鑰。大多數公司很好的保護這些密鑰的存儲它只能被少數人訪問。結果,.NET 框架提供少量在開發中的“測試簽名”技術,然后再“真實簽名”。
版本策略
如剛剛所描述的,每個匯編清單記錄它創建所依賴的每個關系的版本信息。但是,有一些方案應用程序的編寫者或管理員需要在運行時以關系的不同版本運行。例如,管理員應該能發布故障排除版本而不需要重新編譯每個應用程序以便得到該修改。而且,管理員必須能列出因發現安全漏洞或服務故障從沒有使用的匯編的詳細版本。.NET 框架通過版本策略在版本綁定中啟用了該靈活性。
匯編版本號
每個匯編都有四個部分組成的版本號作為它標識的一部分(就是說,一些匯編的版本 1.0.0.0 和 版本 2.1.0.2 是完全不同的與類裝載器有關的標識)。包括作為標識的一部分的版本主要用來區分用于并行目的的匯編的版本。
開發人員和管理員必須理解版本號的結構,因為它是通用語言運行時間如何加強匯編之間的版本關系的關鍵。
<img src=/article/UploadPic/20067195382319.gif>
圖 6. 匯編版本號的四個部分
版本號的幾個部分是主要版本、次要版本、內部版本和修訂版本。主要版本或次要版本的改變可認為是不可兼容的改變。例如,開發人員改變了一些方法參數的類型或徹底刪除了一些類型。類裝載器使用該信息使所依賴匯編的不兼容版本不默認載入。
另一方面,僅改變版本號中的建立和版本部分可認為是兼容的。這些改變一般是故障排除或安全補丁,在這種情況下類型定義沒有在某種程度上中斷調用者的改變,這是兼容的。這些兼容的變化經常稱為 Quick Fix Engineering (QFE) 修復或動態修復。
默認的版本策略
當通用語言運行時間遇到在代碼中引用匯編時,它決定載入所依賴匯編的版本。默認情況下,載入解決引用的匯編必須有與引用中的記錄相同的主版本號和次版本號。如果這些號碼不同,該匯編認為是不兼容的,并且不會默認載入。相反,通用語言運行時間會用最高的號碼得到 QFE (或動態修復)。
例如,如果 Main 以 MyLib 的 1.0.0.0 版本編譯,當運行發現 MyLib 的 1.0.1.1 版本時,將載入 1.0.1.1。
總是取最新的編譯和版本的策略稱為“自動 QFE 策略”。該原則的主要目的是允許管理員在不重新創建所有應用程序的情況下發布故障排除版本。
定制版本策略
有些時候前面描述的默認策略并不是您所要的。例如,也許應用程序被所安裝的 QFE 不經意地中斷了。
默認的版本策略能通過使用 XML 配置文件修改。關于版本,有兩個文件:一個應用程序說明文件和一個機器范圍或管理員文件。應用程序說明文件保存在與應用程序相同的目錄中。該文件中的策略描述只影響該應用程序。機器范圍策略文件當前保存在 Windows 目錄。該文件被管理員用來描述影響本機器上所有應用程序的策略。例如,也許某個管理員確定了某個匯編的詳細的版本具有一些安全漏洞,并且他確保該匯編將不再被使用。
自定義策略的示例包括:
綁定指定的版本
有時需要綁定一個與清單記錄的版本完全不同的匯編。通過在配置文件中的<BindingPolicy>標簽提供對該方案的支持。該策略用于映射某個關系指定版本的引用或某個關系所有版本的引用。
下面例子映射某個稱為 Calcr 的匯編 6.0.0.0 版本的引用:
<BindingPolicy>
<BindingRedir Name="Calcr"
Originator="32ab4ba45e0a69a1"
Version="*" VersionNew="6.0.0.0"
UseLatestBuildRevision="yes"/>
</BindingPolicy>
關閉自動 QFE 策略
該策略允許停止“自動 QFE 策略”引用已提供的匯編。 <BindingPolicy> 標簽也用在這里,但 UseLatestBuildRevision 屬性設置為 No,如下面例子所示:
<BindingPolicy>
<BindingRedir Name="Calcr"
Originator="32ab4ba45e0a69a1"
Version="*" VersionNew="6.1.1212.14"
UseLatestBuildRevision="no"/>
</BindingPolicy>
安全模式
安全模式(或編譯運行)策略用于恢復編譯配置。啟用該策略將使通用語言運行時間載入記錄在清單中的關系的一個精確版本。大概應用程序在它創建、測試并第一次發布時工作。安全模式是用于恢復到該狀態安全的網絡。下面的 XML 代碼為具體的應用程序打開安全模式:
<BindingMode>
<AppBindingMode Mode="safe"/>
</BindingMode>
如果某個具體的關系不符合版本規則或不經意引入一個錯誤,“安全模式”和“關閉自動 QFE 原則”可用于恢復應用程序到某個工作的狀態。
策略解決方案中的階段
本文已經介紹了幾個版本和發布的概念,包括應用程序專用匯編、共享的匯編、全局匯編存儲和用于指定版本策略的 XML 文件。本節通過描述通用語言運行時間查找匯編和應用版本策略經歷的階段將這些概念連接起來。
當通用語言運行時間遇到引用存儲在元數據中的另一個匯編時,開始載入匯編的過程。根據引用,下面的步驟決定載入那個匯編的哪個版本:
參考應用程序說明文件查看是否指定了策略。如果是,以策略的信息修改原始文件。例如,如果引用指定版本 1.0.0.0 而應用程序說明策略文件指定版本 2.0.0.0,通用語言運行時間將按照如同初始指定版本 2.0.0.0 一樣處理。
在應用程序目錄(和子目錄)中查找匹配的匯編。“匹配”定義為精確的主版本號和次版本號(除非 QFE 策略被禁止)。
不管通過查找是否發現一個匹配項,全局匯編庫都將由 QFE 引用。這使管理員發布每個人都應該得到的故障排除。
最后,參考管理員策略文件。最后參考該文件是因為管理員最后決定載入哪個版本。
--------------------------------------------------------------------------------
發布
發布包含至少兩個不同的方面:包裝代碼和將這些包裝分布給運行該應用程序的客戶端和服務器。.NET 框架主要的目標是通過毫無影響的安裝簡化發布和復制發布的可行性。匯編自描述的天性使我們擺脫對注冊表的依賴,因此使安裝、卸載和復制變得相當簡單。但是,有幾種場合下復制作為發布機制并不充分。在這些情況下,.NET 框架提供擴展代碼下載服務并集成在 Windows 安裝程序中。
包裝
在 .NET 框架的第一個版本中有三個包裝選項可用:
As-built(DLL 和 EXE)。在許多場合,不需要特別的包裝。應用程序以發布工具制造的格式發布,即 DLL 和 EXE 的集合。
Cab 文件。為了更有效地下載,Cab 文件可用于壓縮應用程序。
Windows 安裝程序包。 Microsoft Visual Studio.NET 和其他安裝工具允許建立 Windows 安裝程序包(.msi 文件)。Windows 安裝程序允許利用應用程序修復、選擇安裝以及其他 Microsoft Windows 2000 應用程序管理功能。
分布方案
.NET 應用程序能以多種方式發布,包括復制、代碼下載以及通過 Windows 安裝程序。
對于許多應用程序,包括 Web 應用程序和 Web 服務,發布和復制一組文件到磁盤并運行一樣簡單。卸載和復制就像刪除這些文件或復制它們一樣容易。
.NET 框架提供使用 Web 瀏覽器下載代碼的支持。該部分有幾個重要問題,包括:
零影響:沒有注冊表項添加到機器上。
增量下載:匯編的許多分塊只有在引用時才下載。
下載與應用程序隔離:代表某個應用程序的下載代碼不影響機器上其它的應用程序。支持代碼下載的主要目的是:防止用戶下載新版本的某個共享的組件影響瀏覽具體的 Web 站點和影響其他應用程序。
No Authenticode 對話框:訪問安全系統的代碼用于允許可移動代碼以部分信任程度運行。將不再出現對話框詢問用戶選擇是否信任該代碼。
最后,.NET 完全集成到 Windows 安裝程序和 Windows 2000 應用程序管理功能中。