承載.NET公共語言運行庫

公共語言運行庫支持多種不同的應用程序。例如,運行庫可以運行 Web 服務器應用程序和控制檯應用程序,以及帶有傳統的 Windows 用戶界面的應用程序。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

每種應用程序都需要使用一段名爲運行庫宿主的代碼才能啓動。運行庫宿主會將運行庫加載到一個進程中,在該進程中創建應用程序域,然後在這些應用程序域內加載並執行用戶代碼。

宿主概述

.NET 框架附帶了支持幾種常見情況的運行庫宿主,其中包括 ASP.NET 所使用的宿主和 Microsoft Internet Explorer 所使用的宿主。.NET 框架 SDK 還提供了一種非託管 API,它可用於編寫自定義的運行庫宿主。例如,您可以爲應用程序服務器產品編寫自定義的運行庫宿主,以用於同時運行來自多個用戶的代碼。利用自定義的運行庫宿主,應用程序服務器的客戶可以編寫託管代碼,這些代碼將具有高度可縮放性、通用類型系統、多語言支持、自動內存管理等優點。如果將調試器等專用工具用作運行庫宿主,它們就可以訪問諸如在進程中運行的應用程序域列表等信息。

大多數運行庫宿主都包括非託管代碼和託管代碼。非託管宿主代碼將在進程開始時將運行庫加載到進程中。當運行庫加載到進程中後,就可以將控制權轉移給宿主代碼的託管部分,從而提高性能。通過實現宿主中與託管代碼中的用戶代碼進行交互的部分,可獲得更好的性能,因爲宿主代碼對用戶代碼的調用是在託管環境中進行的。如果用非託管代碼來編寫整個宿主,那麼每當宿主代碼與用戶代碼進行交互時,都需要將非託管代碼轉換爲託管代碼。

非託管宿主代碼用於配置公共語言運行庫,將其加載到進程中,並將程序轉換爲託管代碼。而宿主代碼的託管部分將創建用戶代碼運行時所在的應用程序域,並將用戶請求調度給所創建的應用程序域。

將運行庫加載到進程中

執行任何託管代碼之前,宿主必須首先加載並初始化公共語言運行庫。由於此時運行庫尚未在進程中運行,所有宿主都將用非託管的 Stub 啓動。.NET 框架提供了一組名爲宿主 API 的非託管 API,宿主可以利用它們來啓動運行庫。有關更多信息,請參閱 .NET 框架 SDK 的《工具開發人員指南》中的公共語言運行庫宿主接口規範。

爲了將運行庫加載到進程中,宿主將調用 CorBindToRuntimeEx,它位於 .NET 框架 SDK 的公共語言運行庫宿主接口規範中。CorBindToRuntimeEx 的原型位於 .NET 框架 SDK 的“Include”目錄下的 Mscoree.h 中。當調用 CorBindToRuntimeEx 時,宿主可以設置相應的值,以控制所加載的運行庫版本和基本功能(如垃圾回收和程序集加載)的行爲。宿主可以設置下表中所列的值。

說明

並行垃圾回收

指定垃圾回收是在後臺線程上進行還是在運行用戶代碼的線程上進行。

有關更多信息,請參閱 .NET 框架 SDK 的《工具開發人員指南》中的公共語言運行庫宿主接口規範。

加載程序優化

控制是否以非特定於域的方式來加載程序集。如果以非特定於域的方式加載程序集,一個進程中的所有應用程序域就能夠共享程序集代碼和只讀運行庫數據結構。

服務器與工作站

指定是加載工作站內部版本還是服務器內部版本。

有關更多信息,請參閱 .NET 框架 SDK 《工具開發人員指南》中的公共語言運行庫宿主接口規範。

版本

指定將加載到進程中的運行庫版本。

除了在調用 CorBindToRuntimeEx 時設置上表所述的值之外,宿主還可以請求指向 IcorRuntimeHost 的接口指針。利用該指針,宿主可以完成諸如設置配置選項和轉換爲託管宿主代碼等任務,以創建應用程序域並執行用戶代碼。

宿主可以使用 IcorRuntimeHost 來配置運行庫的各個方面(如垃圾回收),以將其加載到進程中或註冊附加的事件。例如,宿主可以使用 GetConfiguration 來註冊回調函數(當特定線程將要在調試器中停止時,該函數會收到通知)或指定垃圾回收堆的大小。

