摘要:探討 Microsoft(R) Windows(R) 2000 和 Windows 98 第二版本中並行共享組件的實現(如 Windows 認證規范中討論的)。包括新的並行組件的創建以及使用 DLL/COM 重定向處理相同組件的不同版本之間的不兼容性。包括編寫和安裝並行組件以及重新打包和測試應用程序的指南。
目錄
介紹
一點背景知識
新組件共享策略
比較兩種策略
創建新的並行組件
並行組件編寫指南
安裝並行組件
DLL/COM 重定向
使用 DLL/COM 重定向
介紹
現代操作系統和應用程序由許多組件構成。組件是自包含的軟件實體,該軟件實體提供了一組可被各種應用程序廣泛使用的函數。因為單獨的組件被多個應用程序使用,所以組件的共享是很有必要的。
成功的全局組件共享要求任何共享的組件功能和該組件的先前版本完全一樣。但是如果不能實現的話,要達到百分之百的向後兼容實際上是很難的,因為測試所有使用共享組件的配置是非常困難的。新舊應用程序最終都使用相同的組件,因此,隨著時間的推移,修正並改進這些組件變得愈加困難。
同時,組件的實際功能也不太容易定義。應用程序可能成為依附在組件上的意外副作用,而不被認為是該組件核心功能一部分。例如,組件中的一個錯誤可能影響到應用程序,以及當組件開發者選擇修正此錯誤時應用程序失敗,這種情況就是人們常說的“DLL Hell(該死的 DLL)”。這使得那些使用組件的應用程序會更加深該問題的嚴重性。
這種缺乏向後兼容性的情況使得在部署新的應用程序時,必須中斷已部署的應用程序,或是犧牲某些新應用程序的功能。所有新的應用程序都要求共享組件的版本與已配置的版本不同。要在增強應用程序穩定性的同時提供成功的共享,Microsoft 已在 Windows 2000 和 Windows 98 第二版本中引入了並行共享,開創了通過選擇性隔離來共享組件的新方式。
一點背景知識
在了解並行共享的詳細信息之前,讓我們看一些背景資料以及“DLL Hell”的問題。
組件共享
Windows 最初就采納了共享的概念。所有操作系統都在提供穩定性、完整的服務集的需求與操作系統所要求的硬件的資源限制的之間尋求平衡。到目前為止, CPU 用量和磁盤空間仍然是 PC 平台中非常緊張的資源。很顯然,要將操作系統和應用程序代碼裝入到一個很小空間,必須盡可能地共享代碼。與許多其他好處相比,代碼共享加強了硬件資源的均衡運用,並且最大程度地減少在前期質量保證檢測中暴露的問題。代碼共享是使 Windows 成功的要素之一。
Windows 的共享並不限於代碼。應用程序和組件的狀態,可以用注冊表狀態的形式、文件系統中的應用程序專用數據存儲的形式和公開全局命名空間的 Windows API 的形式,在整個操作系統中找到。這類共享,在多個軟件供應商生產的應用程序之間提供了高級別的互操作性,降低了成本,提高了軟件的效率。
但是,共享也必須付出一些代價。共享意味著應用程序彼此依賴,引入了脆弱性因素。更改某一組件會對其他組件產生無法預料的影響。典型的情況,一個應用程序可能依賴於共享組件的一個特定版本。而另一個應用程序可能是用該共享組件的升級(或降級)版本安裝的,因此第一個應用程序可能受此更改的影響。在極端的情況下,曾經工作正常的應用程序會突然功能紊亂,甚至失敗。這種情況通常稱為“DLL Hell”。
隔離
在系統中,共享的反面是隔離。通過將所有資源和代碼靜態地綁定到應用程序,可以隔離應用程序。但是如今對於依賴於 COM 或其他全局存儲的系統資源的應用程序來說,完全的隔離是不可行的。
減少應用程序脆弱性的一種解決辦法是,有選擇地隔離應用程序和組件。在這種方案中,所有應用程序可能都具有對相同組件的訪問權,但該組件目前有多個版本可用。組件開發者有權編制舊組件的新版本、作一些改進或修正錯誤。而客戶可以選擇適合於特定應用程序的版本。就像走進一個汽車配件商店為您的 1984 Chevy 挑選一個燃油泵一樣。您會發現貨架上的這個泵和一些比它晚來的適用於其他車型的泵並行放在一起。在使用組件的情況下,關鍵是提供適合於每個應用程序的版本並且隔離其他不同的版本。而且,在重定向的情況下,應用程序可以進行配置,以使用適合於該特定應用程序的組件版本,而不管最近開發的或以後將要開發那些版本。
並行共享
為鼓勵這類隔離,Microsoft Windows 2000 和 Windows 98 第二版本的新的組件共享形式叫做並行共享,它使用選擇性隔離最大程度地減少 DLL Hell。並行共享允許相同組件(COM 或 Win32)的多個版本同時在不同的進程中運行。因此,應用程序可以使用設計並測試過的組件的特定版本,而不管另一個應用程序要求該相同組件的不同版本。這種安排允許開發者建立並部署更可靠的應用程序,因為開發者能指定他們的應用程序使用的組件版本,和系統中其他應用程序無關。
新組件共享策略
Windows 2000 和 Windows 98 第二版本中的並行共享遵照以下兩種策略:
創建並行組件。開發者構造一些新組件,它們支持該組件多個版本的同時執行。這些新組件的用戶現在能夠使用已建立並安裝的版本,不管在計算機中安裝了哪些其他版本。
DLL/COM 重定向。開發者和管理員重新包裝現有的應用程序和組件,使要求的共享組件版本專門用於需要它們的應用程序,各自與其他版本並行使用。
並行組件,不論是最近創建的還是現有的、或重新配置的應用程序的一部分,並不是所有的方案都支持。
創建並行組件的情形主要發生在當在另一個容器應用程序的內部被主持的過程中。例如,如果您的控件將要由桌面 Windows 應用程序(用 Microsoft Visual Basic(R) 或 Visual C++(R) 語言編寫)使用,那麼這些控件最好使用並行設計。不推薦在服務器中使用並行組件。
DLL/COM 重定向的情形主要發生在,新的客戶端應用程序正在部署到已經支持幾個其他應用程序的計算機上時,或者正在部署新的客戶端應用程序的地方,要求它對由於其他應用程序的安裝所引起的共享組件的更改有更大適應力。不推薦在服務器中使用 DLL/COM 重定向。
注意 DLL/COM 重定向致力於解決部署現有應用程序和組件時的應用程序沖突。在開發新的應用程序或組件時,最佳策略是部署被天然隔離的並行組件。
比較兩種策略
下表比較兩種方法,DLL/COM 重定向和創建並行 (SxS) 組件,並指導如何為您的方案選擇正確的方法。
表 1. 並行策略比較
屬性 SxS 組件 DLL/COM 重定向
主要焦點是什麼? 建立穩定的新組件,使它在以後版本中將免受造成組件“向後不兼容”的更改的影響。 將現有應用程序與由於其他應用程序安裝了不兼容的共享 DLL 而導致的問題隔離開。
指定的版本對於使用它們的應用程序來說是隔離的嗎? 是。SxS 組件總是應當部署成:對於使用它們的應用程序來說是隔離的。 是。使用 DLL/COM 重定向的應用程序使用安裝在該應用程序目錄中的共享組件的版本,不管在系統中安裝了哪些其他版本。
需要新代碼還是對現有代碼進行修改? 是。建立 SxS 組件(或編寫現有組件 SxS)至少需要將 COM 注冊代碼更改為允許按相對路徑進行訪問。可能需要一些附加編碼更改,以確保在 SxS 運行版本之間正確地處理全局狀態。 否。DLL/COM 重定向允許應用程序重新配置為,不需要更改任何代碼或重新編譯便能安裝並運行 SxS。這允許沒有應用程序源代碼訪問權的管理員便能重新配置它以解決 DLL Hell 問題。
該策略在所有方案中均采用嗎? 是。SxS 組件被設計並編碼以安裝並運行 SxS。因此,經過良好設計、開發和測試的 SxS 組件(以及使用它們的應用程序)將不會有與 DLL Hell 相關的問題。 否。DLL/COM 重定向不要求改變代碼。現有的應用程序和組件可能沒有按同時運行多個版本的要求進行設計。經驗表明在大多數情況下現有的應用程序和組件可以運行 SxS,但需要進行測試才能確認對於特定方案是否是這樣。(請參見 選擇要隔離的組件。)
一般的常規處理方法:
如果 DLL Hell 問題影響到現有應用程序的部署,可使用 DLL/COM 重定向隔離沖突的組件(要進一步了解該選項,請參見下面的方案)。
如果正在建立新的應用程序,並且要針對 DLL Hell 問題進行設計和部署,則開發並行組件。
創建新的並行組件
並行共享,要求開發者在創建新的應用程序時編寫並行組件。它們都是普通的 COM 或 Win32 組件,不過它們安裝在應用程序目錄(或其子目錄)中,而不是系統目錄中。它們是與特定應用程序隔離的,並且不被所有應用程序全局共享。
因此,組件可以安全地與安裝在其它地方的同一組件的不同版本,並行安裝在另一個應用程序目錄中。如果系統中的另一個應用程序要求(因此安裝了)不同的版本,您的應用程序不會受到影響。這兩個應用程序將和它們各自的組件版本一起運行。
如果另一個應用程序在系統中安裝了新的組件版本,您的該組件版本將保持不變,因為您已將它安裝到自己的應用程序目錄中。在其他應用程序使用它的版本的同時,您的應用程序繼續使用隨該應用程序提供的組件版本。操作系統可以同時加載這兩個版本。
同樣,因為並行組件對於安裝它的應用程序是“私有”的,所以應用程序的卸裝程序始終可以安全地刪除並行組件,按照定義,任何其他應用程序都不能依賴於私有組件。
注意 並行組件必須通過操作系統正確注冊(在本章後面部分中說明),組件的每個版本才不會與該組件的其他現有版本沖突。
注意 Windows 2000 和 Windows 98 第二版本均支持並行共享。以前的 Windows 操作系統不支持它;但是,在這些系統中,可以在系統目錄中安裝一個並行 DLL(按照下一節中的指南編寫的 DLL)。因此,DLL 以全局共享(向後兼容)方式工作。應用程序必須動態地檢查操作系統版本以確定要使用的共享方法。
並行組件編寫指南
下面的指南概述了在產生並行組件(COM 或 Win32)中涉及的事項。當您編寫這類組件並將它們放在應用程序目錄中時,您的代碼被私有化為該應用程序的上下文。當您將數據放入以該應用程序名稱為根項的注冊表項中時,該數據被私有化為該應用程序的。
您的並行組件的用戶,與您的組件的其他用戶所需要的更改絕緣。您還能夠更新您的組件,而不必擔心破壞現有的應用程序。應用程序能夠安裝您的組件而不用重新引導,即使另一個應用程序正在使用該組件的不同版本。
注意 這些指南是當前 Windows 認證指南的更穩定的形式,它告訴用戶將 Win32 DLL 放在應用程序的目錄中。
一般事項
不要嘗試替換受“系統文件保護”(隨 Windows 2000 提供)所保護的任何文件,包括大部分 .sys、.dll、.exe 以及 .ocx 文件。
必須測試所有組件以確保並行的有效性,尤其在可能出現共享的區域中,因為沒有任何由當前操作系統強制的並行。
將所有版本特定名稱一起收集到 #defines 中,以便於將源代碼從一個版本遷移到另一個版本。執行此操作允許用戶在某個位置更改版本;然後自動更改所有注冊表項。例如:
#define MyRegistryKey "MyAppv1.0.0.0"
發行組件的新版本時,只需要在某個位置更改該版本。
謹防喪失對組件進行快速修正的能力,因為它們正在隨意定位的應用程序目錄中。作為組件開發者,您可能不知道所有需要修正組件的位置。應用程序供應商需要向用戶分發更新。
謹防由於疏忽所造成的交叉進程共享。例如,共享的內存部分可能導致問題,因為該部分內存不能在組件的不同版本之間交叉共享。
需要將所有共享的數據結構設計成並行(對於組件的每個版本有所不同),包括內存映射的文件、互斥程序、命名管道以及硬件驅動程序。
必須將不是長期保存的數據存儲在 TEMP 目錄中。
不要將用戶數據放在全局位置。應用程序數據和用戶數據應該明確分開。
增強您的組件
在安裝和卸裝由不同供應商提供的組件過程中,必須正確引用全局注冊表中 GUID 的計數。在 GUID 注冊表項下維護一個引用計數是執行該操作最可靠的方法。
HKEY_CLASSES_ROOT\CLSID\{GUID}\InprocServer32
Default=foo.dll
ThreadingModel=Apartment
RefCount=1
注意
必須引用與 GUID 無關的 DLL 計數。
不要注冊 COM 注冊表的完整路徑名,可以依靠應用程序目錄的目錄搜索查找您的組件及其從屬。
如果更改了 COM GUID 下的元數據(如線程模型),則需要為組件更名,並應用新的 GUID,因為它是組件的有效新版本。需要執行此操作是因為注冊表中的數據在另一個應用程序的上下文中(具有該應用程序私有的組件版本)可能無效。
在您的 DLL 中,而不是在單獨的文件中,必須包含類型庫。
不要使用 LoadRegTypeLib ,通過注冊表加載類型庫。注冊表中類型庫的注冊表項是全局狀態,並且以私有規則運行計數器。因此請使用 LoadType Lib。
OLEAUT 創建外部類型庫新版本的能力是脆弱的,並且易於失敗。建議不要對並行組件使用外部類型庫。