Programmatically connecting to another mailbox when MAPI / HTTP is used

I've been meaning to create this post for a while, but haven't got around to it. I had a case recently where a customer wanted to connect to a user's Archive mailbox for which the service application had permissions.  However, the transport in use was MAPI over HTTP (MAPI/HTTP).  He was getting the error MAPI_E_CALL_FAILED (0x80004005) from the call to IMAPISession::OpenMsgStore().  His code worked when the transport was RPC over HTTP (ROH).

Introduction

When a MAPI client application attempts to connect to multiple mailboxes using a “hub and spoke” type approach, wherein the MAPI application connects to an initial mailbox and then connects to additional mailboxes for some sort of processing, if the transport in use is MAPI/HTTP, IMAPISession::OpenMsgStore() returns MAPI_E_CALL_FAILED (0x80004005).  This may be because the EntryID passed to OpenMsgStore() is not valid.  The EntryID needed to connect using MAPI/HTTP is a version 3 (v3) EntryID.  IExchangeManageStore::CreateStoreEntryID() can only create version 1 (v1) EntryIDs.  After all, this method was created for the early versions of Exchange Server.  Therefore, a new interface is needed in order to create v3 EntryIDs.  Enter IExchangeManageStoreEx.  This interface was previously documented in a former blog post.  However, the method CreateStoreEntryID2() takes additional data.

First,  you will need to define the new property PR_PROFILE_USER_SMTP_ADDRESS_W in your code base.

 // 0x6641001F
#define PR_PROFILE_USER_SMTP_ADDRESS_W PROP_TAG(PT_UNICODE, 0x6641)

Next, you add that to the list of properties passed to the method CreateStoreEntryID2(). This property is what is used by Outlook to perform an Autodiscover to get the mailbox settings for the target mailbox.

 LPSPropValue         lpspv = nullptr;
MAPIAllocateBuffer((sizeof(SPropValue) * 4), (LPVOID*)&lpspv);
ZeroMemory(lpspv, sizeof(SPropValue) * 4);

lpspv[0].ulPropTag = PR_PROFILE_MAILBOX;
lpspv[0].Value.lpszA = TargetMailboxDN;

lpspv[1].ulPropTag = PR_PROFILE_MDB_DN;
lpspv[1].Value.lpszA = TargetStoreDN;

lpspv[2].ulPropTag = PR_FORCE_USE_ENTRYID_SERVER;
lpspv[2].Value.b = true;

lpspv[3].ulPropTag = PR_PROFILE_USER_SMTP_ADDRESS_W;
lpspv[3].Value.lpszW = L"Susan@Contoso.com";

hres = m_pMsgPrivStore->QueryInterface(IID_IExchangeManageStoreEx,
                                                                     (LPVOID*)&pManageStoreEx);
if (FAILED(hRes) || pManageStoreEx == nullptr)
{
    // Handle the error
    goto Cleanup;
}

hRes = pManageStoreEx->CreateStoreEntryID2(4, 
                        lpspv, 
                        ulCreateStoreEIDFlags, 
                        &cbEID,
                        &lpEntryId);
Cleanup:
MAPIFreeBuffer(lpsv);

From there you call IMAPISession::OpenMsgStore() as you did before passing the v3 EntryID you obtained from the call to IExchangeManageStoreEx::CreateStoreEntryID2().

More Information