通过


演练:使用原生代码创建请求级别的 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. “模板 ”窗格中,选择 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 管理器中,单击“连接”窗格中的默认网站

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

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

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

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

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

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

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

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

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

另请参阅

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