IHttpServer::DispenseContainer Method

Returns a context container that may be dispensed.

Syntax

virtual IDispensedHttpModuleContextContainer* DispenseContainer(  
   VOID  
) = 0;  

Parameters

This method takes no parameters.

Return Value

A pointer to an IDispensedHttpModuleContextContainer.

Remarks

IHttpServer implementers will typically return a new``IDispensedHttpModuleContextContainer that will call delete on itself when the IDispensedHttpModuleContextContainer::ReleaseContainer method is called.

Example

The following code example demonstrates how to create a global module that listens for GL_CACHE_OPERATION events. This module defines a custom IHttpStoredContext class and then calls the DispenseContainer method with a new pointer to that custom class. The example then writes the custom data to the Event Viewer.

Caution

IIS 7 generates a large number of events in the Event Viewer. To avoid a log overflow error in a production environment, you should generally avoid writing cache information to the event log. For demonstration purposes, this code example writes an entry to the Event Viewer in debug mode only.

#pragma warning( disable : 4290 )
#pragma warning( disable : 4530 )

#define _WINSOCKAPI_
#include <windows.h>
#include <sal.h>
#include <tchar.h>
#include <initguid.h>
#include <httptrace.h>
#include <httpserv.h>
#include <httpcach.h>

#include <string>
using namespace std;

// The CConvert class mirrors the Convert class that is 
// defined in the .NET Framework. It converts primitives 
// and other data types to wstring types.
class CConvert
{
public:
    // The ToByteString converts a double-byte 
    // character string to a single-byte string.
    // str: the double-byte string to convert.
    // return: a single-byte string copied from str.
    static string ToByteString(const wstring& str)
    {
        // Get the length of the 
        // double-byte string.
        size_t length = str.length();

        // Create a temporary char pointer.
        char* byteChar = new char[length+1];
        byteChar[0] = '\0';
        // Copy the double-byte character string
        // into the single-byte string.        
        size_t charsReturned = 0;
        wcstombs_s(&charsReturned, byteChar, 
                   length+1, str.c_str(), length+1);
        // Create a string to return.
        string retString = byteChar;
        // Delete the temporary string and
        // set that string to NULL.
        delete[] byteChar;
        byteChar = NULL;

        // Return the single-byte string.
        return retString;
    }
};

// The CEventWriter class writes XML 
// documents and strings to the event log.
class CEventWriter
{
public:
    // Creates the CEventWriter class.
    // name: the name of the 
    // event log to open.
    CEventWriter(const wstring& name)
    {                
        #ifdef UNICODE
        m_eventLog = RegisterEventSource(NULL, name.c_str());
        #else
        string multiName = CConvert::ToByteString(name);
        m_eventLog = RegisterEventSource(NULL, multiName.c_str());
        #endif        
    }

    // Creates the destructor for the 
    // CEventWriter class. This destructor
    // closes the HANDLE to the event 
    // log if that HANDLE is open.
    virtual ~CEventWriter()
    {
        // If the HANDLE to the event 
        // log is open, close it.
        if (NULL != m_eventLog)
        {
            // Deregister the event log HANDLE.
            DeregisterEventSource(m_eventLog);
            // Set the HANDLE to NULL.
            m_eventLog = NULL;
        }
    }

    // The ReportInfo method writes 
    // a wstring to the event log.
    // info: the wstring to write.
    // return: true if the event log is written.
    BOOL ReportInfo(const wstring& info)
    {
        return ReportEvent(EVENTLOG_INFORMATION_TYPE, info);
    }
protected:
    // The ReportEvent method accepts an event type
    // and a wstring, and attempts to write that 
    // event to the event log.
    // type: the type of the event.
    // data: the wstring to write to the event log.
    // return: true if the event log is written;
    // otherwise, false.
    BOOL ReportEvent(WORD type, const wstring& data)
    {
        // If the m_eventLog HANDLE 
        // is NULL, return false.
        if (NULL == m_eventLog)
        {
            return FALSE;
        }

        #ifndef _DEBUG
        // If the current build is not debug,
        // return so the event log is not written.
        return TRUE;
        #endif

        #ifdef UNICODE
        // The unicode version of the ReportEvent
        // method requires double-byte strings.
        PCWSTR arr[1];
        arr[0] = data.c_str();
        return ::ReportEvent(m_eventLog,
                             type,
                             0, 0, NULL, 1, 
                             0, arr, (void*)arr);
        #else
        // The non-unicode version of the ReportEvent
        // method requires single-byte strings.
        string multiByte = 
            CConvert::ToByteString(data);
        LPCSTR arr[1];
        arr[0] = multiByte.c_str();
        return ::ReportEvent(m_eventLog,
                             type,
                             0, 0, NULL, 1,
                             0, arr, (void*)arr);
        #endif
    }
private:
    // Specify the HANDLE to the 
    // event log for writing.
    HANDLE m_eventLog;
};

// The DispensedContext class is a 
// custom IHttpStoredContext implementer.
class DispensedContext : public IHttpStoredContext
{
public:
    // The public constructor for the 
    // DispensedContext class.
    // Writes to the event log.
    DispensedContext() : m_eventWriter(L"IISADMIN")
    {
        wstring constructor(L"DispensedContext::DispensedContext");
        m_eventWriter.ReportInfo(constructor);
    }

    // Writes trace information to the event log
    // for this method.
    void Display()
    {
        wstring className(L"DispensedContext::Display");
        m_eventWriter.ReportInfo(className);
    }
   
    // The public pure virtual CleanupStoredContext
    // method that all instantiable classes implementing 
    // the IHttpStoredContext must implement.
    // This method calls delete on this.
    virtual VOID CleanupStoredContext(VOID)
    {
        wstring className(L"DispensedContext::CleanupStoredContext");
        m_eventWriter.ReportInfo(className);
        delete this;
    }
protected:
    // The protected virtual desturctor of 
    // the DispensedContext class. Clients 
    // should not call delete directly; rather, 
    // they should call the CleanupStoredContext 
    // method. Writes information to the event log.
    virtual ~DispensedContext()
    {
        wstring constructor(L"DispensedContext::~DispensedContext");
        m_eventWriter.ReportInfo(constructor);
    }
private:
    // Specify the private CEventWriter.
    CEventWriter m_eventWriter;
};

// The CDispensedContainerModule class extends the 
// CGlobalModule class by creating a custom stored 
// container that can be disposed.
class CDispensedContainerModule : public CGlobalModule
{
public:
    // RegisterGlobalModule is the public 
    // static method that creates an instance 
    // of the CDispenseContainerModule class.
    // dwServerVersion: the current server version.
    // pModuleInfo: the current 
    // IHttpModuleRegistrationInfo pointer.
    // pGlobalInfo: the current IHttpServer pointer.
    static HRESULT RegisterGlobalModule
    (
        DWORD dwServerVersion,
        IHttpModuleRegistrationInfo* pModuleInfo,
        IHttpServer* pGlobalInfo
    )
    {        
        // Both the IHttpServer pointer and the
        // IHttpModuleRegistrationInfo pointers 
        // are necessary for further processing.
        if ((NULL == pGlobalInfo) || (NULL == pModuleInfo))
        {
            return E_INVALIDARG;
        }

        // Get the id of the module.
        HTTP_MODULE_ID moduleId = 
            pModuleInfo->GetId();

        // The HTTP_MODULE_ID cannot be NULL because
        // this is needed for calling the SetContext method
        // on an IDispensedHttpModuleContextContainer pointer.
        if (NULL == moduleId)
        {
            return E_INVALIDARG;
        }

        // Create a new CGlobalCacheModule pointer.
        CDispensedContainerModule* containerModule = 
            new CDispensedContainerModule(pGlobalInfo, moduleId);

        // Return an out-of-memory error if the 
        // containerModule is NULL after the 
        // call to the new operator.
        if (NULL == containerModule)
        {            
            return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
        }        

        // Attempt to set global notification 
        // for GL_CACHE_OPERATION events
        // by using the traceModule as a listener.
        HRESULT hr = pModuleInfo->SetGlobalNotifications
            (containerModule, GL_CACHE_OPERATION);                            

        // Return the HRESULT from the call to 
        // the SetGlobalNotifications method.
        return hr;
    }    

    // The virtual method that is called 
    // when a cache operation occurs.
    // pProvider: the current ICacheProvider pointer.
    // return: GL_NOTIFICATION_CONTINUE.
    virtual GLOBAL_NOTIFICATION_STATUS OnGlobalCacheOperation
    (
        IN ICacheProvider* pProvider
    )
    {      
        // Get the current IHttpModuleContextContainer.
        IHttpModuleContextContainer* contextContainer =
            this->GetModuleContextContainer();

        // If the IHttpModuleContextContainer pointer
        // is NULL, or the call to create the container
        // or the custom context failed, 
        // return GL_NOTIFICATION_CONTINUE.
        if ((NULL == contextContainer) ||
            (FAILED(m_hr)))
        {
            return GL_NOTIFICATION_CONTINUE;
        }

        // Get the IHttpStoredContext pointer from 
        // the IHttpModuleContextContainer pointer.
        IHttpStoredContext* storedContext =
            contextContainer->GetModuleContext(m_moduleId);

        // Attempt to safely cast this container 
        // to a DispensedContext pointer.
        DispensedContext* dispensedContext =
            dynamic_cast<DispensedContext*>(storedContext);        

        // If the IHttpStoredContext pointer is 
        // safely cast to a DispensedContext, 
        // then ask it to display itself.
        if (NULL != dispensedContext)
        {
            dispensedContext->Display();
        }

        // Return GL_NOTIFICATION_CONTINUE.
        return GL_NOTIFICATION_CONTINUE;
    }
    
    // The GetModuleContextContainer method returns the internal
    // IDispensedHttpModuleContextContainer pointer that is returned
    // from the call into the IHttpServer pointer.
    virtual IHttpModuleContextContainer* GetModuleContextContainer(VOID)
    {
        return m_dispensedContainer;        
    }

