MAPI Notifications

MAPI notifications are used as a communication method by a MAPI object and your application. Whenever a specific change has occurred within either a message store or a message session object, you will receive a notification event that provides you with the details of what has changed. For example, if a new message is received by a message store, you will receive a notification that a message has arrived. MAPI notifications can also be used when an error occurs within either one of the MAPI objects.

NOTE:

Pocket PC supports notification events only on MAPI objects within your own process space. This means you can register to receive notifications for actions that occur in your application, but cannot be informed when another application, such as the Inbox, has done something with the message store.


In order for your application to receive notifications from MAPI, you must implement the IMAPIAdviseSink interface. The interface supports the single method described in Table 11.23.

Table 11.23. IMAPIAdviseSink Method

Method

Description

OnNotify()

A notification event has occurred.

Notifications are enabled once your client application has registered itself with a particular MAPI object as an advise sink. Both the IMsgStore and IMAPISession interfaces can be used as advise sources for your notification object, and you will use them to manage the connection between the two.

To register your IMAPIAdviseSink interface with the MAPI session, you use the IMAPISession::Advise() function. Registering with the session object provides you with notification events regarding any errors that occur during the MAPI session. The function is defined as follows:

HRESULT IMAPISession::Advise(ULONG cbEntryID, LPENTRYID
   lpEntryID, ULONG ulEventMask, LPMAPIADVISESINK
   lpAdviseSink, ULONG *lpulConnection);

The first two parameters are unused and should be set to 0. Because the session object supports only error notifications, you can also set the ulEventMask parameter to NULL. The lpAdviseSink parameter should point to the IMAPIAdviseSink interface that you want to use for receiving notifications. The last parameter, lpulConnection, will point to a connection identifier after the function has returned. This will be used later by the ::Unadvise() function to stop receiving notifications.

Registering your notification sink with a message store will provide you with much more detailed events from MAPI. For example, you can receive notifications when new mail arrives, when objects are modified or deleted, and even when an object is moved. To register for notifications with a message store object, you can call the IMsgStore::Advise() function:

HRESULT IMsgStore::Advise(ULONG cbEntryID, LPENTRYID
   lpEntryID, ULONG ulEventMask, LPMAPIADVISESINK
   lpAdviseSink, ULONG *lpulConnection);

The first two parameters are used to specify a particular MAPI object for which you want to receive events. To monitor a specific folder or message, set the lpEntryID parameter to point to the entry identifier, and set the cbEntryID parameter to the size the ENTRYID of the object. You can also set the lpEntryID parameter to NULL if you want to receive notifications for all objects in the message store.

The ulEventMask parameter is used to specify what notification events you want to receive. It can be set to one or more of the following:

  • Use the fnevObjectCreated flag for receiving notifications when a new object is created.

  • Use the fnevObjectCopied flag for receiving notifications when an object is copied.

  • Use the fnevObjectDeleted flag for receiving notifications when an object is deleted.

  • Use the fnevObjectModified flag for receiving notifications when an object is modified.

  • Use the fnevObjectMoved flag for receiving notifications when an object is moved.

The lpAdviseSink parameter should point to the IMAPIAdviseSink interface that you want to use for receiving notification events. The last parameter, lpulConnection, will point to a connection identifier after the function has returned. This will be used later by the ::Unadvise() function to stop receiving notifications.

When your application does not want to receive any more notifications, you must cancel your notification registration with the object from which you are receiving events. To do so, use the following function:

HRESULT ::Unadvise(ULONG ulConnection);

The only parameter that you need to provide is the connection identifier that was returned to you from your previous call to the IMsgStore::Advise() or IMAPISession::Advise() functions.

The following example shows how you can set up a program that is registered to receive notification events on the POP3 message store:

////////////////////////////////////////
// MAPINOTIFY.H

// Remember to link with cemapi.lib, ole32.lib,
// oleaut32.lib, and uuid.lib
#include <objbase.h>
#include <initguid.h>

#define INITGUID
#define USES_IID_IMAPIAdviseSink

#include <windows.h>
#include <cemapi.h>
#include <mapiutil.h>

class CMAPIAdviseSink:public IMAPIAdviseSink {
private:
   long m_lRef;
public:
   CMAPIAdviseSink();
   ~CMAPIAdviseSink();

   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv);
   STDMETHODIMP_(ULONG) AddRef();
   STDMETHODIMP_(ULONG) Release();

   // IMAPIAdviseSink
   STDMETHODIMP_(ULONG) OnNotify(ULONG cNotif,
      LPNOTIFICATION lpNotifications);
};

///////////////////////////////////
// MAPINOTIFY.CPP
#include "mapinotify.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE
  hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
{
   HRESULT hr = S_OK;

   // Initalize COM
   if(FAILED(CoInitializeEx(NULL, 0)))
      return FALSE;

   // Get an instance of our IMAPIAdviseSink
   CMAPIAdviseSink *pMapiNotifySink = new CMAPIAdviseSink();
   IMAPIAdviseSink *pMapiSink = NULL;
   hr = pMapiNotifySink->QueryInterface(IID_IMAPIAdviseSink,
      (void **)&pMapiSink);
   if(FAILED(hr)) {
      pMapiNotifySink->Release();
      return 0;
   }

   // Initialize MAPI and hook up the notification
   IMAPISession *pIMapi = NULL;

   if(MAPIInitialize(NULL) != S_OK)
      return FALSE;

   if(MAPILogonEx(0, NULL, NULL, 0, &pIMapi) != S_OK) {
      MAPIUninitialize();
      return FALSE;
   }

   // Open up the POP3 Message Store
   IMAPITable *pIMapiStoresTable = NULL;
   hr = pIMapi->GetMsgStoresTable(0, &pIMapiStoresTable);
   if(FAILED(hr)) {
      pIMapi->Logoff(0, 0, 0);
      pIMapi->Release();
      pIMapi = NULL;
      MAPIUninitialize();
      return FALSE;
   }

   // Query the store table. We need to get the ENTRYID and
   // Display name to find the POP3 store
   IMsgStore *pPop3Store = NULL;
   while(1) {
      SRowSet *pRowSet = NULL;

      SizedSPropTagArray(2, tblColumns) = {2,
         {PR_DISPLAY_NAME,PR_ENTRYID}};
      pIMapiStoresTable->SetColumns((LPSPropTagArray)
         &tblColumns, 0);
      hr = pIMapiStoresTable->QueryRows(1, 0, &pRowSet);

      if(pRowSet->cRows != 1)
         break;

      // Compare the name with "POP3". If it's a match, open
      // the msg store
      if(_tcscmp(TEXT("POP3"), pRowSet->aRow[0].lpProps[0].
        Value.lpszW) == 0)
      {
        ENTRYID *pEntry =
           (ENTRYID *)pRowSet->aRow[0].lpProps[1].Value.
              bin.lpb;
        ULONG ulStoreBytes = pRowSet->aRow[0].lpProps[1].
              Value.bin.cb;

        pIMapi->OpenMsgStore(NULL, ulStoreBytes, pEntry,
           NULL, NULL, &pPop3Store);
        FreeProws(pRowSet);
        break;
     }

     // Free buffers allocated by MAPI
     FreeProws(pRowSet);
};

// Create a new message.
// Start by opening the Drafts folder
LPSPropValue rgprops = NULL;
LPSPropValue lppPropArray = NULL;
ULONG cValues = 0;
IMAPIFolder *pPOPDraftsFolder = NULL;
SizedSPropTagArray(2, rgTags) =
   {2,{PR_CE_IPM_DRAFTS_ENTRYID,PR_OBJECT_TYPE}};

// Now get the Drafts folder.
hr = pPop3Store->GetProps((LPSPropTagArray)&rgTags,
   MAPI_UNICODE, &cValues, &rgprops);
if(FAILED(hr))
   return FALSE;

hr = pPop3Store->OpenEntry(rgprops[0].Value.bin.cb,
   (LPENTRYID)rgprops[0].Value.bin.lpb, NULL,
   MAPI_MODIFY, NULL, (LPUNKNOWN*)&pPOPDraftsFolder);
if(FAILED(hr))
   return FALSE;

// Now that the folder is open, establish a notification
// sink on the POP3 Store
ULONG ulConnNum = 0;
hr = pPop3Store->Advise(0, NULL,
   fnevObjectCreated|fnevObjectMoved|
   fnevObjectCopied|fnevObject Deleted|fnevObjectModified,
   pMapiSink, &ulConnNum);

if(FAILED(hr))
   return FALSE;

// DO SOMETHING HERE, such as create a new message,
// etc...

// Now we are finished, so clean up
pIMapiStoresTable->Release();

// Disconnect the advise sink
if(pMapiSink) {
   pPop3Store->Unadvise(ulConnNum);
   pMapiSink->Release();
   delete pMapiNotifySink;
}

pPop3Store->Release();
pIMapi->Logoff(0, 0, 0);

   pIMapi->Release();
   pIMapi = NULL;
   MAPIUninitialize();

   return 0;
}

