Outlooking on Outlook - your answers are here

Outlook Global Technical Support Team Blog

How to programmatically add additional mailboxes to an existing mail profile

How to programmatically add additional mailboxes to an existing mail profile

  • Comments 3
  • Likes

There's a well-known knowledge base article which describes on how to programmatically add mailboxes to a given e-mail profile:
171636 How to add more Exchange mailboxes to a MAPI profile
http://support.microsoft.com/kb/171636/EN-US

This code has been updated and slightly modified in the following blog:
http://blogs.msdn.com/b/emeamsgdev/archive/2011/11/02/how-to-add-more-exchange-mailboxes-to-a-mapi-profile-kb-171636.aspx

This latest code snippet doesn't solve the issue completely, as it only fixes the display problem of the added additional mailbox.

Reason:
Beginning with Outlook 2010, more than one exchange account can be added to an e-mail profile. This leads to the following issues:
- The 'Exchange global section' in the mail profile is no longer used to store information about additional mail profiles
- Each Exchange account is saved with a dynamically created UID in the mail profile

Each added additional mailbox will be stored with it's UID in the PR_STORE_PROVIDERS property of the MSEMS section, but this property has to be in-sync with the PR_STORE_PROVIDERS property of the exchange account itself (the exchange provider).
This is important when adding more than one additional mailbox.

Adding addtional mailboxes to a given e-mail profile requires two steps:
- Create the EMSDelegate provider for the MSEMS service
- Read the the PR_STORE_PROVIDERS property from the MSEMS section and add this prop to the dynamically created exchange account in this profile

Disclaimer

This code is sample code. these samples are provided "as is" without warranty of any kind. Microsoft further disclaims all implied warranties including without limitation any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the samples remains with you. in no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the samples, even if Microsoft has been advised of the possibility of such damages. Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you.


The following code snippet demonstrates this:

HRESULT AddMailbox(LPSTR lpszProfile, LPWSTR lpszMailboxDisplay, LPSTR lpszMailboxDN, LPSTR lpszServer, LPSTR lpszServerDN)
{
 HRESULT             hRes = S_OK;
 LPPROFADMIN         lpProfAdmin = NULL;
 LPSERVICEADMIN      lpSvcAdmin = NULL;
 LPPROVIDERADMIN     lpProvAdmin = NULL;
 LPMAPITABLE         lpMsgSvcTable = NULL;
 LPMAPITABLE         lpMsgProvTable = NULL;
 LPPROFSECT          lpMSEMSProfileSection = NULL;
 LPPROFSECT          lpProvProfileSection = NULL;

 LPSRowSet           lpSvcRows = NULL;
 LPSRowSet           lpProvRows = NULL;
 SPropValue          rgval[5];
 SRestriction        sres;
 SPropValue          SvcProps;

 LPSPropValue        lpMSEMSProp = NULL;  // Property value structure to MSEMS profile info prop
 LPSPropValue        lpProvProp = NULL;  // Property value struct for account-specific prop
 ULONG               ulProps = 0;            // Count of props.
 
 // Enumeration for convenience
 enum {iDispName, iSvcName, iSvcUID, iSctUID, cptaSvc};
 enum {iDName, iProvUID, cptaProv};
   
 // This structure tells HrQueryAllRows what columns we want returned.
 SizedSPropTagArray(cptaSvc, sptCols) = {cptaSvc, PR_DISPLAY_NAME, PR_SERVICE_NAME, PR_SERVICE_UID, PR_EMSMDB_SECTION_UID};
 SizedSPropTagArray(cptaProv, sptCols2) = {cptaProv, PR_DISPLAY_NAME, PR_PROVIDER_UID};

 // This structure tells our GetProps call what properties to get from the 'global' profile section.
 SizedSPropTagArray(1, sptMSEMS) = {1, PR_STORE_PROVIDERS};

 // Get an IProfAdmin interface.
 hRes = MAPIAdminProfiles(0, &lpProfAdmin); // Pointer to new IProfAdmin
 if (FAILED(hRes)) goto error_handler;
 printf("Retrieved IProfAdmin interface.\n");

 // Get an IMsgServiceAdmin interface off of the IProfAdmin interface.
 hRes = lpProfAdmin->AdminServices(lpszProfile,  // Profile that we want to modify.
                                         "",           // Password for that profile.
                                         NULL,         // Handle to parent window.
                                         0,            // Flags.
                                         &lpSvcAdmin); // Pointer to new IMsgServiceAdmin.
 if (FAILED(hRes)) goto error_handler;
 printf("Retrieved IMsgServiceAdmin interface.\n");
          
 // We now need to get the entry id for the Exchange service.
 // First, we get the Message service table.

 hRes = lpSvcAdmin->GetMsgServiceTable(0, &lpMsgSvcTable);
 if (FAILED(hRes)) goto error_handler;
 printf("Retrieved message service table from profile.\n");

 // Set up restriction to query table.
 sres.rt = RES_PROPERTY;
 sres.res.resProperty.relop = RELOP_EQ;
 sres.res.resProperty.ulPropTag = PR_SERVICE_NAME;
 sres.res.resProperty.lpProp = &SvcProps;
       
 SvcProps.ulPropTag = PR_SERVICE_NAME;
 SvcProps.Value.lpszA = "MSEMS";

 // Query the table to get the entry for the Exchange message service.
 hRes = HrQueryAllRows(lpMsgSvcTable, (LPSPropTagArray)&sptCols, &sres, NULL, 0, &lpSvcRows);
 if (FAILED(hRes)) goto error_handler;
 printf("Queried table for Exchange message service.\n");

 if (lpSvcRows->cRows > 1)
 {
  printf("More than one Exchange Account detected....exiting.\n");
  goto error_handler;
 }

 //We only modify the first and primary exchange service
 
 // Get a service provider admin pointer.
 hRes = lpSvcAdmin->AdminProviders((LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb, 0, &lpProvAdmin);
 if (FAILED(hRes)) goto error_handler;
 printf("Retrieved IProviderAdmin interface\n");

 //Get the profile section pointer for the MSEMS service
 hRes = lpProvAdmin->OpenProfileSection((LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb,
                                              NULL,
                                              MAPI_FORCE_ACCESS | MAPI_MODIFY,
                                              &lpMSEMSProfileSection);
 if (FAILED(hRes)) goto error_handler;
 printf("Retrieved profile section interface, MSEMS\n");
   
 // Set up a SPropValue array for the properties you need to configure.

 // First, display name, as Unicode.
 ZeroMemory(&rgval[0], sizeof(SPropValue) );
 rgval[0].ulPropTag = PR_DISPLAY_NAME_W;
 rgval[0].Value.lpszW = lpszMailboxDisplay;
   
 // Next, the DN of the mailbox.
 ZeroMemory(&rgval[1], sizeof(SPropValue) );
 rgval[1].ulPropTag = PR_PROFILE_MAILBOX;
 rgval[1].Value.lpszA = lpszMailboxDN;
   
 // Next, the name of the server the mailbox is on.
 ZeroMemory(&rgval[2], sizeof(SPropValue) );
 rgval[2].ulPropTag = PR_PROFILE_SERVER;
 rgval[2].Value.lpszA = lpszServer;
   
 // Next, the DN of the server the mailbox is on.
 ZeroMemory(&rgval[3], sizeof(SPropValue) );
 rgval[3].ulPropTag = PR_PROFILE_SERVER_DN;
 rgval[3].Value.lpszA = lpszServerDN;
   
 // Finally, the PR_EMSMDB_SECTION_UID
 ZeroMemory(&rgval[4], sizeof(SPropValue));
 rgval[4].ulPropTag = PR_EMSMDB_SECTION_UID;
 MAPIAllocateBuffer(sizeof(MAPIUID), (LPVOID *) &rgval[4].Value.bin.lpb);
 memcpy(rgval[4].Value.bin.lpb, (LPMAPIUID)lpSvcRows->aRow->lpProps[iSctUID].Value.bin.lpb, lpSvcRows->aRow->lpProps[iSctUID].Value.bin.cb);
 rgval[4].Value.bin.cb = sizeof(MAPIUID);
   
 // Create the message service with the above properties
 hRes = lpProvAdmin->CreateProvider("EMSDelegate", 5, rgval, 0, 0, (LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb);
 if (FAILED(hRes)) goto error_handler;
 printf("The new mailbox is added.\n");
 
 //With OL2010 and higher, the new added EMSDelegate will be added to the PR_STORE_PROVIDERS prop for the MSEMS section
 //Read the PR_STORE_PROVIDERS from the MSEMS section
 hRes = lpMSEMSProfileSection->GetProps((LPSPropTagArray)&sptMSEMS, 0, &ulProps, &lpMSEMSProp);
 if (FAILED(hRes)) goto error_handler;
 printf("Read MSEMS profile section prop.\n"); 

 //But it needs to be added to the PR_STORE_PROVIDERS prop for the primary exchange account in this profile
 //So I need to loop thru the list of installed services
 hRes = lpProvAdmin->GetProviderTable(0, &lpMsgProvTable);
 if (FAILED(hRes)) goto error_handler;
 printf("Got provider table.\n");

 hRes = HrQueryAllRows(lpMsgProvTable, (LPSPropTagArray)&sptCols2, NULL, NULL, 0, &lpProvRows);
 if (FAILED(hRes)) goto error_handler;
 printf("Got provider services.\n");

 for(ULONG ProvCnt = 0; ProvCnt < lpProvRows->cRows; ProvCnt++)
 {
  //Read the props for each provider and check the PR_STORE_PROVIDERS prop
  hRes = lpProvAdmin->OpenProfileSection((LPMAPIUID)lpProvRows->aRow[ProvCnt].lpProps[iProvUID].Value.bin.lpb,
            NULL,
            MAPI_FORCE_ACCESS | MAPI_MODIFY,
            &lpProvProfileSection);
  if(S_OK == hRes)
  {
   hRes = lpProvProfileSection->GetProps((LPSPropTagArray)&sptMSEMS, 0, &ulProps, &lpProvProp);
   if((lpProvProp) && (PROP_TYPE(lpProvProp->ulPropTag) != PT_ERROR) && (PROP_ID(lpProvProp->ulPropTag) == PROP_ID(PR_STORE_PROVIDERS)))
   {
    if(lpMSEMSProp->Value.bin.cb != lpProvProp->Value.bin.cb)
    {
     //Need to replace the PR_STORE_PROVIDERS for the account with the correct one from MSEMS
     hRes = lpProvProfileSection->SetProps(ulProps, lpMSEMSProp, NULL);
     if (hRes == S_OK)
      printf("Set provider properties.\n");
    }
    //Release these two props
    MAPIFreeBuffer(lpMSEMSProp);
    MAPIFreeBuffer(lpProvProp);
   }

   if(lpProvProfileSection)
    lpProvProfileSection->Release();
  }
 }
 
 goto cleanup;
      
 error_handler:
  printf("ERROR: hRes = %0x\n", hRes);

 cleanup:
  // Clean up the new stuff
  if(lpProvRows)
   FreeProws(lpProvRows);
  if(lpMsgProvTable)
   lpMsgProvTable->Release();

 // Clean up
 if (lpSvcRows) FreeProws(lpSvcRows);
 if (lpMsgSvcTable) lpMsgSvcTable->Release();
 if (lpSvcAdmin) lpSvcAdmin->Release();
 if (lpProfAdmin) lpProfAdmin->Release();
 if (lpProvAdmin) lpProvAdmin->Release();
 if (lpMSEMSProfileSection) lpMSEMSProfileSection ->Release();

 printf("Done cleaning up.\n");
 return hRes;
}

Comments
  • Is this code will work seamlessly with older versions of Outlook (2007/2003/etc..)?

  • I have tested this code with various version of Outlook and Windows with mixed results.

    This code works on Windows pre-8 with any version of Outlook, but it fails on Windows 8 with Outlook 2013, and 2010 and probably earlier as well, CreateProvider returns MAPI_E_INVALID_PARAMETER.

    Windows 8 must have some special undocumented differences in its MAPI subsystem that prevents this from working.

  • This step-by-step article details the process of adding a mailbox to a MAPI profile. This will allow you to open this additional mailbox with delegate access. The owner of this mailbox must grant you delegate access prior to following this process.

    This process is the programmatic equivalent to:
    Launching the mail program from the control panel.
    Selecting a profile that has the Microsoft Exchange Server service installed.
    Pulling up the Properties for Microsoft Exchange Server.
    Selecting the Advanced tab.
    Adding an additional mailbox to open.
    The sample code below illustrates the steps needed to add an additional mailbox to an existing MAPI profile.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment