November 26, 2014
Hot Topics:

Mastering CEMAPI

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

Working with Messages

Well, now that we have successfully opened the desired folder in some message store, we are ready to figure out how to deal with the messages themselves. The IMAPIFolder interface has a full set of methods for message management. You may create, open, delete, copy, or move selected messages together with messages enumeration. To make your life easier, IMAPIFolder returns a IMessage interface as an e-mail message representation. Let's take a look at how it happens in the simplest examples:

CMAPIMessage* CMAPIFolder::CreateMessage()
{
   IMessage* pMessage;
   HRESULT hr = m_pFolder->CreateMessage(NULL, 0, &pMessage);

   if(FAILED(hr))
      return NULL;

   return new CMAPIMessage(pMessage);
}

BOOL CMAPIFolder::DeleteMessage(const MAPIEntry& entry)
{
   SBinary id = { entry.arrId.GetSize(), (BYTE*)entry.arrId.GetData() };
  ENTRYLIST ls = { 1, &id };

   HRESULT hr = m_pFolder->DeleteMessages(&ls, 0, NULL, 0);

   return !FAILED(hr);
}

With IMessage, we actually arrived to the "workhorse" of CEMAPI, the IMAPIProp interface. Via this interface, you may get a lot of message properties such as sender, body, recipients, and the like. You may find a long list of available properties in the mapitags.h header file. Below, you will find the sample implementation of how to retrieve a required message property:

LPSPropValue MAPIGetPropertyImpl(IMAPIProp* object, ULONG propTag,
                                 CString sPropName, CString sFile, int nLine)
{
   LPSPropValue pValue;
   ULONG count;
   SPropTagArray tags = { 1, { propTag } };
   HRESULT hr = object->GetProps(&tags, MAPI_UNICODE, &count, &pValue);
   if(FAILED(hr) || count != 1 || pValue->ulPropTag != propTag)
   {
      CString sLog;
      sLog.Format(_T("FAIL: MAPIGetProperty(%s=0x%08x) at %s:%d\r\n\t\
         HRESULT 0x%08x count %d ulPropTag = 0x%08x err = 0x%08x\n"),
         sPropName, propTag, sFile, nLine, hr, count, pValue->ulPropTag,
         pValue->ulPropTag == PT_ERROR ? pValue->Value.err : S_OK);
      TRACE(sLog);
      return NULL;
   }
   return pValue;
}

The sample project implements some of useful classes; for example, handling message recipients. You will find many examples of properties retrieving throughout the project, so we may just skip odd explanations here.

Managing Accounts

Another important point of CEMAPI is e-mail accounts maintenance. Until PocketPC 2003, there was no documented way to handle all needed stuff. Fortunately, in PocketPC 2003, the SDK Config Manager API was introduced. This API is presented by only one function:

HRESULT DMProcessConfigXML(LPCWSTR pszWXMLin, DWORD dwFlags,
                           LPWSTR *ppszwXMLout);

But it uses general XML input/output, thus allowing a wide range of configuration tasks. We will deal with EMAIL2 for our purposes. By the way, EMAIL2 is also a new challenge for the Pocket PC 2003 line. Now, the configuration becomes an almost trivial task; you just need to provide some XML file, and that's all. In MSDN, you may find the following sample:

<wap-provisioningdoc>
   <characteristic type="EMAIL2">
      <characteristic type="Some GUID should be placed here">
         <parm name="SERVICENAME" value="MyPOP"/>
         <parm name="SERVICETYPE" value="POP3"/>
         <parm name="INSERVER"    value="popserver"/>
         <parm name="OUTSERVER"   value="smtpserver"/>
         <parm name="AUTHNAME"    value="alias"/>
         <parm name="AUTHSECRET"  value="password"/>
         <parm name="DOMAIN"      value="domain"/>
         <parm name="REPLYADDR"   value="emailAddress"/>
      </characteristic>
   </characteristic>
</wap-provisioningdoc>

This example shows how to create a new e-mail account named "MyPOP". A full list of existing parameters may be found here. I'll just note that you must generate a new GUID to create each account. All the rest of the parameters are intuitive enough. Thus, you're set up with accounts.

Synchronization

The last thing we will speak about in this acticle is mail box synchronization. CEMAPI has IMailSyncHandler, an interface to synchronize message boxes. To actually get it working, you should do some tricks. Deep inside cemapi.h, you can discover the following definition:

typedef HRESULT (WINAPI *ONESTOPFACTORYFUNC)(LPCWSTR pszType,
                                             IMailSyncHandler** ppObj);

It declares a pointer to the function that returns an IMailSyncHandler instance according to service type—POP3 or IMAP4. The "magic secret" is to know that this function is located in MailTrns.dll. Thus, the required workaround is something like this:

HINSTANCE hMailTrns = LoadLibrary( _T("MailTrns.dll") );
if ( hMailTrns == NULL )
{
   // Load failed...
}
else
{
   ONESTOPFACTORYFUNC pOneStopFactoryFunc = (ONESTOPFACTORYFUNC)
                      GetProcAddress( hMailTrns, _T("OneStopFactory") );
   if ( pOneStopFactoryFunc == NULL )
   {
      // Error GetProcAddress OneStopFactory...
   }
   else
   {
      if ( pIMailSyncHandler == NULL )
         hr = (*pOneStopFactoryFunc)(_T("POP3"), &pIMailSyncHandler);
   }
}