// CMAPIAdviseSink object
CMAPIAdviseSink::CMAPIAdviseSink() {
   m_lRef = 1;
   return;
}

CMAPIAdviseSink::~CMAPIAdviseSink() {
   return;
}

// IMAPIAdviseSink's IUnknown interface
STDMETHODIMP CMAPIAdviseSink::QueryInterface(REFIID riid,
  LPVOID *ppv) {
   if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid,
       IID_IMAPIAdviseSink)){
      *ppv = (IMAPIAdviseSink *)this;
      AddRef();
      return NO_ERROR;
   }

   *ppv = NULL;
   return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CMAPIAdviseSink::AddRef() {
   return (ULONG)InterlockedIncrement(&m_lRef);
}

STDMETHODIMP_(ULONG) CMAPIAdviseSink::Release() {
   ULONG ulCount = (ULONG)InterlockedDecrement(&m_lRef);
   if(ulCount == 0)
      delete this;

   return ulCount;
}

STDMETHODIMP_(ULONG) CMAPIAdviseSink::OnNotify(ULONG cNotif,
   LPNOTIFICATION lpNotifications)
{

   switch(lpNotifications->ulEventType) {
      case fnevCriticalError:
         OutputDebugString(TEXT("Critical Error\r\n"));
         break;
      case fnevNewMail:
         OutputDebugString(TEXT("New Mail\r\n"));
         break;
      case fnevObjectCreated:
         OutputDebugString(TEXT("Object Created\r\n"));
         break;
      case fnevObjectDeleted:
         OutputDebugString(TEXT("Object Deleted\r\n"));
         break;
      case fnevObjectModified:
         OutputDebugString(TEXT("Object Modified\r\n"));
         break;
      case fnevObjectMoved:
         OutputDebugString(TEXT("Object Moved\r\n"));
         break;
      case fnevObjectCopied:
         OutputDebugString(TEXT("Object Copied\r\n"));
         break;
      case fnevTableModified:
         OutputDebugString(TEXT("Table Modified\r\n"));
         break;
   }
   return NO_ERROR;
}

Receiving Notifications

When an event occurs, the IMAPIAdviseSink object that was registered receives a call into the IMAPIAdviseSink::OnNotify() function. This function is defined as follows:

ULONG IMAPIAdviseSink::OnNotify(ULONG cNotif,
   LPNOTIFICATION lpNotifications);

The cNotif parameter specifies the number of NOTIFICATION structures in the array pointed to by the lpNotifications parameter.

Each notification that is received is specified by an individual NOTIFICATION structure. It is defined as follows:

typedef struct {
   ULONG ulEventType;
   union {
      ERROR_NOTIFICATION         err;
      NEWMAIL_NOTIFICATION       newmail;
      OBJECT_NOTIFICATION        obj;
      TABLE_NOTIFICATION         tab;
      EXTENDED_NOTIFICATION      ext;
      STATUS_OBJECT_NOTIFICATION statobj;
   } info;
} NOTIFICATION, FAR *LPNOTIFICATION;

The first field, ulEventType, specifies the notification type for the current event. The info field will be filled in with the appropriate structure for the event you have received.