共用方式為


攻略:使用原生程式碼建立 Request-Level HTTP 模組

本教學示範如何使用 C++ 建立一個範例請求層級 HTTP 模組,實作 IIS 7 中新的請求處理架構。 這種新架構擴充了原生程式碼程式設計的能力,當您在撰寫 IIS 應用程式時,比較早期版本的 ASP.NET HTTP 模組及 ISAPI 過濾器或擴充應用程式。 欲了解更多使用新請求處理架構設計 HTTP 模組的資訊,請參見 HTTP 模組設計 Native-Code

在這個教學中,你會為你的 HTTP 模組建立一個 C++ 專案,加入「Hello World」專案所需的程式碼,然後編譯並測試該模組。

先決條件

完成範例步驟需要以下軟體:

  • IIS 7。

  • Visual Studio 2005。

  • Windows 軟體開發套件(SDK)。

    你可以使用 Visual Studio .NET 2003 或更早版本,雖然攻略步驟可能不完全相同。

模組創建

在這部分的攻略中,你會為你的 HTTP 模組建立一個空的 C++ DLL 專案。

建立新的 C++ DLL 專案

  1. 開啟 Visual Studio 2005。

  2. 請確認所有全域選項中的 SDK 包含檔案路徑都正確無誤。

    1. [工具] 功能表上,按一下 [選項]

    2. 在樹狀檢視中展開專案 與解決方案 節點,然後點選 VC++ 目錄

    3. 顯示目錄的類型 的下拉選單中,選擇 包括檔案

    4. 確認你安裝 Windows SDK 包含檔案的路徑是否被列出。 如果路徑沒有列出,點選 新行 圖示,然後新增安裝 SDK 包含檔案的路徑。 預設安裝目錄是 $(VCInstallDir)PlatformSDK\bin。

    5. 按一下 確定

  3. 建立一個新的 C++ 專案:

    1. [檔案] 功能表上,指向 [開新檔案] ,然後按一下 [專案]

      專案 對話框會打開。

    2. 專案類型 窗格中,展開 Visual C++ 節點,然後點選 Win32

    3. 範本 欄選 Win32 專案

    4. 名稱 框輸入 HelloWorld

    5. 位置 欄位輸入樣本路徑。

    6. 按一下 確定

      Win32 應用程式精靈會打開。

    7. 點選 應用程式設定

    8. 應用程式類型中,點選 DLL

    9. 「附加選項」中,點選 「清空專案」。

    10. 按一下完成

新增程式碼與原始碼檔案

下一步是將所需的 C++ 與模組定義檔案加入專案。

