Walkthrough: Creating a Request-Level HTTP Module By Using Native Code

This walkthrough demonstrates how to use C++ to create a sample request-level HTTP module that implements the new request-processing architecture in IIS 7. This new architecture extends the capabilities of native-code programming when you are writing IIS applications over earlier versions of ASP.NET HTTP modules and ISAPI filters or extensions. For more information about designing HTTP modules using the new request-processing architecture, see Designing Native-Code HTTP Modules.

In this walkthrough, you will create a C++ project for your HTTP module, add the required code for a "Hello World" project, and then compile and test the module.

Prerequisites

The following software is required to complete the steps in the example:

  • IIS 7.

  • Visual Studio 2005.

  • Windows Software Development Kit (SDK).

    Note You can use Visual Studio .NET 2003 or earlier, although the walkthrough steps may not be identical.

Creating the Module

In this part of the walkthrough, you will create an empty C++ DLL project for your HTTP module.

To create a new C++ DLL project

  1. Open Visual Studio 2005.

  2. Verify that the global options have all the right paths to the SDK include files:

    1. On the Tools menu, click Options.

    2. Expand the Projects and Solutions node in the tree view, and then click VC++ Directories.

    3. In the Show directories for drop-down box, select Include files.

    4. Verify that the path where you installed the Windows SDK include files is listed. If the path is not listed, click the New Line icon, and then add the path where you installed the SDK include files. The default installation directory is $(VCInstallDir)PlatformSDK\bin.

    5. Click OK.

  3. Create a new C++ project:

    1. On the File menu, point to New, and then click Project.

      The New Project dialog box opens.

    2. In the Project Types pane, expand the Visual C++ node, and then click Win32.

    3. In the Templates pane, select Win32 Project.

    4. In the Name box, type HelloWorld.

    5. In the Location box, type the path for the sample.

    6. Click OK.

      The Win32 Application Wizard opens.

    7. Click Application Settings.

    8. Under Application type, click DLL.

    9. Under Additional options, click Empty project.

    10. Click Finish.

Adding the Code and Source Files

The next step is to add the required C++ and module-definition files to the project.

To add the source files to the project

  1. Create the module-definition file to export the RegisterModule function:

    1. In Solution Explorer, right-click Source Files, point to Add, and then click New Item.

      The Add New Item dialog box opens.

    2. Expand the Visual C++ node in the Categories pane, and then click Code.

    3. In the Templates pane, select the Module-Definition File template.

    4. In the Name box, type HelloWorld, and leave the default path for the file in the Location box.

    5. Click Add.

    6. Add a line with EXPORTS and RegisterModule. Your file should look like the code below:

      LIBRARY"HelloWorld"  
      EXPORTS  
          RegisterModule  
      

      Note

      Instead of creating a module-definition file, you can export the RegisterModule function by using the /EXPORT:RegisterModule switch.

  2. Create the C++ file:

    1. In Solution Explorer, right-click Source Files, point to Add, and then click New Item.

      The Add New Item dialog box opens.

    2. Expand the Visual C++ node in the Categories pane, and then click Code.

    3. In the Templates pane, select the C++ File template.

    4. In the Name box, type HelloWorld, and leave the default path for the file in the Location box.

    5. Click Add.

    6. Add the following code:

      #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
          );
      }
      

Compiling and Testing the Module

To compile and test the project

  1. Compile the HTTP module:

    1. On the Build menu, click Build Solution.

    2. Verify that Visual Studio did not return any errors or warnings.

    3. Add the HelloWorld.dll module (with the complete path) to the globalModules section of %windir%\system32\inetsrv\config\applicationHost.config file.

  2. Use Internet Explorer to browse to your Web site; you should see "Begin Request sample " with the request count displayed.

Note

You will need to stop IIS before you link your project on subsequent builds.

Troubleshooting Your Settings

If your module does not compile or does not work as expected, here are several areas that you can check:

  • Ensure that you have specified __stdcall for your exported functions, or that you have configured compilation by using the __stdcall (/Gz) calling convention.

  • Ensure that IIS has loaded HelloWorld.dll:

    1. In IIS Manager, click Default Web Site in the Connections pane.

    2. In the workspace (the center pane), select Features View.

    3. In the Group by box, select Category.

    4. In the Server Components category, double-click Modules.

    5. Verify that HelloWorld module is listed.

  • Ensure that you have added the correct RegisterModule export to your definition file.

  • Ensure that you have added the definition file to the project settings. To add the file to the project settings, complete the following steps:

    1. On the Project menu, click Properties.

    2. Expand the Configuration Properties node in the tree view, expand the Linker node, and then click Input.

    3. For the Module Definition File settings, ensure that your definition file is listed.

See Also

Creating Native-Code HTTP Modules
Designing Native-Code HTTP Modules