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

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

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

先決條件

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

  • IIS 7。

  • Visual Studio 2005。

備註

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

模組創建

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

建立新的 C++ DLL 專案

  1. 啟動 Visual Studio 2005。

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

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

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

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

    4. 確認你安裝 SDK 包含檔案的路徑是否被列出。 如果路徑沒有列出,點選 新行 圖示,然後新增安裝 SDK 包含檔案的路徑。

    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. 新增下列程式碼:

      LIBRARY HelloWorld  
      
      EXPORTS  
          RegisterModule  
      
  2. 你可以選擇性地使用 /EXPORT:RegisterModule 開關匯出 RegisterModule 函式:

    1. 在 專案 選單中,點選 HelloWorld 屬性。

    2. 在樹狀檢視中展開 設定屬性 節點,展開 連結節點 ,然後點選 命令列。

    3. 在設定下拉選單中,選擇「所有設定」。

    4. 在 附加選項 框中輸入 /EXPORT:RegisterModule。

    5. 按一下 確定。

  3. 建立 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's global class.
      class MyGlobalModule : public CGlobalModule
      {
      public:
      
          // Process a GL_APPLICATION_START notification.
          GLOBAL_NOTIFICATION_STATUS
          OnGlobalPreBeginRequest(
              IN IPreBeginRequestProvider * pProvider
          )
          {
              UNREFERENCED_PARAMETER( pProvider );
              WriteEventViewerLog( "Hello World!" );
              return GL_NOTIFICATION_CONTINUE;
          }
      
          VOID Terminate()
          {
              // Remove the class from memory.
              delete this;
          }
      
          MyGlobalModule()
          {
              // Open a handle to the Event Viewer.
              m_hEventLog = RegisterEventSource( NULL,"IISADMIN" );
          }
      
          ~MyGlobalModule()
          {
              // Test whether the handle for the Event Viewer is open.
              if (NULL != m_hEventLog)
              {
                  // Close the handle to the Event Viewer.
                  DeregisterEventSource( m_hEventLog );
                  m_hEventLog = NULL;
              }
          }
      
      private:
      
          // Create a handle for the event viewer.
          HANDLE m_hEventLog;
      
          // Define a method that writes to the Event Viewer.
          BOOL WriteEventViewerLog(LPCSTR szNotification)
          {
              // Test whether the handle for the Event Viewer is open.
              if (NULL != m_hEventLog)
              {
                  // Write any strings to the Event Viewer and return.
                  return ReportEvent(
                      m_hEventLog,
                      EVENTLOG_INFORMATION_TYPE, 0, 0,
                      NULL, 1, 0, &szNotification, NULL );
              }
              return FALSE;
          }
      };
      
      // Create the module's exported registration function.
      HRESULT
      __stdcall
      RegisterModule(
          DWORD dwServerVersion,
          IHttpModuleRegistrationInfo * pModuleInfo,
          IHttpServer * pGlobalInfo
      )
      {
          UNREFERENCED_PARAMETER( dwServerVersion );
          UNREFERENCED_PARAMETER( pGlobalInfo );
      
          // Create an instance of the global module class.
          MyGlobalModule * pGlobalModule = new MyGlobalModule;
          // Test for an error.
          if (NULL == pGlobalModule)
          {
              return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
          }
          // Set the global notifications and exit.
          return pModuleInfo->SetGlobalNotifications(
              pGlobalModule, GL_PRE_BEGIN_REQUEST );
      }
      
  4. 你可以選擇性地使用 __stdcall (/Gz) 呼叫慣例來編譯程式碼:

    1. 在 專案 選單中,點選 HelloWorld 屬性。

    2. 在樹狀檢視中展開 配置屬性 節點,展開 C/C++ 節點,然後點選 進階。

    3. 在設定下拉選單中,選擇「所有設定」。

    4. 在呼叫慣例下拉選單中,選擇__stdcall (/Gz)。

    5. 按一下 確定。

模組編譯與測試

你的 HTTP 模組擁有它所需的一切。 只需要編譯並測試你的 HTTP 模組。

編譯並測試專案

  1. 編譯 HTTP 模組:

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

    2. 確認 Visual Studio 沒有回傳任何錯誤或警告。 若遇到任何錯誤或警告,必須先解決這些問題,再進行專案測試。

  2. 將 HTTP 模組的 DLL 檔案複製到 IIS 資料夾:

    1. 打開 Windows 檔案總管,找到你建立 C++ 專案時指定的預設資料夾。

      根據你的建置選項,你應該會在專案的預設資料夾裡看到一個名為 Debug 或 Release 的資料夾。

    2. 在 Debug 或 Release 資料夾中,找到名為 HelloWorld.dll的檔案。

    3. 將 HelloWorld.dll 檔案複製到你的 Inetsrv 資料夾,預設位置是 %WinDir%\System32\Inetsrv。

  3. 將 HelloWorld.dll 模組加入你的模組清單(說明請參見 HTTP 模組設計 Native-Code)。

  4. 使用 Internet Explorer 瀏覽您的網站;你應該會看到你平常網站的內容。

  5. 打開 Windows 事件檢視器並切換到全域應用程式日誌;你應該會看到一個條目列出「IISADMIN」作為事件來源。

  6. 右鍵點擊事件,然後點選 屬性 以查看活動細節。 你應該會在 描述 窗格看到「Hello World!」的訊息。

設定故障排除

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

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

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

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

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

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

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

另請參閱