進入.NET運行時的真正的入口發生在一些沒有被文檔記載的類和接口中(譯著:當然,你可以用Reflector來查看J).除了微軟,很少人知道這些接口,微軟的家伙們也並不熱衷於談論這些細節,他們認為這些實現細節對於使用ASP.NET開發應用的開發人員並沒有什麼用處。
工作進程(IIS5中是ASPNET_WP.EXE,IIS6中是W3WP.EXE)寄宿.NET運行時和ISAPI DLL,它(工作進程)通過調用COM對象的一個小的非托管接口最終將調用發送到ISAPIRuntime類的一個實例上(譯注:原文為an instance subclass of the ISAPIRuntime class,但是ISAPIRuntime類是一個sealed類,疑為作者筆誤,或者這裡的subclass並不是子類的意思).進入運行時的第一個入口就是這個沒有被文檔記載的類,這個類實現了IISAPIRuntime接口(對於調用者說明來說,這個接口是一個COM接口)這個基於Iunknown的底層COM接口是從ISAPI擴展到ASP.NET的一個預定的接口.圖3展示了IISAPIRuntime接口和它的調用簽名.(使用了Lutz Roeder出色的.NET Reflector 工具http://www.aisto.com/roeder/dotnet/).這是一個探索這個步步為營過程的很好的方法.
圖3-如果你想深入這個接口,打開Reflector,指向System.Web.Hosting命名空間. ISAPI DLL通過調用一個托管的COM接口來打開進入ASP.NET的入口,ASP.NET接收一個指向ISAPI ECB的非托管指針.這個ECB包含訪問完整的ISAPI接口的能力,用來接收請求和發送響應回到IIS.
IISAPIRuntime接口作為從ISAPI擴展來的非托管代碼和ASP.NET之間的接口點(IIS6中直接相接,IIS5中通過命名管道).如果你看一下這個類的內部,你會找到含有以下簽名的ProcessRequest函數:
[return: MarshalAs(UnmanagedType.I4)]
int ProcessRequest([In] IntPtr ecb,
[In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
其中的ecb參數就是ISAPI擴展控制塊(Extention Control Block),被當作一個非托管資源傳遞給ProcessRequest函數.這個函數接過ECB後就把它做為基本的輸入輸出接口,和Request和Response對象一起使用.ISAPI ECB包含有所有底層的請求信息,如服務器變量,用於表單(form)變量的輸入流和用於回寫數據到客戶端的輸出流.這一個ecb引用基本上提供了用來訪問ISAPI請求所能訪問的資源的全部功能,ProcessRequest是這個資源(ecb)最初接觸到托管代碼的入口和出口.
ISAPI擴展異步地處理請求.在這個模式下ISAPI擴展馬上將調用返回到工作進程或者IIS線程上,但是在當前請求的生命周期上ECB會保持可用.ECB含有使ISAPI知道請求已經被處理完的機制(通過ecb.ServerSupportFunction方法)(譯注:更多信息,可以參考開發ISAPI擴展的文章),這使得ECB被釋放.這個異步的處理方法可以馬上釋放ISAPI工作線程,並將處理傳遞到由ASP.NET管理的一個單獨的線程上.
ASP.NET接收到ecb引用並在內部使用它來接收當前請求的信息,如服務器變量,POST的數據,同樣它也返回信息給服務器.ecb在請求完成前或超時時間到之前都保持可訪問(stay alive),這樣ASP.NET就可以繼續和它通訊直到請求處理完成.輸出被寫入ISAPI輸出流(使用ecb.WriteClient())然後請求就完成了,ISAPI擴展得到請求處理完成的通知並釋放ECB.這個實現是非常高效的,因為.NET類本質上只是對高效的、非托管的ISAPI ECB的一個非常”瘦”(thin)的包裝器.