將原始碼檔案加入專案

  1. 建立 module-definition 檔案以匯出 RegisterModule 函式:

    1. 在解決方案總管中,右鍵點擊 「來源檔案」,指向 「新增」,然後點選 「新增項目」。

      [新增項目] 對話框開啟。

    2. 分類窗格中展開 Visual C++ 節點,然後點選程式碼

    3. 範本 面板中,選擇 Module-Definition 檔案 範本。

    4. 名稱 框輸入 HelloWorld,並在 位置 欄位保留檔案的預設路徑。

    5. 按一下新增

    6. 加上一條含有 EXPORTSRegisterModule 的線。 你的檔案應該會像下面的程式碼一樣:

      LIBRARY"HelloWorld"  
      EXPORTS  
          RegisterModule  
      

      備註

      你可以不建立模組定義檔案,而是透過 /EXPORT:RegisterModule 開關匯出 RegisterModule 函式。

  2. 建立 C++ 檔案:

    1. 在解決方案總管中,右鍵點擊 「來源檔案」,指向 「新增」,然後點選 「新增項目」。

      [新增項目] 對話框開啟。

    2. 分類窗格中展開 Visual C++ 節點,然後點選程式碼

    3. 範本 欄選中,選擇 C++ 檔案 範本。

    4. 名稱 框輸入 HelloWorld,並在 位置 欄位保留檔案的預設路徑。

    5. 按一下新增

    6. 新增下列程式碼:

      #define _WINSOCKAPI_
      #include <windows.h>
      #include <sal.h>
      #include <httpserv.h>
      
      // Create the module class.
      class CHelloWorld : public CHttpModule
      {
      public:
          REQUEST_NOTIFICATION_STATUS
          OnBeginRequest(
              IN IHttpContext * pHttpContext,
              IN IHttpEventProvider * pProvider
          )
          {
              UNREFERENCED_PARAMETER( pProvider );
      
              // Create an HRESULT to receive return values from methods.
              HRESULT hr;
              
              // Retrieve a pointer to the response.
              IHttpResponse * pHttpResponse = pHttpContext->GetResponse();
      
              // Test for an error.
              if (pHttpResponse != NULL)
              {
                  // Clear the existing response.
                  pHttpResponse->Clear();
                  // Set the MIME type to plain text.
                  pHttpResponse->SetHeader(
                      HttpHeaderContentType,"text/plain",
                      (USHORT)strlen("text/plain"),TRUE);
      
                  // Create a string with the response.
                  PCSTR pszBuffer = "Hello World!";
                  // Create a data chunk.
                  HTTP_DATA_CHUNK dataChunk;
                  // Set the chunk to a chunk in memory.
                  dataChunk.DataChunkType = HttpDataChunkFromMemory;
                  // Buffer for bytes written of data chunk.
                  DWORD cbSent;
                  
                  // Set the chunk to the buffer.
                  dataChunk.FromMemory.pBuffer =
                      (PVOID) pszBuffer;
                  // Set the chunk size to the buffer size.
                  dataChunk.FromMemory.BufferLength =
                      (USHORT) strlen(pszBuffer);
                  // Insert the data chunk into the response.
                  hr = pHttpResponse->WriteEntityChunks(
                      &dataChunk,1,FALSE,TRUE,&cbSent);
      
                  // Test for an error.
                  if (FAILED(hr))
                  {
                      // Set the HTTP status.
                      pHttpResponse->SetStatus(500,"Server Error",0,hr);
                  }
      
                  // End additional processing.
                  return RQ_NOTIFICATION_FINISH_REQUEST;
              }
      
              // Return processing to the pipeline.
              return RQ_NOTIFICATION_CONTINUE;
          }
      };
      
      // Create the module's class factory.
      class CHelloWorldFactory : public IHttpModuleFactory
      {
      public:
          HRESULT
          GetHttpModule(
              OUT CHttpModule ** ppModule, 
              IN IModuleAllocator * pAllocator
          )
          {
              UNREFERENCED_PARAMETER( pAllocator );
      
              // Create a new instance.
              CHelloWorld * pModule = new CHelloWorld;
      
              // Test for an error.
              if (!pModule)
              {
                  // Return an error if the factory cannot create the instance.
                  return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
              }
              else
              {
                  // Return a pointer to the module.
                  *ppModule = pModule;
                  pModule = NULL;
                  // Return a success status.
                  return S_OK;
              }            
          }
      
          void
          Terminate()
          {
              // Remove the class from memory.
              delete this;
          }
      };
      
      // Create the module's exported registration function.
      HRESULT
      __stdcall
      RegisterModule(
          DWORD dwServerVersion,
          IHttpModuleRegistrationInfo * pModuleInfo,
          IHttpServer * pGlobalInfo
      )
      {
          UNREFERENCED_PARAMETER( dwServerVersion );
          UNREFERENCED_PARAMETER( pGlobalInfo );
      
          // Set the request notifications and exit.
          return pModuleInfo->SetRequestNotifications(
              new CHelloWorldFactory,
              RQ_BEGIN_REQUEST,
              0
          );
      }
      

模組編譯與測試

編譯並測試專案

  1. 編譯 HTTP 模組:

    1. [建置] 功能表上,按一下 [建置解決方案]。

    2. 確認 Visual Studio 沒有回傳任何錯誤或警告。

    3. 將 HelloWorld.dll 模組(包含完整路徑)加入 %windir%\system32\inetsrv\config\applicationHost.config 檔案的區段 globalModules

  2. 使用 Internet Explorer 瀏覽您的網站;你應該會看到「開始請求範例」,並顯示請求數量。

備註

你需要在後續建置中連結專案前先停止 IIS。

設定故障排除

如果你的模組無法編譯或運作如預期,以下是幾個你可以檢查的區域:

  • 請確保你已指定 __stdcall 匯出函式,或已透過呼叫 __stdcall (/Gz) 慣例設定編譯。

  • 確保 IIS 已載入 HelloWorld.dll:

    1. 在 IIS Manager 中,點擊連接欄中的預設網站

    2. 在工作區(中央窗格)中,選擇 「功能檢視」。

    3. 在「按群組」框中,選擇類別

    4. 伺服器元件 類別中, 雙擊模組

    5. 確認 HelloWorld 模組是否被列出。

  • 請確保你已將正確的 RegisterModule 匯出檔案加入定義檔案。

  • 請確保你已經將定義檔加入專案設定。 要將檔案加入專案設定,請完成以下步驟:

    1. 在 [項目] 功能表上,點擊 [屬性]。

    2. 在樹狀檢視中展開 設定屬性 節點,展開 連結節點 ,然後點選 輸入

    3. 模組定義檔案 設定中,請確保你的定義檔案被列出。

另請參閱

建立 Native-Code HTTP 模組
設計 Native-Code HTTP 模組