一.進入Asp.net運行時之前
雖然本文的重點是對托管代碼的解析,但為了整個知識點的完整性,這里簡單介紹一下IIS處理請求的一些基本情況。在一個IIS服務器
對于.aspx頁面,這個擴展就是aspnet_isapi.dll。因為這些ISAPI都是非托管的Win32應用,直接對它們進行改動是比較困難的。所以,為了增強Asp.net運行時的可擴展性,aspnet_isapi.dll本身的功能非常少,我們可以把aspnet_isapi.dll簡單理解為請求信息的路由器,負責把請求從IIS傳送到asp.net運行時。而后面我們將要講到的HttpHandle和HttpModule則分別擔負起了ISAPI Extension和ISAPI Filter的功能,幸運的是,HttpHandle和HttpModule可以由純的托管代碼來實現。
二.從非托管代碼到托管代碼
前面說了,aspnet_isapi.dll是非托管代碼,而asp.net運行時是托管代碼,他們都運行在w3wp.exe工作進程里面,那么兩者之間的調用點發生在什么地方呢?在介紹接下來的內容之前必須先介紹一個概念:ECB。ECB的全稱是Extension Control Block,它是一個非托管資源包,具有對ISAPI接口完整的訪問能力,包含了所有和一個傳入請求有關的底層信息,如提交的標單中的數據等等。所以說,asp.net中的托管代碼想要訪問aspnet_isapi.dll對外提供的接口,就需要通過ECB。其實更準確的來說,是托管代碼公布了一個IUnknown類型的接口供aspnet_isapi.dll調用,而aspnet_isapi.dll在調用的時候會把自己的ecb地址傳進去。
明白了ECB的概念,下面我們要介紹一個接口和一個接口的實現類(位于System.Web.Hosting名字空間下),請讀者注意筆者在代碼中的注釋(本文的主要目的就是和大家一起從代碼實現的角度來認識整個Asp.net運行時,所以代碼里的注釋是筆者添加的關鍵性說明,后面的所有代碼段都是這樣):
1/**//*InterfaceType(ComInterfaceType.InterfaceIsIUnknown)指明了這個接口將作為 IUnknown 派生接口向 COM 公開,這就使得isapi.dll可以以COM方式調用此接口。*/ 2[ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 3public interface IISAPIRuntime 4{ 5 void StartProcessing(); 6 void StopProcessing(); 8 [return: MarshalAs(UnmanagedType.I4)] 9 int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel); 10 void DoGCCollect(); 11} 12 13/**//*這個類實現了IISAPIRuntime接口。它的實例對象存在于每一個AppDomain中,作為整個Asp.net運行時的入口。*/ 14public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IRegisteredObject 15{ 16 // Fields 17 private static int _isThisAppDomainRemovedFromUnmanagedTable; 18 private static string s_thisAppDomainsIsapiAppId; 19 20 // Methods 21 [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Minimal), SecurityPermission(SecurityAction.Demand, Unrestricted=true)] 22 public ISAPIRuntime(); 23 public void DoGCCollect(); 24 public override object InitializeLifetimeService(); 25 /**//*處理請求的入口點方法,由isapi.dll以COM方式調用*/ 26 public int ProcessRequest(IntPtr ecb, int iWRType); 27 internal static void RemoveThisAppDomainFromUnmanagedTable(); 28 internal void SetThisAppDomainsIsapiAppId(string appId); 29 public void StartProcessing(); 30 public void StopProcessing(); 31 void IRegisteredObject.Stop(bool immediate); 32} |
所以,一切都是從aspnet_isapi.dll以COM方式調用了一個ISAPIRuntime對象的ProcessRequest方法開始的?梢远嗵嵋痪涞氖,這種調用是異步的,也就是說,aspnet_isapi.dll在調用后會立即返回,但ECB會一直保留下來,直到整個請求被處理完畢之后再釋放。
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/