ICorRuntimeHost 還提供了 StartStop 方法,使宿主能夠顯式地控制運行庫在進程中的生存期。當第一個託管代碼在進程中運行時,將隱式調用 Start;當關閉進程時,將隱式調用 Stop。雖然宿主無需(通常也不會)顯式調用這些方法,但在某些情況下,這樣做還是有用的。例如,當宿主運行完託管代碼,需要爲節省內存和其他資源而從進程中卸載運行庫時,它就可能需要顯式調用這些方法。

轉換爲託管宿主代碼

當加載並初始化運行庫後,宿主必須從非託管代碼轉換爲託管代碼,以便執行託管宿主代碼和用戶代碼。託管宿主代碼通常在默認應用程序域中運行。每當運行庫初始化時,它都將自動創建默認應用程序域。當關閉進程時,將卸載默認的應用程序域。大多數宿主都不在默認應用程序域中運行用戶代碼,因爲它無法獨立於進程關閉。

要轉換爲託管代碼,宿主必須獲取指向默認應用程序域的指針,然後將宿主的託管部分加載到該域中。當完成向託管代碼的轉換後,宿主的託管部分可以創建其他應用程序域,以查找用戶代碼或更多的宿主代碼。

通過調用 ICorRuntimeHost::GetDefaultDomain,宿主可以獲取指向默認應用程序域的接口指針。此調用將返回指向 System. AppDomain(表示默認應用程序域)實例的指針。接口指針的類型爲 _AppDomain。簡而言之,宿主通過 COM 互操作性服務來調用託管類 System.AppDomain 實例的方法,從而將託管宿主代碼加載到默認域中。當宿主獲取指向默認域的指針後,您就可以調用 System.AppDomain 的一種 Load 方法,將宿主的託管部分加載到默認域中。

宿主的託管部分通常包含宿主的大部分邏輯。當完成向託管代碼的轉換後,將不再需要非託管的宿主代碼。由於宿主所加載和運行的用戶代碼都是託管代碼,所以如果用託管代碼實現宿主中的大部分,就可以提供更高的性能。性能之所以會提高,是因爲宿主代碼對用戶代碼的調用均是在託管環境中進行的,而不必每次宿主代碼與用戶代碼進行交互時都在非託管代碼和託管代碼之間進行轉換。

確定應用程序域邊界

當宿主代碼完成從非託管代碼到託管代碼的轉換後,它必須新建一個或多個用於運行用戶代碼的應用程序域。應用程序域是宿主隔離在進程中運行的代碼時所使用的構造。爲了確保不應交互的代碼無法進行交互,這種隔離是必要的。例如,如果代碼從兩個不同的 Web 站點下載到 Internet Explorer 宿主中,則必須將其隔離。爲了確保這種隔離,Internet Explorer 宿主將爲每個站點創建一個應用程序域。

宿主在創建用於運行用戶代碼的應用程序域之前,必須確定新應用程序域的邊界位置。此決定的影響因素包括在限制特定類型對其他類型的訪問、配置、安全性以及能否卸載不需要的代碼等方面的要求。

限制對類型的訪問

在一個應用程序域中運行的類型能夠發現在該域中運行的其他類型並直接調用這些類型。但是,一個類型永遠也無法發現在其他應用程序域中運行的類型,因此也無法調用這些類型。當確定在何處創建邊界時,能否限制特定類型對其他類型的訪問是需要考慮的主要事項。

配置設置

應用程序域是運行庫中配置的基本單位。每個應用程序域都有一個可選的關聯配置文件,該文件描述與該域中所運行代碼相關的設置。

例如,配置文件中可以包含用於查找專用程序集的目錄列表、共享程序集的版本綁定信息、可遠程訪問的類型的位置等。

安全

宿主可以爲域設置代碼訪問安全策略和基於角色的安全策略。這樣,宿主既能夠控制向特定域中的代碼授予的權限集,也能爲基於角色的安全策略設置當前線程的原則和默認的原則。

例如,宿主可以定義應用程序域級別的代碼訪問安全策略,以確保在該域中只能運行從特定站點下載的代碼。或者,宿主可以設置基於角色的安全原則,以實現自定義的身份驗證方案。

代碼卸載

要從內存中卸載在進程中運行的託管代碼,以便將內存用於其他目的,宿主必須卸載代碼運行時所在的應用程序域。不能卸載單獨的程序集或類型。在何時可以卸載用戶代碼這一方面,宿主有其自己的規則。例如,Internet 瀏覽器可能會將託管控件加載到由站點確定的域中。此時,瀏覽器可能會應用相應的規則,在內存中保留最近查看過的網頁,以便使“前進”和“後退”按鈕更迅速地作出響應。當瀏覽器確定不再需要在內存中保留某頁時,它將刪除應用程序域,因此也刪除了託管控件的代碼。

