演练:使用原生代码创建全局级别的HTTP模块

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

在本演练中,你将为 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. 创建模块定义文件以导出 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++ 项目时指定的默认文件夹。

      根据生成选项,应在项目的默认文件夹中看到名为“调试”或“发布”的文件夹。

    2. 在“调试”或“发布”文件夹中,找到名为 HelloWorld.dll的文件。

    3. 将 HelloWorld.dll 文件复制到 Inetsrv 文件夹,该文件夹默认位于 %WinDir%\System32\Inetsrv。

  3. 将 HelloWorld.dll 模块添加到模块列表(有关说明,请参阅 设计 Native-Code HTTP 模块)。

  4. 使用 Internet Explorer 浏览到网站;应会看到正常的网站内容。

  5. 打开 Windows 事件查看器并切换到全局应用程序日志;应会看到一个条目,其中列出了“IISADMIN”作为事件源。

  6. 右键单击该事件,然后单击“ 属性 ”以查看事件详细信息。 应会看到“ 说明”窗格中 显示的“Hello World!”消息。

设置故障排除

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

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

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

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

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

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

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

另见