Borland 在 Delphi/C++ Builder 中提供了多種類型的 Web 應用程式開發,其中最常用的應該是用於調試的 Web App Debugger 應用(WAD應用)和在 Windows/IIS 平台下啟動並執行 CGI/ISAPI 。雖然從理論上說,這幾種類型的 Web 應用程式除了類型不同以外,應該沒有什麼太大的區別。即一般情況下,可以很容易改變其類型,如:假設有一個調試好的 WAD 應用,只要建立一個空的 ISAPI 應用程式,然後把 WAD 應用中的必要單元加進來編譯一下即可產生一個同樣功能的 ISAPI 應用程式。但不幸的是,事實並非如此,在本站發表《C++ Builder 6 BizSnap(2) -- 自訂資料轉送》一文後不久,有幾位朋友給我發來 Mail 說明他們在將文中的程式改成 ISAPI 後遇到一些問題,我才發現這幾種類型的 Web 應用程式之間的一些必須關注的區別。 那個問題出在資料庫訪問控制項和 DataModule 上。在那個例子中,我是把資料訪問控制項放在 WebModule 中,這在 WAD 中是沒什麼問題的,但在 ISAPI 中卻有問題,這與這兩種類型的程式執行過程不同有關。另一個則是關於 DataModule 的,在 WAD 中增加一個 DataModule 來放資料訪問控制項是沒什麼問題的,但在 CGI 中卻會出現程式不執行的情況(即出現下載檔案的提示或出錯),在 ISAPI 中則出現 AV(Access Violation) 錯誤。 所以本文將從這幾種類型的 Web 應用程式的執行過程分析來看看它們的區別。 先看看 WAD 的執行過程: 分析 WAD 類型的來源程式得知:每個 WAD 應用都會用 TWebAppAutoObjectFactory 建立一個 Automation 對象,當 WAD 收到一個發給此 WAD 應用的 Web 請求時就會用 GetActiveObject 來調用一個運行中的執行個體,如果沒有執行個體在運行就啟動一個。一旦取得運行中的 WAD 應用執行個體就通過此 Automation 對象的介面把 Web 請求發過去,由此 Automation 對象調用 WebModule 來類比真實的 Web 服務器。之後的執行是一般 Web 應用程式必要的過程。在 Automation 對象被釋放時(如引用計數減為0時),同時釋放 WebModule 等。即在整個 SOAP 請求期間,WebModule 都是可用的。 再來看看 CGI 應用: CGI 是作為 Web 服務器外部的一個進程單獨運行,它是一個標準的控制台程式,只是 Web 服務器把 Web 請求重新導向到 CGI 程式的標準輸入,而把 CGI 程式的標準輸入重新導向到 Web 響應。但是 Borland 在編譯處理時可能有一些特別做法,即當程式中有 DataModule 時會把它編譯為非 Web 應用程式,使這樣的 CGI 程式在 IIS 中不能運行(即在瀏覽器中輸入其 URL 時將出現下載對話方塊)。所以,必須在處理請求時動態建立 DataModule 才行。因為 CGI 是根據每個 Web 請求建立執行個體的,所以當請求響應返回, CGI 程式也就結束了,此時才釋放 WebModule ,所以實際上在整個 SOAP 請求期間,WebModule 都是可用的,這一點跟 WAD 應用一樣。 最後來看一下 ISAPI 應用程式: 實際上, ISAPI 應用程式是在第一個請求到達 Web 服務器時就被啟動(此時建立 WebModule ),此操作在 ISAPI 的入口: DllEntryPoint 函數中實現。請求處理結束後, ISAPI 應用程式仍存在於記憶體中,包括 WebModule 也是。 Web 服務器為每個請求建立一個 Worker Thread 來處理,處理完成只是終止 Worker Thread 。即在處理請求時是在 Worker Thread 中,並不與 WebModule 在一起,特別是 IIS 可以用 dllhost.exe 來調用 ISAPI ,使它在獨立的進程空間中運行(這種情況下不執行 DllEntryPoint),所以在處理請求時無法訪問 WebModule 及同時建立的 DataModule (如果有的話),如果在處理請求時用到它們就將出現 AV 錯誤。所以在 ISAPI 中,不能使用全域的 DataModule ,只能動態建立。 解決 DataModule 的問題還有一個辦法,那就是用 SOAP Server Data Module 來代替 DataModule 。為什麼這樣是可以的?因為 SoapDataModule 其實只是一個從 IAppServerSOAP 派生的介面,在每個請求處理時會自動建立相應的執行個體,可以省去手工動態建立 DataModule 的麻煩。但它也有一個問題,那就是在 WSDL 中多了很多不必要的介面: IAppServer/IAppServerSOAP 等,同時,如果在其中放了 DataSetProvider 的時候,會把不必要匯出的表匯出了。特別是用了 dbExpress 的情況,此時儘可能用 SQLClientDataSet 來代替 SQLDataSet + DataSetProvider + ClientDataSet 組合。 [Mental Studio]猛禽 Aug.17-02 |