創建和配置應用程序域

當宿主根據上一節所述的條件確定域邊界的位置後,它將使用 System.AppDomain 類型的 CreateDomain 方法來創建用於運行用戶代碼的域。每個應用程序域都包含一個名稱/值對的集合,宿主可以將有關域的信息存儲在這些名稱/值對中。然後,名稱/值對將作爲參數傳遞給 CreateDomain

.NET 框架定義了大量運行庫本身即可理解的屬性。這些屬性的名稱由 System.AppDomain 類中的靜態字符串來定義。宿主可以通過設置本身可理解的屬性來自定義應用程序域。例如,這些屬性可以控制如何隔離在不同域中運行的代碼。爲了使宿主能夠定義用於存儲方案特定信息的自定義屬性,可以對名稱/值對進行擴展。

總的說來,應用程序域提供的隔離具有兩種形式:

  • 應用程序域防止一個域中的類型發現和調用其他域中的類型,從而防止在一個應用程序域中運行的代碼影響其他域。應用程序域將依賴於這樣的事實:代碼已經過驗證,不會受到內存故障的損害。

  • 宿主控制運行庫從何處查找代碼,以代表宿主將代碼加載到特定的應用程序域中。這一點很重要,因爲它將防止一個應用程序中的代碼意外地影響其他應用程序。以這種方式控制代碼加載請求範圍的能力與 Microsoft Win32 和 COM 當前的工作方式有很大的區別。當前,在 Windows 中,由於任何應用程序都可以使用註冊表中所述的任何代碼或位於已知位置(如 Windows system 目錄)中的任何代碼,所以解析範圍是整個計算機。以這種方式進行共享是當前的默認方式,而這種行爲可能會導致 DLL 衝突。

除了確立代碼加載方式的範圍外,還必須將配置信息的範圍縮小到一個應用程序。不過,對於多數配置設置,目前尚不太可能。

例如,如果配置一個遠程計算機來運行 COM 類,那麼特定類在註冊表中的 RemoteServerName 鍵設置將影響所有使用該類的應用程序。與非有意的代碼共享類似,非有意共享地配置數據將防止應用程序完全控制其自身的行爲。

System.AppDomainFlags.ApplicationBaseSystem.AppDomainFlags.ConfigurationFile 屬性分別控制以下兩方面的能力:指定運行庫查找程序集的目錄;控制特定應用程序域的配置設置範圍。

ApplicationBase 將爲應用程序域建立一個根目錄,運行庫會在該目錄下查找專用程序集。如果宿主允許從磁盤加載程序集,它必須提供一個 ApplicationBase,讓運行庫知道從何處查找已加載的程序集。

ConfigurationFile 屬性可指定包含配置應用程序(這些應用程序在應用程序域中運行)設置的 XML 文件的名稱。應用程序配置文件中的設置示例包括程序集版本控制規則,以及有關如何查找應用程序域中運行的類型可遠程訪問類型的指南。

加載和執行用戶代碼

之所以要編寫宿主,是爲了設置用於運行託管用戶代碼的應用程序環境。在此上下文中,用戶代碼是指任何不專門屬於宿主一部分的託管代碼。例如,對於 Internet Explorer 宿主,用戶代碼是組成 HTML 頁的託管控件和腳本。對於應用程序服務器宿主,用戶代碼是包含應用程序服務器所管理和執行的企業業務規則的代碼。

所有託管代碼都是 Assembly 類的一部分。因此,可用來加載和運行託管代碼的方法都基於程序集。例如,System.AppDomain 和 System.Reflection.Assembly 類包含使宿主能夠加載程序集的方法。Load 方法具有各種形式:有些方法採用程序集名稱,有些方法採用程序集清單所在文件的完整文件系統路徑。這些方法用於加載先前已創建並保存到磁盤上的程序集。

例如,假設上述應用程序服務器宿主允許用戶編寫託管代碼業務規則,以加載到應用程序服務器進程中運行。當對特定業務規則運行方法的請求發送到應用程序服務器中時,服務器運行庫宿主代碼將確定在哪一個域中運行代碼,或者是否必須新建域。然後,運行庫宿主代碼使用一種程序集 Load 方法來加載包含業務規則的程序集,並使用反射來執行該業務規則上的方法。有關更多信息,請參閱 System.Reflection namespace 的文檔。

System.Reflection.Emit 命名空間還提供了用於動態創建程序集的類型。當應用程序處理腳本代碼時,將以這種方法加載程序集。

