攻略:使用原生程式碼建立 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. 加上一條含有 EXPORTS 和 RegisterModule 的線。 你的檔案應該會像下面的程式碼一樣:

      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 模組