free web page counters

Windows Mobile Pocket PC Smartphone Programming

==>Click here for the SiteMap<==. Original contents with decent amount of source codes.

Tuesday, February 21, 2006

How to programmatically query mail account information in Windows Mobile devices, for example, incoming server and username?

====>SiteMap of this Blog<===

How to programmatically query mail account information in Windows Mobile devices, for example, incoming server and username?

Pocket Outlook manages a few mail accounts. Out of box there is an account called "Outlook E-Mail", which is used to synchronize with Desktop Outlook. For Pocket PC Phone edition and Smartphones, out of box there is another account called "Text Messages", which is used to store SMS. Besides, a user can create new accounts using Pocket Outlook's New Account Wizard and manually typing required information, such as incoming server, outgoing server, username, mail transport (POP3 or IMAP4, or customized ones), and the etc.

Developers can programmatically create mail accounts using omnipotent DMProcessConfigXML. The CSP (Configuration Service Provider) to use is EMAIL2. Read the great example in MSDN on how to achieve this purpose.

There is not much to talk about how to create a mail account. What I am trying to tackle here is how to query back the mail account information, for example, trying to enumerate all the mail accounts that a user has setup in Pocket Outlook, and get the authentication and server information. Such information is useful and handy, if you have your own pieces of application that deals with mail accounts, and you want to prevent users from having to manually type all the information in twice (once in the mail setup and once in your application).

For 5.0 devices, Microsoft provides a new managed namespace to deal with Outlook: Microsoft.WindowsMobile.PocketOutlook. EmailAccountCollection class defines the set of all e-mail accounts that are currently configured on the mobile device. However, the class EmailAccount only provides account name and transport name, which is far less than enough for our purpose. Besides, we also need to target Pocket PC 2003 devices, which does not the managed environment yet.

DMProcessConfigXML is the natural way (and seemingly the only way) to go. The trouble is, you need to feed this nice API with a GUID, like the following:

<wap-provisioningdoc>
  <characteristic type="EMAIL2">
    <characteristic-query type="{D671C70B-8EE3-4881-8045-2AEE6F731B55}"/>
  </characteristic>
</wap-provisioningdoc>

What the heck? Why not simply using the human-readable human-memorable account name, which is unique anyway? Well, the guys in Redmond apparently developed a better appetite to consume GUIDs, which can be supplied, indeed, infinitely. Of course, not infinitely in strict mathematical sense. So in addition to CSLID, IID, LIBID, ..., we have another GUID, the ID to identify each mail account.

Now we need to solve the simple problem of digging out the GUID for each account. How about trying the usual hack to search in registry? No! It does not work. No such information is stored in registry.

Do not give up yet. So let us resort to our friend MAPI here. Hate it or love it, the more than 10-year-old MAPI still powers messaging applications in Windows kingdom, under the hood of CDO or later Outlook PIA. With MAPI being so ancient, you can easily smell its unusualness from its way of using complex structures to define properties and make everything a table of records, every record a number of properties.

And a little bit of knowledge how Pocket Outlook's mail account is linked to an MAPI store. Each mail account is associated with a MAPI store, and a transport. A store is a store, for storing email folders and email messages. The transport is responsible for synchronizing a mail account by talking to the remote server. Outlook itself manages UI and coordinates the transport and the store. Enough with the abstract stuff, two concrete samples: "Outlook E-Mail" is associated with a store called "ActiveSync", "Text Messages" is associated with a store called "SMS".

Back to the main topic: What we need to do is to using MAPI to achieve two purposes:

  • Enumerate all mail accounts
  • Find GUID for each account

The first purpose is easy to achieve. We just need to enumerate all MAPI stores. The straightforward approach is to open the message store table and iterate through all records; for each record, we load the MAPI store name.

The second purpose is a little harder to fulfill. Digging in "cemapi.h", the main header file for CEMAPI (a trimmed version of MAPI for CE device), one line caught my eye:


    // This property is to uniquely identify a store. It's a guid.
    #define PR_CE_UNIQUE_STORE_ID PROP_TAG(PT_CLSID, 0x8113)

Aha! Here we are. This must be the GUID we need. See the comment, its name and the type, exactly a GUID. Since this is a property associated with a store, we can query it in two ways:

  • While iterating through the records in the message store table, we request a column PR_CE_UNICODE_STORE_ID when inspecting each record;
  • Or, we can load the PR_ENTRY_ID for each MAPI store, and open the store then query the property by calling GetProps.

The first approach saves both coding and runtime computation, so below is the code following first approach.

Notice "ActiveSync" and "SMS" are not inspected by the code. This is anticipated, as they do not have "username" or "incoming server" in the normal sense. Also notice EMAIL2 won't give you the password :)

#include "cfgmgrapi.h"
// Query a mail account information using DMProcessConfigXML API
// IN: the account GUID
HRESULT QueryAccountInfo(LPCTSTR szAccountGUID)
{
   LPTSTR szProvXMLOut = NULL;
   TCHAR szProvXMLIn[512];

   memset(szProvXMLIn, 0, 512*sizeof(TCHAR));
   wcscat(szProvXMLIn, TEXT("<wap-provisioningdoc>
      <characteristic type=\"EMAIL2\"><characteristic-query type=\""));
   wcscat(szProvXMLIn, szAccountGUID);
   wcscat(szProvXMLIn, TEXT("\"/>"));
   wcscat(szProvXMLIn, TEXT("</characteristic></wap-provisioningdoc>"));

   HRESULT hr = DMProcessConfigXML(szProvXMLIn,
      CFGFLAG_PROCESS, &szProvXMLOut);
   if (!FAILED(hr)) {
      //...
   } else {
      // ... do error handling
   }
   ALERT(szProvXMLOut);

   DELETE_STR(szProvXMLOut);

   return hr;
}