例如,字處理程序可能支持用戶可用來自定義應用程序行爲的宏語言。當加載運行庫並創建應用程序域後,字處理程序可能會將宏腳本編譯爲託管代碼,並使用 System.Reflection.Emit 創建一個程序集。然後,可以將所創建的程序集加載到應用程序域中運行。根據具體的情況,程序集可能僅存在於應用程序的生存期中(即從不保存到磁盤中)。

設置應用程序域級別的安全策略

.NET 框架既提供代碼訪問安全機制,也提供基於角色的安全機制。由於具有這兩種安全機制,您能夠對代碼可執行的操作進行細微的控制。它還提供一種結構,使組件能夠決定用戶可以執行的操作。對於在宿主創建的應用程序域中運行的代碼,宿主對兩種安全機制都具有高度的控制。

當管理員和宿主使用代碼訪問安全機制時,無論是哪個用戶在執行代碼,都將根據代碼本身的特性來確定代碼可執行的操作。代碼特性稱作證據,它可以包括下載代碼的 Web 站點或區域,或發佈代碼的供應商的數字簽名。

當加載並運行代碼時,代碼訪問安全機制會將此證據映射到一組權限。這些權限定義代碼可以執行的特定操作。管理員或宿主會將特定的證據映射到向代碼授予的權限。此映射稱作安全策略。例如,管理員可能會創建一種安全策略,以便向下載自 Intranet 的代碼授予一組較高的權限(如訪問文件系統),而向下載自 Internet 的代碼授予一組較低的權限。

宿主對應用程序域設置的安全策略稱爲應用程序域安全策略。管理員也會在企業、計算機和用戶級別定義一些策略,以確定向代碼授予的全部權限,這些策略和應用程序域安全策略之間存在相交的情況。請注意,應用程序域策略只能限制更高級策略(企業、計算機或用戶)所授予的權限集。

宿主通過對 System.AppDomain 類調用 AppDomain.SetAppDomainPolicy 方法來設置應用程序域級別的策略。宿主只有在被授予了 SecurityPermission 來控制證據時才能設置應用程序域級別的策略。

設置基於角色的安全策略和原則

利用基於角色的安全策略,組件可以在運行時識別當前用戶及其關聯角色。然後,將使用代碼訪問安全策略映射此信息,以確定在運行時授予的權限集。宿主可以爲給定的應用程序域設置基於角色的安全策略和當前安全原則。安全原則表示用戶和與該用戶相關聯的角色。

基於角色的安全策略通常用於實現自定義的身份驗證方案。例如,ASP.NET 宿主使用基於角色的安全策略來實現基於用戶信息的身份驗證方案,這些用戶信息將從 Internet 信息服務 (IIS) 中獲取。

用戶和用戶角色的定義都針對於特定的應用程序。應用程序可以具有不同於 Windows 的用戶概念。例如,應用程序可以要求用戶在登錄到該應用程序時提供用戶名和密碼。該用戶名/密碼與用戶登錄到 Windows 時使用的用戶名/密碼無關。

宿主可以通過調用 System.AppDomain 類的 SetThreadPrincipal 方法來設置當前線程的規則。當前原則用作代碼訪問安全策略的輸入。它確定給定原則是否可以執行指定的操作。

.NET 框架提供了默認原則的概念。此原則是在未顯式設置原則時自動與運行線程相關聯的原則。默認原則的內置值包括一個未經身份驗證的用戶和當前正在使用其帳戶執行線程的 Windows 用戶。宿主可以通過調用 System.AppDomain 類的 SetPrincipalPolicy 來更改默認原則。

卸載域和關閉進程

應用程序域可以在不停止整個進程的情況下卸載。宿主可以利用這一特點來卸載不再需要的代碼,從而減少內存佔用並增加其應用程序的可縮放性。

System.AppDomain 類包括一種名爲 Unload 的靜態方法,宿主可以使用此方法來卸載特定的應用程序域。AppDomain.Unload 將執行正常關機,只要存在任何活動線程,就不會將域卸載。

如果程序集已加載到默認域中或者已經以非特定於域的形式加載,除非關閉整個進程或從進程中卸載運行庫,否則無法卸載這些程序集。

ICorRuntimeHost 接口包括一個名爲 Stop 的方法,宿主可以使用該方法從進程中強制卸載運行庫。當調用 Stop 時,將立即卸載所有域(包括默認域和所有非特定於域的代碼),並從進程中全部移除運行庫。當對進程調用 Stop 後,不能將運行庫加載回該進程。要再次開始運行託管代碼,必須創建一個新的進程。

總結

    上面是VS.NET中公共運行庫的一些主要的特性和方法介紹,給大家參考一下。有任何建議請MAIL我 [email protected]

 

發佈了33 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章