    // public pure virtual method that all 
    // CGlobalModule non-abstract classes must
    // implement. This method calls delete on this.
    virtual VOID Terminate(VOID)
    {
        delete this;
    }    
protected:    
    // The protected constructor for the
    // CDispensedContainerModule class.
    // pGlobalInfo: the IHttpServer pointer to use
    // for retrieving an IDispensedHttpModuleContextContainer
    // pointer by calling the DispenseContainer method.
    // moduleId: the current HTTP_MODULE_ID for setting the
    // current module context with.
    CDispensedContainerModule
    (
        IHttpServer* pGlobalInfo,
        HTTP_MODULE_ID moduleId
    ) 
        : m_moduleId(moduleId)
    {
        // Initialize the internal HRESULT to E_FAIL.
        m_hr = E_FAIL;

        // Get a container that is dispensed 
        // from the IHttpServer pointer. By default, the
        // IHttpServer implementer creates a new container
        // that is both dispensed and synchronized.
        m_dispensedContainer = 
            pGlobalInfo->DispenseContainer();

        // Test for NULL on the return value from
        // the DispenseContainer method call.
        if (NULL != m_dispensedContainer)
        {
            // Create a new DispensedContext instance
            DispensedContext* context = 
                new DispensedContext;

            // If the call to the new operator 
            // returns NULL, set the HRESULT 
            // to an out-of-memory error.
            if (NULL == context)
            {
                m_hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
            }
            else
            {
                // Set the custom module context 
                // on the dispensed container.
                m_hr =
                    m_dispensedContainer->SetModuleContext(context, m_moduleId);

                // If the SetModuleContext method call fails,
                // the client of the IHttpStoredContext pointer
                // is responsible for cleanup on this data.
                if (FAILED(m_hr))
                {
                    context->CleanupStoredContext();
                }        
            }
        }
    }

    // The protected destructor for the 
    // CDispensedContainerModule class.
    // This method calls the Dispose method.
    virtual ~CDispensedContainerModule()
    {
        Dispose();
    }

    // A protected Dispose method. This method
    // calls ReleaseContainer on the internal 
    // IDispensedHttpModuleContextContainer pointer
    // and then sets that pointer to NULL.
    void Dispose()
    {
        // If creating the container and the
        // custom IHttpStoredContext was successful,
        // call the ReleaseContainer on the
        // IDispensedHttpModuleContextContainer pointer.
        if (SUCCEEDED(m_hr))
        {
            // Call release on the container.
            m_dispensedContainer->ReleaseContainer();

            // Set the container to NULL.
            m_dispensedContainer = NULL;            
        }
    }

private:
    // Specify the IDispensedHttpModuleContextContainer 
    // that will be returned when callers call the 
    // GetModuleContextContainer method.
    IDispensedHttpModuleContextContainer* m_dispensedContainer;

    // Specify the HTTP_MODULE_ID pointer.
    HTTP_MODULE_ID m_moduleId;

    // Specify an HRESULT for the values return from attempting 
    // to create an IDispensedHttpModuleContextContainer pointer.
    HRESULT m_hr;
};

// The RegisterModule method is the 
// main entry point for the DLL.
// dwServerVersion: the current server version.
// pModuleInfo: the current 
// IHttpModuleRegistrationInfo pointer.
// pGlobalInfo: the current IHttpServer pointer.
// return: the value returned by calling the
// CDispenseContainerModule::RegisterGlobalModule
// method.
HRESULT
__stdcall
RegisterModule(
    DWORD dwServerVersion,
    IHttpModuleRegistrationInfo* pModuleInfo,
    IHttpServer* pGlobalInfo
)
{        
    // Call the static method for initialization.
    return CDispensedContainerModule::RegisterGlobalModule            
        (dwServerVersion, 
         pModuleInfo, 
         pGlobalInfo);             
}

The above code writes new events to the Event Viewer, where the Data box contains strings similar to the following.

DispensedContext::~DispensedContext

DispensedContext::CleanupStoredContext

DispensedContext::Display

DispensedContext::DispensedContext

Your module must export the RegisterModule function. You can export this function by creating a module definition (.def) file for your project, or you can compile the module by using the /EXPORT:RegisterModule switch. For more information, see Walkthrough: Creating a Request-Level HTTP Module By Using Native Code.

The RegisterModule calling convention must be stdcall. The best way to guarantee this is to explicitly declare RegisterModule with __stdcall.

Requirements

Type Description
Client - IIS 7.0 on Windows Vista
- IIS 7.5 on Windows 7
- IIS 8.0 on Windows 8
- IIS 10.0 on Windows 10
Server - IIS 7.0 on Windows Server 2008
- IIS 7.5 on Windows Server 2008 R2
- IIS 8.0 on Windows Server 2012
- IIS 8.5 on Windows Server 2012 R2
- IIS 10.0 on Windows Server 2016
Product - IIS 7.0, IIS 7.5, IIS 8.0, IIS 8.5, IIS 10.0
- IIS Express 7.5, IIS Express 8.0, IIS Express 10.0
Header Httpserv.h

See Also

IHttpServer Interface