演练:使用本机代码创建Request-Level HTTP 模块

本演练演示如何使用 C++ 创建示例请求级 HTTP 模块,该模块在 IIS 7 中实现新的请求处理体系结构。 当你在早期版本的 ASP.NET HTTP 模块和 ISAPI 筛选器或扩展上编写 IIS 应用程序时,此新体系结构扩展了本机代码编程的功能。 有关使用新的请求处理体系结构设计 HTTP 模块的详细信息,请参阅 设计Native-Code HTTP 模块

在本演练中,你将为 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. 创建模块定义文件以导出 RegisterModule 函数:

    1. 在解决方案资源管理器中,右键单击“源文件”,指向“添加”,然后单击“新建项”。

      此时将打开“添加新项”对话框。

    2. “类别”窗格中展开“Visual C++”节点,然后单击“代码”。

    3. “模板 ”窗格中,选择“ 模块定义文件” 模板。

    4. 在“ 名称 ”框中,键入 “HelloWorld”,并在“ 位置 ”框中保留文件的默认路径。

    5. 单击“添加”。

    6. 添加具有 和 RegisterModuleEXPORTS行。 文件应如以下代码所示:

      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 模块 (添加到 globalModules %windir%\system32\inetsrv\config\applicationHost.config 文件的 部分。

  2. 使用 Internet Explorer 浏览到网站;应会看到“开始请求示例”,其中显示了请求计数。

注意

在后续生成中链接项目之前,需要停止 IIS。

设置疑难解答

如果模块未编译或未按预期工作,则可以检查以下几个方面:

  • 确保已为导出的函数指定 __stdcall ,或者已使用 __stdcall (/Gz) 调用约定配置编译。

  • 确保 IIS 已加载HelloWorld.dll:

    1. 在 IIS 管理器中,单击“连接”窗格中的“默认网站”。

    2. 在工作区 (中心窗格) ,选择“ 功能视图”。

    3. 在“ 分组依据 ”框中,选择“ 类别”。

    4. 在“ 服务器组件” 类别中,双击“ 模块”。

    5. 验证是否已列出 HelloWorld 模块。

  • 确保已将正确的 RegisterModule 导出添加到定义文件。

  • 确保已将定义文件添加到项目设置。 若要将文件添加到项目设置,请完成以下步骤:

    1. “项目” 菜单上,单击 “属性”

    2. 在树视图中展开 “配置属性” 节点,展开 “链接器 ”节点,然后单击“ 输入”。

    3. 对于 “模块定义文件 ”设置,请确保已列出定义文件。

另请参阅

创建Native-Code HTTP 模块
设计Native-Code HTTP 模块