Once you have an instance of the IMailSyncHandler interface, you are able to synchronize messages. The first step along this way is to call the Initialize method. It takes three parameters: pointer to callback interface, account name, and pointer to mail storage to be synchronized. The brilliant thing is that you should develop some implementation of the IMailSyncCallBack interface. It's not so awful as it may seem at first glance, but you have no chance to avoid additional development. A simplified example is presented below:

HRESULT CMailSyncCallBack::Progress( LPCWSTR pszProfile, SYNCPROGRESSITEM* pinfo)
{
   CString msg;

   m_pMainView->LogStatus( _T("CMailSyncCallBack::Progress(...)"));

   switch (pinfo->mask)
   {
   case SYNCPROGRESSITEM_STATUSTEXT:

      msg.Format( _T("Mail status: %s"), pinfo->pwszStatusText);
      m_pMainView->MessageNotify(msg);
      break;

   case SYNCPROGRESSITEM_STATUSTYPE:

      msg.Format(L"Mail status");
      m_pMainView->LogStatus(msg);
      break;

   case SYNCPROGRESSITEM_PROGVALUE:

      nCurrentMail = pinfo->ulProgValue; 
      msg.Format(L"mail number %d from %d",nCurrentMail,nTotalNewMail);
      m_pMainView->MessageNotify(msg);

      m_pMainView->ProgressNotify(100 * nCurrentMail/nTotalNewMail);
      break;

   case SYNCPROGRESSITEM_MAXVALUE:

      nTotalNewMail = pinfo->ulMaxValue;
      msg.Format(L"Number of mail's %d",nTotalNewMail);
      break;

   case SYNCPROGRESSITEM_DISCONNECTED:

      msg = _T("Mail disconnected");
      m_pMainView->LogStatus(msg);
      break;

   case SYNCPROGRESSITEM_TOTAL_NEW_MAIL:

      nTotalNewMail = pinfo->ulTotalNewMail;
      msg.Format(L"Total New mail %d",nTotalNewMail);
      m_pMainView->LogStatus(msg);
      break;

   case SYNCPROGRESSITEM_NEW_MESSAGE:

      msg.Format(L"Sync new message");
      m_pMainView->LogStatus(msg);
      break;
  }

   return S_OK;
}

HRESULT CMailSyncCallBack::LogEvent( TRANSPORTEVENT* pevt )
{
   CString msg;
   m_pMainView->LogStatus(_T("CMailSyncCallBack::LogEvent
                          (TRANSPORTEVENT* pevt)"));
   msg.Format(_T("  pevt->pszSourceDLL : %s"), pevt->pszSourceDLL);
   m_pMainView->LogStatus(msg);
   msg.Format(_T("  pevt->pszSourceProfile  : %s"),
              pevt->pszSourceProfile );
   m_pMainView->LogStatus(msg);
   msg.Format(_T("  pevt->hr : 0x%08x "), pevt->hr);
   m_pMainView->LogStatus(msg);

   if ( m_pIMailSyncHandler != NULL )
   {
      LPWSTR eventstr;
      m_pIMailSyncHandler->DecodeEvent( pevt, &eventstr );
      m_pMainView->LogStatus(eventstr);
   }
   return S_OK;
}

With some implementation at hand, you then may initialize IMailSyncHandler:

hr = pIMailSyncHandler->Initialize(pIMailSyncCallBack,
                                   _T("POP3"),pmsgStore);

The next step is to define the sync credentials and connect:

SYNCCREDENTIALS sc;
sc.cbSize      = sizeof(SYNCCREDENTIALS);
sc.cbBufSize   = 0;
sc.pszUsername = L"user"
sc.pszPassword = L"password";
sc.pszDomain   = L"domain";

hr = pIMailSyncHandler->Connect( 0, &sc );

After a successful connection, it's time to execute the sync and then disconnect:

MAILSYNCREQUEST msr;
msr.cbSize    = sizeof(MAILSYNCREQUEST);
msr.cbBufSize = 0;
msr.cbCookie  = 0;
msr.pbCookie  = NULL;
msr.ffFlags   = SYNC_NORMAL;
msr.objType   = 0;
msr.pid       = NULL;
msr.cbId      = 0;
msr.pval      = NULL;

m_pMainView->LogStatus(_T("Calling pIMailSyncHandler->Synchronize()\r\n"));
hr = pIMailSyncHandler->Synchronize( &msr );

m_pMainView->LogStatus(_T("Calling pIMailSyncHandler->Disconnect
                      (...)\r\n"));
hr = pIMailSyncHandler->Disconnect( 0 );

At this time, all messages will be received or sent. You may browse the message store to handle them accordingly.

Going Forward

In this article, we obviously did not touch a lot of CEMAPI capabilities. As usual, it's left as homework. But, cemapi.h will help you to investigate all the rest: Message Forms, Form Providers, Transports ... and more.

Download the sample file here.

About the Author

Alex Gusev started to play with mainframes in the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.





Page 2 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