HOWTO: Implement Worker Threads Using MFC ISAPI Extension

ID: Q185518


The information in this article applies to:
  • Microsoft Internet Information Server versions 3.0, 4.0
  • Microsoft Visual C++, 32-bit Editions, version 5.0
  • Microsoft Internet Server Application Programming Interface (API)


SUMMARY

One method of implementing an ISAPI extension that can potentially require a long processing time (whether it is waiting on database queries or waiting on other network connections) is to implement a worker thread model. This ensures that the IIS Asynchronous Thread Queue (ATQ) thread is not tied up for long periods. While an example of implementing worker threads within an ISAPI extension is distributed with IIS 4.0, implementing a similar scheme using an ISAPI Wizard-generated MFC-based extension is not as clear-cut. This article demonstrates how to incorporate worker threads within an MFC-based extension.


MORE INFORMATION

There are several steps involved in implementing worker threads within an MFC based extension. The first step is to override the HttpExtensionProc() virtual method found in the CHttpServer base class. This is very straightforward. The sample code below assumes that a ISAPI Extension Wizard project named MyApp has been created:

  1. In MyApp.h, override HttpExtensionProc():


  2. 
          class CMyAppExtension : public CHttpServer
          {
          public:
    
          // Code generated by the ClassWizard.
    
          // Simply include the method in the public section of the code
          // generated by the ClassWizard making certain not to alter
          // anything else.
             virtual DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB);
          }; 
  3. In MyApp.cpp, override the base class HttpExtensionProc(). It should read something like this:


  4. 
          DWORD CMyAppExtension::HttpExtensionProc( EXTENSION_CONTROL_BLOCK
                                                    *pECB)
          {
    
          // The result of the base class's (CHttpServer) HttpExtensionProc()
          // must be checked.  If it's successful, you will need to return a
          // HSE_STATUS_PENDING.  This is so that IIS will not shutdown the
          // connection and not destroy the ECB.
          // If the result of the base class is not successful, you should
          // simply propagate the error message up to IIS.
    
             DWORD  dwResult = CHttpServer::HttpExtensionProc(pECB);
             if(dwResult == HSE_STATUS_SUCCESS)
               return HSE_STATUS_PENDING;
             else
               return dwResult;
          } 
  5. Now, inside the parse map function (here, I assume that you are simply using the Default function inserted for us by the ClassWizard--the method takes no variable except the CHttpServerContext and returns nothing):
    
          void CMyAppExtension::Default(CHttpServerContext* pCtxt)
          {
          // Setting the m_bSendHeaders variable to FALSE will prevent
          // IIS from sending headers; this must be handled by your
          // thread.
    
             pCtxt->m_bSendHeaders = FALSE;
    
          // Spawn thread...you must use AfxBeginThread since this is an MFC
          // based extension.
             AfxBeginThread((AFX_THREADPROC)MyThreadProc,
                            (LPVOID)(pCtxt->m_pECB));
          } 
    If you are using some other function that takes several parameters, you should create your own class/struct to encapsulate the ECB pointer and your parameters and pass a pointer to that class to the thread process (just make sure you clean up your class/struct).


  6. In your thread process:


  7. 
          UINT MyThreadProc(LPVOID pvParam)
          {
             EXTENSION_CONTROL_BLOCK *pECB = (EXTENSION_CONTROL_BLOCK*)pvParam;
    
          // Do some processing here...
    
          // To send custom headers, remember to set the m_bSendHeaders to
          // FALSE in the CHttpServerContext from the method that spawned
          // this thread and use the ServerSupportFunction().
    
          // Write data to the client...
          // pECB->WriteClient(...);
    
          // Before returning from the thread routine, you must send the
          // HSE_REQ_DONE_WITH_SESSION flag so IIS can clean up the session.
             pECB->ServerSupportFunction(pECB->ConnID,
                                         HSE_REQ_DONE_WITH_SESSION,
                                         NULL, NULL, 0);
    
             return 0;
          } 

Completed Sample

The following files are available for download from the Microsoft Download Center. Click the file names below to download the files:
MFCWkThr.exe
For more information about how to download files from the Microsoft Download Center, please visit the Download Center at the following Web address
http://www.microsoft.com/downloads/search.asp
and then click How to use the Microsoft Download Center. To build the sample, first extract the files into a directory, and open the project in Microsoft Visual C++.

NOTE: This example uses a single worker thread per session. A more sophisticated and efficient model would use a pool of worker threads and a queuing mechanism to handle the multiple ECBs. Also remember, that while the ATQ thread is impersonating the IUSR_<MachineName> account, the newly spawned worker thread will be running in the security context of the local System account. To change to the IUSR_<MachineName> account, save the thread security token in the ATQ thread by calling OpenThreadToken() and pass that token along with the ECB to the worker thread and have the worker thread call SetThreadToken().

WARNING: The example above assumes all functions in the parse map will be using the worker thread model. If any function does not use a worker thread, you must notify CMyAppExtension::HttpExtensionProc() to not send back HSE_STATUS_PENDING. The simplest solution is to force all parse map functions to use worker threads. If this is not an acceptable solution, one way to notify the HttpExtensionProc() would be to use Thread Local Storage. If care is not taken to ensure the proper return flag is sent by the HttpExtensionProc(), you can potentially have sessions orphaned.


REFERENCES

See \InetPub\iissamples\sdk\isapi\extensions\WorkerThread (distributed with IIS 4.0) for example of implementing worker threads using non-MFC based ISAPI extension.

See Thread Local Storage in Win32 API Documentation.

Additional query words: kbDSupport kbdsi

Keywords : kbDSupport kbiis300 kbiis400
Version : winnt:3.0,4.0,5.0
Platform : winnt
Issue type : kbhowto


Last Reviewed: December 8, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.