// Query all stores
HRESULT QueryMsgStore(IMAPISession* pMAPISession)
{
   HRESULT hr = S_OK;
   IMAPITable* pMsgStoresTable = NULL;
   LPSRowSet pRows = NULL;

   enum {
      ePR_DISPLAY_NAME,
      ePR_CE_UNIQUE_STORE_ID,
      NUM_COLS
   };

   // These tags represent the attachment prop information
   // we would like to pick up
   SizedSPropTagArray(NUM_COLS, Columns) =
   {  
      NUM_COLS,
      PR_DISPLAY_NAME,
      PR_CE_UNIQUE_STORE_ID,
   };

   // get the message store table
   hr = pMAPISession->GetMsgStoresTable(0, &pMsgStoresTable);
    EXIT_ON_FAILED(hr);

   // Get the display name and all store IDs
   // for each message store
    hr = pMsgStoresTable->SetColumns((LPSPropTagArray)&Columns, 0);
    EXIT_ON_FAILED(hr);
  
   // loop through
   while (TRUE) {
      hr = pMsgStoresTable->QueryRows(1, 0, &pRows);
      if (FAILED(hr) || pRows->cRows != 1) {
         if (!FAILED(hr)) hr = E_FAIL;
          EXIT_ON_FAILED(hr);
      };

      if (PR_DISPLAY_NAME ==
         pRows->aRow[0].lpProps[ePR_DISPLAY_NAME].ulPropTag
         &&
         PR_CE_UNIQUE_STORE_ID ==
         pRows->aRow[0].lpProps[ePR_CE_UNIQUE_STORE_ID].ulPropTag) {

         // this is the display name column
         TCHAR *pszStoreName = pRows->aRow[0].lpProps[0].Value.LPSZ;
         ALERT(pszStoreName);

         // Now we got the GUID
         LPGUID pGUID = pRows->aRow[0].
            lpProps[ePR_CE_UNIQUE_STORE_ID].Value.lpguid;

         TCHAR szAccountGUID[40] = {0};
         memset(szAccountGUID, 0, 40*sizeof(TCHAR));
         wsprintf(szAccountGUID,
            TEXT("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
            pGUID->Data1, pGUID->Data2, pGUID->Data3,
            pGUID->Data4[0], pGUID->Data4[1],
            pGUID->Data4[2], pGUID->Data4[3],
            pGUID->Data4[4], pGUID->Data4[5],
            pGUID->Data4[6], pGUID->Data4[7]);
         QueryAccountInfo(szAccountGUID);
      }

      if (pRows) {
         FreeProws(pRows);
         pRows = NULL;
      }
   }

FuncExit:
   if (pRows) FreeProws(pRows);
   RELEASE_COMOBJ(pMsgStoresTable);

   return hr;
}

Category: [Outlook / Transport / MAPI]

====>SiteMap of this Blog<===




[ [permalink] ]

6 Comments:

At November 13, 2006 12:57 PM, Anonymous Anonymous said...

PR_CE_UNICODE_STORE_ID does not appear to be a property available from the table of message stores on WM5 PPC.

 
At November 13, 2006 4:08 PM, Anonymous Anonymous said...

nm, it works!

 
At March 25, 2008 7:09 AM, Blogger Michelle S. R. said...

Hello,

I need help with some iPAQs. I've already tried HP support but they didn't help me, and I really need to solve it by the end of this week.

We own approx. 100 x HP iPAQ 2210, 30 x HP iPAQ 2410 and 50 x HP iPAQ 2490 iPaqs.

We`re having trouble using MAPI to retrieve e-mail`s GUIDs in order to make remote configurations for our Sales Force Team. If we follow MS`s documentation, the code works properly in Visual Studio`s emulator, but not in our equipment. We have found that the iPaqs have a different value for PR_CE_UNIQUE_STORE_ID when we query the property pRows->aRow 0 lpProps ePR_CE_UNIQUE_STORE_ID .ulPropTag (MS says it must be 0x81130048 but the iPaq returns another value). And even if we change this value to match iPaq`s one, the GUID returned does not points to a valid e-mail account and thus we cannot use it to manage them. We need to solve this ASAP, since we have a deadline from our Audit team and this problem is hindering the delivery of the solution.

Please if someone have any idea how to solve it, please send me an e-mail: michelle.rosario@garoto.com.br

Best Regards.

Michelle Santos

 
At August 25, 2008 6:27 PM, Anonymous Anonymous said...

It is cool.

 
At February 03, 2009 2:46 AM, Anonymous Anonymous said...

Hello,

Thanks for this valuable information : gave me a lot of help to create accounts from backup files :)

However, I'm facing a problem. When I retrieve accounts manually created on a Windows MObile 5 device, the GUID (PR_CE_UNIQUE_STORE_ID) is not implemented. With WM 6, no problem.

So, my questions are these : do you know the reason why this happends ? Is there a workaround to retrieve the GUID (if it's available) and is there a way to generate a GUID to bind with those accounts ?

Thanks so much for the help :)

Pierre

 
At November 20, 2009 6:35 AM, Anonymous Anonymous said...

Hi I enjoyed your post,. I have been wondering about this issue,so thanks for posting. I’ll likely be coming back to your blog.
Video AC Milan|Video Sepak Bola|How To Make a Kite|How to make a origami|Tips for lossing weight|How to six pack abs

 

Post a Comment

<< Home