開發 IIS 7.0 的原生 C\C++ 模組
作者: Mike Volodarsky
簡介
IIS 7.0 和更新版本允許透過兩種方式開發的模組來擴充伺服器:
- 使用 Managed 程式碼和 ASP.NET 伺服器擴充性 API
- 使用機器碼和 IIS 原生伺服器擴充性 API
不同于舊版 IIS,大部分的伺服器擴充性案例不需要原生 (C++) 程式碼開發,而且可以使用 Managed 程式碼和 ASP.NET API 來容納。 使用 ASP.NET 擴充伺服器可讓您大幅縮短開發時間,並利用 ASP.NET 和.NET Framework的豐富功能。 若要深入瞭解如何使用 ASP.NET 擴充 IIS,請參閱 使用 .NET 開發 IIS 模組。
IIS 也提供 (C++) 原生核心伺服器 API,以取代舊版 IIS 中的 ISAPI 篩選和擴充功能 API。 如果您有需要原生程式碼開發的特定需求,或想要轉換現有的原生 ISAPI 元件,請利用此 API 來建置伺服器元件。 新的原生伺服器 API 會使用直覺式物件模型來提供物件導向開發的功能、更能控制要求處理,並使用更簡單的設計模式來協助您撰寫健全的程式碼。
本逐步解說會檢查下列工作:
- 使用原生 (C++) 伺服器 API 開發原生模組
- 在伺服器上部署原生模組
若要編譯模組,您必須安裝包含 IIS 標頭檔的平臺 SDK。 這裡 提供最新的Windows Vista 平臺 SDK。
若要搭配 Visual Studio 2005 使用平臺 SDK,您必須註冊 SDK。 安裝 SDK 之後,請透過啟動 > 程式 > Microsoft Windows SDK > Visual Studio 註冊向 Visual Studio 註冊 > Windows SDK 目錄來執行此動作。
此模組的原始程式碼可在 Visual Studio IIS7 原生模組範例中使用。
開發原生模組
在這項工作中,我們會使用新的原生 (C++) 伺服器 API 來檢查原生模組的開發。 原生模組是包含下列專案的 Windows DLL:
- RegisterModule 匯出的函式。 此函式負責建立模組處理站,並註冊一或多個伺服器事件的模組。
- 繼承自 CHttpModule 基類的模組類別實作。 此類別提供模組的主要功能。
- 實作 實作 IHttpModuleFactory 介面的模組處理站類別。 類別負責建立模組的實例。
注意
在某些情況下,您也可以實作 IGlobalModule 介面,以擴充某些與要求處理無關的伺服器功能。 這是進階主題,本逐步解說未涵蓋。
您的原生模組具有下列生命週期:
伺服器背景工作進程啟動時,它會載入包含模組的 DLL,並叫用其匯出的 RegisterModule 函式。 在此函式中,您會:
a. 建立模組處理站。
b. 為模組實作的要求管線事件註冊模組處理站。當要求送達時,伺服器:
a. 使用您提供的處理站建立模組類別的實例。
b. 針對您註冊的每個要求事件,在模組實例上呼叫適當的事件處理常式方法。
c. 在要求處理結束時處置模組的實例。
現在,若要建置它。
模組的完整原始程式碼可在 Visual Studio IIS7 原生模組範例中取得。 下列步驟是開發模組最重要的步驟,不包含支援的程式碼和錯誤處理。
實作伺服器載入模組 DLL 時所叫用的 RegisterModule 函式。 其簽章和其餘原生 API 定義于 HTTPserv.h 標頭檔中,如果您是平臺 SDK (的一部分,如果您沒有平臺 SDK,請參閱 簡介 以取得它) :
main.cpp:
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CMyHttpModuleFactory();
// step 3: register for server events
hr = pModuleInfo->SetRequestNotifications( pFactory,
RQ_ACQUIRE_REQUEST_STATE,
0 );
}
The RegisterModule
在RegisterModule內需要完成三項基本工作:
儲存全域狀態
我們將儲存全域伺服器實例,以及模組內容識別碼,以供稍後用於全域變數。 雖然此範例未使用這項資訊,但許多模組發現在要求處理期間儲存及使用會很有用。 IHttpServer介面可讓您存取許多伺服器函式,例如開啟檔案,以及存取快取。 模組內容識別碼可用來將自訂模組狀態與數個伺服器物件產生關聯,例如要求和應用程式。
建立模組處理站
我們將在本逐步解說稍後實作 Factory 類別 CMyHttpModuleFactory。 此處理站負責每個要求的模組製造實例。
針對所需的要求處理事件註冊模組處理站
註冊是透過 SetRequestNotificatons 方法完成,它會指示伺服器:使用指定的處理站為每個要求建立模組實例;和 ,針對每個指定的要求處理階段叫用適當的事件處理常式。
在此情況下,我們只對RQ_ACQUIRE_REQUEST_STATE階段感興趣。 組成要求處理管線的階段完整清單定義于 HTTPserv.h:
#define RQ_BEGIN_REQUEST 0x00000001 // request is beginning
#define RQ_AUTHENTICATE_REQUEST 0x00000002 // request is being authenticated
#define RQ_AUTHORIZE_REQUEST 0x00000004 // request is being authorized
#define RQ_RESOLVE_REQUEST_CACHE 0x00000008 // satisfy request from cache
#define RQ_MAP_REQUEST_HANDLER 0x00000010 // map handler for request
#define RQ_ACQUIRE_REQUEST_STATE 0x00000020 // acquire request state
#define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 // pre-execute handler
#define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 // execute handler
#define RQ_RELEASE_REQUEST_STATE 0x00000100 // release request state
#define RQ_UPDATE_REQUEST_CACHE 0x00000200 // update cache
#define RQ_LOG_REQUEST 0x00000400 // log request
#define RQ_END_REQUEST 0x00000800 // end request
此外,您可以訂閱數個非決定性事件,這些事件可能會在要求處理期間發生,因為其他模組所採取的動作,例如排清對用戶端的回應:
#define RQ_CUSTOM_NOTIFICATION 0x10000000 // custom notification
#define RQ_SEND_RESPONSE 0x20000000 // send response
#define RQ_READ_ENTITY 0x40000000 // read entity
#define RQ_MAP_PATH 0x80000000 // map a url to a physical path
為了讓 RegisterModule 實作可供伺服器存取,我們必須匯出它。 使用 。DEF 檔案,其中包含 EXPORT 關鍵字以匯出 RegisterModule 函式。
接下來,實作模組 Factory 類別:
mymodulefactory.h:
class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
virtual HRESULT GetHttpModule(
OUT CHttpModule **ppModule,
IN IModuleAllocator *
)
{
}
virtual void Terminate()
{
}
};
模組處理站會實作 IHttpModuleFactory 介面,並用來在每個要求上建立模組的實例。
伺服器會在每個要求的開頭呼叫 GetHttpModule 方法,以取得要用於此要求的模組實例。 實作只會傳回我們接下來實作之模組類別 CMyHttpModule的新實例。 如我們稍後所見,這可讓我們輕鬆地儲存要求狀態,而不必擔心執行緒安全性,因為伺服器一律會為每個要求建立並使用模組的新實例。
更進階的處理站實作可能會決定使用單一模式,而不是每次建立新的實例,或使用提供的 IModuleAllocator 介面在要求集區中配置模組記憶體。 本逐步解說不會討論這些進階模式。
當背景工作進程關閉以執行模組的最終清除時,伺服器會呼叫 Terminate 方法。 如果您在 RegisterModule中初始化任何全域狀態,請在此方法中實作其清除。
實作 Module 類別
此類別負責在一或多個伺服器事件期間提供模組的主要功能:
myHTTPmodule.h:
class CMyHttpModule : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS
OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
};
模組類別繼承自 CHttpModule 基類,此類別會針對稍早討論的每個伺服器事件定義事件處理常式方法。 當要求處理管線執行每個事件時,它會在每個已註冊該事件的模組實例上叫用相關聯的事件處理常式方法。
每個事件處理常式方法都有下列簽章:
REQUEST_NOTIFICATION_STATUS
OnEvent(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
IHttpCoNtext介面提供要求內容物件的存取權,可用來執行要求處理工作,例如檢查要求,以及操作回應。
IHttpEventProvider介面會取代為每個為模組提供特定功能的事件更特定的介面。 例如, OnAuthenticateRequest 事件處理常式會接收 IAuthenticationProvider 介面,讓模組能夠設定已驗證的使用者。
每個事件處理常式方法的傳回都是REQUEST_NOTIFICATION_STATUS列舉的其中一個值。 如果模組已成功執行工作,您必須傳回RQ_NOTIFICATION_CONTINUE;管線應該會繼續執行。
如果發生失敗,而且您想要中止要求處理時發生錯誤,您必須設定錯誤狀態並傳回RQ_NOTIFICATION_FINISH_REQUEST。 RQ_NOTIFICATION_PENDING傳回可讓您以非同步方式執行工作,並讓執行緒要求,以便將它重複使用給另一個要求。 本文未討論非同步執行。
我們的模組類別會覆寫 OnAcquireRequestState 事件處理常式方法。 為了在任何管線階段提供功能,模組類別必須覆寫個別的事件處理常式方法。 如果您在 RegisterModule中註冊事件,但未覆寫模組類別上適當的事件處理常式方法,則模組會在執行時間失敗 (,並在偵錯模式中編譯時觸發偵錯時間判斷提示) 。 請小心,並確定覆寫方法的方法簽章與您正在覆寫 之 CHttpModule 類別的基類方法完全相同。
編譯模組
請記住,您需要平臺 SDK 才能編譯。 如需取得它並讓 Visual Studio 參考它的詳細資訊,請參閱 簡介 。
部署原生模組
編譯模組之後,您必須將其部署在伺服器上。 編譯模組,然後視需要) 將IIS7NativeModule.dll (和 IIS7NativeModule.pdb 偵錯符號檔案複製到執行 IIS 的電腦上任何位置。
原生模組與可以直接新增至應用程式的受控模組不同,必須先安裝在伺服器上。 這需要系統管理許可權。
若要安裝原生模組,您有數個選項:
- 使用APPCMD.EXE命令列工具
APPCMD 讓模組安裝變得簡單。 移至 [啟動 > 程式 > 配件],以滑鼠右鍵按一下命令列提示字元,然後選擇 [以系統管理員身分執行]。 在命令列視窗中,執行下列命令:
%systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
其中 [FULL_PATH_TO_DLL] 是您剛建置之已編譯 DLL 的完整路徑。 - 使用 IIS 管理工具
這可讓您使用 GUI 來新增模組。 移至 [開始 > 執行],輸入 inetmgr,然後按 Enter。 連線到 localhost,找出 [模組] 工作,然後按兩下以開啟它。 然後按一下右窗格中的 [ 新增原生模組 ] 工作。 - 手動安裝模組
將模組新增至 < applicationHost.config 組態檔中的 system.webServer > / < globalModules > 組態區段,並在相同檔案的 < system.webServer > / < modules > 組態區段中新增模組的參考,以啟用模組。 建議您使用前兩個選項之一來安裝模組,而不是直接編輯組態。
工作已完成,我們已完成設定新的原生模組。
摘要
在本逐步解說中,您已瞭解如何使用新的原生 (C++) 擴充性 API 來開發和部署自訂原生模組。 請參閱 原生程式碼開發概觀 ,以深入瞭解原生 (C++) 伺服器 API。
若要瞭解如何使用 Managed 程式碼和 .NET 架構擴充 IIS,請參閱 使用 .NET 開發 IIS 模組。 若要深入瞭解如何管理 IIS 模組,請參閱模組概 觀白皮書。