December 20, 2014
Hot Topics:

Mastering CEMAPI

  • July 15, 2004
  • By Alex Gusev
  • Send Email »
  • More Articles »

In our recent era of global communications, providing some e-mailing capabilities to an application is more than "nice to have." Mobile applications are no exception here. WinCE e-mailing was available ages ago, since the first versions of WIndows CE were released. Since Pocket PC 2002, Microsoft has decided to change its working message store model and has introduced a new set of interfaces. eVC help proudly states:

"Windows CE Messaging Application Programming Interface (CEMAPI) is for mobile devices, what MAPI is for computers. CEMAPI provides a set of classes, interfaces, functions, and other data types to facilitate the development of messaging applications for mobile devices"

As usual, previous MsgStore functionality was dropped and forgotten. This article's main goal is to highlight how to work with CEMAPI and related stuff.

Getting Started

So what's CEMAPI? You have about 20 interfaces to manage message stores, sessions, filtering incoming messages, and so forth. When you try to work with all this, you'll quickly realize that that is going to be as almost the same fun as the OLE DB jungle. The Pocket PC 2003 SDK has several samples for CEMAPI, so you may get an initial feeling for what it is.

A typical working scenario is as follows:

  • Call the MAPIInitialize function.
  • Call the MAPILogonEx function.
  • Get a CEMAPI interface.
  • Call some CEMAPI methods.
  • Call the IMAPISession::Logoff method.
  • Call the MAPIUninitialize function.

All sets of interfaces may be groupped by their functionality. In the next sections, we will discuss mail folder management, message-related interfaces, and synchronization. You can find more details about CEMAPI here.

CEMAPI Session

The "entry point" of CEMAPI is the IMAPISession interface. It allows you to handle different tasks, among them managing message stores. The Windows CE Inbox is logically organized as a tree with accounts as high-level nodes. There is one default account called "ActiveSync," which gives you an opportunity to synchronize e-mails via MS ActiveSync utility. You also may create your own accounts. Later in this article, we will see how to do it programmatically. In the meantime, what we are interested in is that IMAPISession exposes methods to create, open, and enumerate message stores.

The following sample code illustrates how to open some message store in the Inbox. It uses wrapper classes from the attached sample project:

BOOL CMAPISession::EnumMsgStores(CMAPIEntriesArray& entries)
{
   ASSERT(m_pSession != NULL);
   if(m_pSession == NULL)
      return FALSE;

   IMAPITable* pMsgStoresTable;
   HRESULT hr = m_pSession->GetMsgStoresTable(MAPI_UNICODE, &pMsgStoresTable);
   TRACE(_T("CMAPISession::EnumMsgStores - GetMsgStoresTable: %X\n"), hr);
   if(FAILED(hr))
      return FALSE;

   return entries.Read(pMsgStoresTable);
}


CMAPIMsgStore* CMAPISession::OpenMsgStore(const MAPIEntry& entry)
{
   IMsgStore* pMsgStore;
   HRESULT hr = m_pSession->OpenMsgStore( 0,
      entry.arrId.GetSize(),              //entry id size
      (ENTRYID*)entry.arrId.GetData(),    //entry id bytes
      NULL,
      MDB_NO_DIALOG | MAPI_BEST_ACCESS,
      &pMsgStore);

   TRACE(_T("CMAPISession::OpenMsgStore(")
      + entry.sName + _T("): %X\n"), hr);
   if(FAILED(hr))
      return NULL;

   return new CMAPIMsgStore(pMsgStore,entry);
}

CMAPIMsgStore* CMAPISession::OpenMsgStore(CString sName)
{
   CMAPIEntriesArray entriesArr;
   if(!EnumMsgStores(entriesArr))
      return NULL;
   BOOL bCmp = FALSE;
   for(int i = 0; i < entriesArr.GetSize(); ++i)
   {
      if(entriesArr[i].sName.CompareNoCase(sName) == 0)    //compare
      {
         bCmp = TRUE;
         break;
      }
   }
   if(!bCmp)
   {
      TRACE(_T("CMAPISession::OpenMsgStore(")
         + sName +_T(") has no elements with this name\n"));
      return NULL;
   }
   return OpenMsgStore(entriesArr[i]);
}

CMAPIMsgStore* CMAPISession::CreateMsgStore(CString sName)
{
   CMAPIMsgStore* pMsgStore = OpenMsgStore(sName);
   if(pMsgStore !=NULL)
      return pMsgStore;

   IMsgStore* pStore;
   HRESULT hr = ((ICEMAPISession*)m_pSession)->CreateMsgStore(sName, &pStore );
   TRACE(_T("CreateMsgStore: %X\n"), hr);
   if(FAILED(hr))
      return NULL;
   pStore->Release();
   return OpenMsgStore(sName);
}

All of the above is simple enough, so we will move forward to message folders.

Managing Inbox Folders

CEMAPI dedicates a specific interface that allows the programmer work with mail folders—IMAPIFolder. It's some analogue of the file system folder; in other words, you're able to enumerate its contents, manage messages inside it, and so on. There are several standard folders: Inbox, Outbox, Deleted, Drafts, and Sent. Below is a tiny example of opening a folder via the IMsgStore interface:

...
//somewhere in the code
m_pMsgStore->OpenFolder(m_eBuiltInID));
...

CMAPIFolder* CMAPIMsgStore::OpenFolder(CMAPIFolder::BUILTIN_ID nFolderID)
{
   ULONG propIds[] =
   {
      PR_IPM_WASTEBASKET_ENTRYID,
      PR_CE_IPM_DRAFTS_ENTRYID,
      PR_CE_IPM_INBOX_ENTRYID,
      PR_IPM_OUTBOX_ENTRYID,
      PR_IPM_SENTMAIL_ENTRYID
   };

   return GetMessageFolder(propIds[nFolderID]);
}

CMAPIFolder* CMAPIMsgStore::GetMessageFolder(ULONG propTag)
{
   HRESULT hr;
   IMAPIFolder* pFolder;

   LPSPropValue rgprops = MAPIGetProperty(m_pStore,propTag);
   if(rgprops == NULL)
              return NULL;

   hr = m_pStore->OpenEntry(rgprops[0].Value.bin.cb,
       (LPENTRYID)rgprops[0].Value.bin.lpb, NULL, MAPI_MODIFY,
        NULL, (LPUNKNOWN*)&pFolder);
   if(FAILED(hr))
   {
      TRACE(_T("CMAPIMsgStore::GetMessageFolder OpenEntry: %X\n"),hr);
      MAPIFreeBuffer(rgprops);
      return NULL;
   }

   MAPIEntry entry;
   entry.SetId(rgprops[0].Value.bin);

   MAPIFreeBuffer(rgprops);
   rgprops = MAPIGetProperty(pFolder, PR_DISPLAY_NAME);

   if(rgprops == NULL)
   {
      pFolder->Release();
      return NULL;
   }

   entry.sName = rgprops->Value.lpszW;
   MAPIFreeBuffer(rgprops);

   return new CMAPIFolder(pFolder,entry);
}

As a common base, most interfaces in CEMAPI can be achieved via several other ones. IMAPIFolder is just one good example. It also supports folder enumeration:

BOOL CMAPIFolder::EnumFolders(CMAPIEntriesArray& entryArr)
{
   LPMAPITABLE pTable;
   HRESULT hr = m_pFolder->GetHierarchyTable(0,&pTable);
   if(FAILED(hr))
      return FALSE;

   return entryArr.Read(pTable);
}




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel