Platform SDK: Exchange Server

How the Sample Works

The Suggest COM component is implemented using the Active Template Library (ATL). The following list gives the names of the Suggest application sample files and a brief description of file's role in the application.

File Manifest

File Description
Exchhndl*.* Header and interface definition files from the Platform SDK.
ExEvHandler.cpp Contains the implementation of the ExecuteEvent method on the exposed IExchangeEventHandler interface.
ExEvHandler.h Contains declarations for the CExEvHandler class.
Exevhandler.rgs Contains information for registering the CExEvHandler CLSID.
Propid.h Contains (mostly property) definitions.
Regs.cpp Contains routines to work with the registry.
Regs.h Contains declarations for the registry routines in Regs.cpp.
SAProps.cpp Contains the implementation of the CSAProps class.
SAProps.h Contains the declaration of the CSAProps methods in SAProps.cpp.
SAProps.rgs Contains information used to register the COM component.
Suggest.cpp Contains the DllMain routine (entry point for the DLL).
Suggest.def Contains definitions of the library name and exported functions.
Suggest.dsp
Suggest.dsw
The project and workspace. Do not directly edit these files, use the IDE.
SuggestEvents.cpp Contains the code for handling events.
SuggestEvents.h Contains declarations for the routines in SuggestEvents.cpp.

Discussion

An Exchange Server event handler is any COM class that provides an implementation of the IExchangeEventHandler COM interface. The interface definition language (IDL) definition for this interface can be found in the file exchhndl.idl (located in the Platform SDK core build environment include directory), and is provided here for convenience:

[
  object,
  uuid(69E54156-B371-11D0-BCD9-00AA00C1AB1C), 
  oleautomation,
  pointer_default(unique)
]
interface IExchangeEventHandler : IUnknown
{
    HRESULT ExecuteEvent([in] IDispatch* pEventSource);
};

When custom agents (event handlers) are registered on the Exchange Server machine, and are properly advertised as such by being registered in the COM category "Exchange Event Handler Category", they can be bound to folders to handle events. Every time the Exchange Server Event Service is started, it checks the registy for registered handlers, and makes them available to handle folder events. When users select the Agents property page with Microsoft Outlook for a folder, they are presented with a list of any number of registered agents that they can use (with proper permissions) for the folder.

Once an agent has been bound to a folder, the Exchange Server Event Service monitors the folder using Incremental Change Synchronization for changes in its status, such as the addition or removal of items. When such a change occurs, it does the following:

  1. It packages the information about the change for the folder in what we will call an EventSource COM object. This object exposes the IEventSource COM interface.
  2. It creates an instance (CoCreateInstance) of each event handler class registered to handle events for the folder.
  3. It requests the IExchangeEventHandler interface (QueryInterface) on each newly created agent object, and if successful, passes the IEventSource interface on the EventSource object to each agent using its ExecuteEvent method.

The IEventSource interface IDL definition is located in the file exchhndl.idl, and is presented here:

[
  object,
  uuid(69E54152-B371-11D0-BCD9-00AA00C1AB1C), 
  oleautomation,
  pointer_default(unique)
]
interface IEventSource : IUnknown {
 [propget] HRESULT Source ([out,retval] IUnknown**);
 [propput] HRESULT Source ([in] IUnknown*)

 [propget] HRESULT SourceID ([out,retval] BSTR*);
 [propput] HRESULT SourceID ([in] BSTR);

 [propget] HRESULT MessageID ([out,retval] BSTR*);
 [propput] HRESULT MessageID ([in] BSTR);

 [propget] HRESULT Session ([out,retval] IUnknown**);
 [propput] HRESULT Session ([in] IUnknown*);

 [propget] HRESULT EventType ([out,retval] DWORD*);
 [propput] HRESULT EventType ([in] DWORD);

 [propget] HRESULT Response ([out,retval] VARIANT*);
 [propput] HRESULT Response ([in] VARIANT);
 
 [propget] HRESULT MaxExecutionTime ([out,retval] DWORD*);
 [propput] HRESULT MaxExecutionTime ([in] DWORD);
};

Note  The IExecuteEvent interface is not dual, as it inherits from the IUnknown interface.  The first parameter of the ExecuteEvent method is typed to IDispatch. Do not attempt to use any methods defined for IDispatch as this interface is not dual. Immediately call QueryInterface to get the IExecuteEvent interface and work with this interface only.

Once the agent has the IEventSource interface, it can retrieve event information and act accordingly. The event type for the folder is one in the following enumeration:

enum EventType
{
    Invalid,
    Folder_OnTimer,
    Folder_OnMessageCreated,
    Folder_OnMessageDeleted,
    Message_OnChange,
    Message_OnMove,
};

Response text can be placed in the Response property (VT_BSTR) of the EventSource object and it will be appended to the folders Event log.

The Suggest Custom Agent Implementation

The Suggest COM component provides the ExEvHandler COM class as a custom event handler. It exposes the IExchangeEventHandler interface and provides a concrete implementation of the ExecuteEvent method. The Event Service calls this method, passing the IEventSource interface on an EventSource COM object. The agent code shown in the following section does the following:

  1. Gets the IEventSource interface on the EventSource object.
  2. Gets the event type from the EventSource object.
  3. Creates an stack instance of the CSuggestEvents ATL C++ class.
  4. Depending on the event type, calls the appropriate method on the C++ object, passing to it a reference to itself (this).
  5. When the called method completes, the IEventSource interface is released and the agent returns (exits).
STDMETHODIMP CExEvHandler::ExecuteEvent(LPDISPATCH pSource)
{
    HRESULT        hr           =  S_OK;
    IEventSource*  pEventSource =  NULL;
    DWORD          dwEventType  =  0;
    
    // Get the IEventSource Interface
    if(SUCCEEDED(hr = pSource->QueryInterface(IID_IEventSource, (void**)&pEventSource)))
    {
        // cache this pointer
        m_pEventSource = pEventSource;

        // Determine our Event Type
        if(SUCCEEDED(hr = GetEventType(&dwEventType)))
        {
            // We instantiate a instance of our events class 
            CSuggestEvents se;

            // Call appropriate function
            switch(dwEventType)
            {
            case Message_OnChange: 
                hr = se.OnChange(this);
                break;
            case Folder_OnMessageDeleted:
                hr = se.OnMessageDeleted(this);
                break;
            case Folder_OnTimer:
                hr = se.OnTimer(this);
                break;
            case Folder_OnMessageCreated:
                hr = se.OnMessageCreated(this);
                break;
            default: 
                hr = E_INVALIDARG; 
                break;
            }
        }
        m_pEventSource->Release();
    }
    
    return(hr);
}

The handling methods are listed in the following table along with a brief description of what they do.

CSuggestEvents ATL C++ Class

Member Description
::OnMessageCreated This method is called when the Event Type is Folder_OnMessageCreated. This method sends a reply to the person whose message triggered the event. The body of the message contains the text "Thank-you for your suggestion. You will be hearing from a suggestion consultant soon."
::OnChange Returns S_OK
::OnMessageDeleted Returns S_OK
::OnTimer Returns S_OK.

For additional information about implementing custom agents, see Advanced Topics: Custom Agents.