MAPI Application: How to add more Exchange mailboxes to a MAPI profile in Outlook 2010 (KB 171636)


We have recently had a few cases where the customers were having some issues with using the code published in the Knowledge Base article 171636 (How to add more Exchange mailboxes to a MAPI profile) with Outlook 2010. When running the code, the additional mailbox either wasn’t added to the profile or it was added but the display name was "Microsoft Exchange Message Store" or simply some garbled text.

I have redeclared some proptags and done some small changes to the code to avoid Unicode vs. ANSI problems. Please find below the working code:

// AddMailbox.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
//The following headers may be required if not already in the project:
#include <stdio.h>
#include <mapix.h>
#include <MAPITAGS.H>
#include <MAPIUTIL.H>
#include <edkmdb.h>
#include <cstring>
#include <string>
#include <iostream>

/*******************************************************************************
AddMailbox

This function will add an additional Exchange mailbox to an existing MAPI profile. It assumes that you have already initialized MAPI.

Parameters:
lpszProfile The name of the profile you are going to modify.
lpszMailboxDisplay The string that will be displayed in the profile UI. Outlook uses the format "Mailbox - John Doe"
lpszMailboxDN The distinguished name of the mailbox to add. Ex. "/o=Microsoft/ou=Test/cn=Recipients/cn=JohnD"
lpszServer The DNS name of the server where the additional mailbox resides.
lpszServerDN The distinguished name of the server where the additional mailbox resides. Ex. "/o=Microsoft/ou=Test/cn=Configuration/cn=TestSrv"

Output:
HRESULT hRes Returns S_OK if completed successfully, otherwise returns a MAPI error.
*******************************************************************************/

#define PidTagProviderDisplay 0x3006001E
#define PR_PROVIDER_DISPLAY PROP_TAG( PT_STRING8, PidTagProviderDisplay )
#define PR_PROVIDER_DISPLAY_A PROP_TAG( PT_STRING8, PidTagProviderDisplay )
#define PR_PROVIDER_DISPLAY_W PROP_TAG( PT_STRING8, PidTagProviderDisplay )

HRESULT AddMailbox(LPSTR lpszProfile,
LPSTR lpszMailboxDisplay,
LPSTR lpszMailboxDN,
LPSTR lpszServer,
LPSTR lpszServerDN)
{
HRESULT hRes = S_OK; // Result code returned from MAPI calls.
LPPROFADMIN lpProfAdmin = NULL; // Profile Admin pointer.
LPSERVICEADMIN lpSvcAdmin = NULL; // Message Service Admin pointer.
LPPROVIDERADMIN lpProvAdmin = NULL; // Provider Admin pointer.
LPMAPITABLE lpMsgSvcTable = NULL; // MAPI table pointer.
LPPROFSECT lpProfileSection = NULL;// Profile Section Pointer.
LPSRowSet lpSvcRows = NULL; // Row set pointer.
SPropValue rgval[4]; // Property value structure to hold configuration info.
SPropValue NewVals; // Property value structure to hold global profile info.
SRestriction sres; // Restriction structure (used in HrQueryAllRows).
SPropValue SvcProps; // Property value structure used in restriction.
LPSPropValue lpGlobalVals = NULL; // Property value struct pointer for global profile section.
ULONG ulProps = 0; // Count of props.
ULONG cbNewBuffer = 0; // Count of bytes for new buffer.

// Enumeration for convenience.
enum {iDispName, iSvcName, iSvcUID, cptaSvc};

// This structure tells HrQueryAllRows what columns we want returned.
SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc,
PR_DISPLAY_NAME,
PR_SERVICE_NAME,
PR_SERVICE_UID };

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

// Get an IProfAdmin interface.
hRes = MAPIAdminProfiles(0, // Flags
&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(LPWSTR(lpszProfile), // Profile that we want to modify.
LPWSTR(""), // 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, // Flags
&lpMsgSvcTable); // Pointer to table
if (FAILED(hRes)) goto error_handler;
printf("Retrieved message service table from profile.\n");

// Set up restriction to query table.
sres.rt = RES_CONTENT;
sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
sres.res.resContent.ulPropTag = PR_SERVICE_NAME_A;
sres.res.resContent.lpProp = &SvcProps;

SvcProps.ulPropTag = PR_SERVICE_NAME_A;
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>0)
{
// Get a 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");
}

// Set up a SPropValue array for the properties you need to configure.
int a = lstrlenA(lpszMailboxDisplay);
BSTR unicodestr = SysAllocStringLen(NULL, a);
MultiByteToWideChar(CP_ACP, 0, lpszMailboxDisplay, a, unicodestr, a);

// First, display name.
ZeroMemory(&rgval[0], sizeof(SPropValue) );
rgval[0].ulPropTag = PR_DISPLAY_NAME_W;
rgval[0].Value.lpszW = unicodestr;
// 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;
// Finally, 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;

// Create the message service with the above properties.
hRes = lpProvAdmin->CreateProvider(LPWSTR("EMSDelegate"),
4,
rgval,
0,
0,
(LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb);
if (FAILED(hRes)) goto error_handler;
printf("The new mailbox is added.\n");

// Now let's set the props we need so that the additional mailbox
// will display in the UI.

// Open the global profile section.
hRes = lpProvAdmin->OpenProfileSection((LPMAPIUID)pbGlobalProfileSectionGuid,
NULL,
MAPI_MODIFY,
&lpProfileSection);
if (FAILED(hRes)) goto error_handler;
printf("Opened global profile section.\n");

// Get the list of store providers in PR_STORE_PROVIDERS.
hRes = lpProfileSection->GetProps((LPSPropTagArray)&sptGlobal,
0,
&ulProps,
&lpGlobalVals);
if (FAILED(hRes)) goto error_handler;
printf("Got the list of mailboxes being opened.\n");

// Now we set up an SPropValue structure with the original
// list + the UID of the new service.

// Compute the new byte count
cbNewBuffer = lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.cb + lpGlobalVals->Value.bin.cb;

// Allocate space for the new list of UIDs.
hRes = MAPIAllocateBuffer( cbNewBuffer,
(LPVOID *)&NewVals.Value.bin.lpb);
if (FAILED(hRes)) goto error_handler;
printf("Allocated buffer to hold new list of mailboxes to be opened.\n");

// Copy the bits into the list.
// First, copy the existing list.
memcpy(NewVals.Value.bin.lpb,
lpGlobalVals->Value.bin.lpb,
lpGlobalVals->Value.bin.cb);

// Next, copy the new UID onto the end of the list.
memcpy(NewVals.Value.bin.lpb + lpGlobalVals->Value.bin.cb,
lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb,
lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.cb);
printf("Concatenated list of mailboxes and new mailbox.\n");

// Set the count of bytes on the SPropValue variable.
NewVals.Value.bin.cb = cbNewBuffer;
// Initialize dwAlignPad.
NewVals.dwAlignPad = 0;
// Set the prop tag.
NewVals.ulPropTag = PR_STORE_PROVIDERS;

// Set the property on the global profile section.
hRes = lpProfileSection->SetProps(ulProps,
&NewVals,
NULL);
if (FAILED(hRes)) goto error_handler;
printf("Set the new list on the global profile section.\n");

goto cleanup;

error_handler:
printf("ERROR: hRes = %0x\n", hRes);

cleanup:
// Clean up.
if (NewVals.Value.bin.lpb) MAPIFreeBuffer(NewVals.Value.bin.lpb);
if (lpGlobalVals) MAPIFreeBuffer(lpGlobalVals);
if (lpSvcRows) FreeProws(lpSvcRows);
if (lpMsgSvcTable) lpMsgSvcTable->Release();
if (lpSvcAdmin) lpSvcAdmin->Release();
if (lpProfAdmin) lpProfAdmin->Release();
if (lpProvAdmin) lpProvAdmin->Release();
if (lpProfileSection) lpProfileSection->Release();

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

int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hRes = S_OK;
MAPIInitialize(NULL);

LPSTR arg1 = new CHAR[lstrlenW(argv[1])+1];
WideCharToMultiByte(CP_ACP, 0, argv[1], -1, arg1, lstrlenW(argv[1])+1, 0, 0);
LPSTR arg2 = new CHAR[lstrlenW(argv[2])+1];
WideCharToMultiByte(CP_ACP, 0, argv[2], -1, arg2, lstrlenW(argv[2])+1, 0, 0);
LPSTR arg3 = new CHAR[lstrlenW(argv[3])+1];
WideCharToMultiByte(CP_ACP, 0, argv[3], -1, arg3, lstrlenW(argv[3])+1, 0, 0);
LPSTR arg4 = new CHAR[lstrlenW(argv[4])+1];
WideCharToMultiByte(CP_ACP, 0, argv[4], -1, arg4, lstrlenW(argv[4])+1, 0, 0);
LPSTR arg5 = new CHAR[lstrlenW(argv[5])+1];
WideCharToMultiByte(CP_ACP, 0, argv[5], -1, arg5, lstrlenW(argv[5])+1, 0, 0);

hRes = AddMailbox(arg1, //Profile name,
arg2, //Mailbox alias,
arg3, //legacyExchangeDN (i.e.: "/o=MyOrganization/ou=My-Site/cn=Recipients/cn=MMailbox"),
arg4, //Exchange Mailbox server FQDN or NETBios name,
arg5); //msExchangeHomeServerName (i.e.: "/o=MyOrganization/ou=My-Site/cn=Configuration/cn=MyServer");

MAPIUninitialize();
return 0;
}

Errors that might pop-up when running this code:

0x80040605

This error might indicate a problem with the MAPI stub files located in C:\Windows\System32. To workaroud this problem, try the following:

  1. Close Outlook and any other MAPI application
  2. Go to C:\Windows\System32
  3. Find mapi32.dll and rename it to mapi32.old
  4. Find fixmapi.exe in the c:\windows\system32 folder and run it

0x80040111

If you get this error, try running the code as a user account that has the rights to edit the profile you are trying to add the additional mailbox to.

Comments (7)

  1. Charlie says:

    Do you have a sample function that removes the mailboxes?  This adds them with the correct display name but I can't remove them…

  2. Charlie says:

    Our RemoveMailBox looks like this (but it doesn't work with the code above):

    HRESULT CMapi::RemoveMailbox(LPTSTR lpszMailbox)

    {

    LPSPropValue lpProp  =   NULL;      

    SPropValue spv;

        // Set up a SPropValue array for the properties you need to configure.

    // Restrict the columns to just PR_DISPLAY_NAME & PR_SERVICE_UID

       static SizedSPropTagArray(2, Columns) =   {2, {PR_DISPLAY_NAME_W,

                                                        PR_PROVIDER_UID}};

    hr = GetEMSServiceUID();

    if(FAILED(hr))

    {

    return hr;

    }

    hr = pSvcAdmin->AdminProviders(&pServiceUID,0,

      &pProviderAdmin);

    if(FAILED(hr))

    {

    return hr;

    }

    hr = pProviderAdmin->GetProviderTable(0, &pMsgSvcTable);

    if(FAILED(hr))

    {

    return hr;

    }

    hr = pMsgSvcTable->SetColumns((LPSPropTagArray)&Columns, 0);

    if(FAILED(hr))

    {

    return hr;

    }

    sres.rt = RES_PROPERTY;

    sres.res.resProperty.relop = RELOP_EQ;

    sres.res.resProperty.ulPropTag = PR_DISPLAY_NAME_W;

    sres.res.resProperty.lpProp = &spv;      

    spv.ulPropTag = PR_DISPLAY_NAME_W;

    spv.Value.lpszW = lpszMailbox; //Name to find.      

    // Apply the above restriction to just Providers with our

    // display name.

    hr = pMsgSvcTable->Restrict(&sres, TBL_ASYNC);

    if(FAILED(hr))

    {

    return hr;

    }

    // Set to beginning of table

    hr = pMsgSvcTable->SeekRow(BOOKMARK_BEGINNING,0,NULL);      

    hr = pMsgSvcTable->QueryRows(4000, 0, &lpRows); // Max 4000 rows.

    // There should be at least one row

    if(lpRows->cRows == 0)

    {

    return hr;

    }

    // We've found the Service. Go get it.

    // We know that the 2nd row has the Service UID. See SetColumns()

    lpProp = &lpRows->aRow[0].lpProps[1];      

    // Allocate space for Service UID and return it.

    hr = MAPIAllocateBuffer(lpProp->Value.bin.cb,

     (LPVOID*)&pProviderUID);      

    // copy the UID into our local.

    memcpy(&pProviderUID.ab, lpProp->Value.bin.lpb,

    lpProp->Value.bin.cb);

    hr = pProviderAdmin->DeleteProvider(&pProviderUID);

    // MessageBox("Mailbox removed");

    return hr;

    }

  3. Andrei Ghita says:

    Hi Charlie,

    I will look into this and let you know if I get the code working.

  4. Charlie says:

    Thank you Andrei, I will test this now.

  5. Steve Lyons says:

    Hi does this work with Vs2010? I'm, not a C++ guy but it seems that some of the headers are missing i.e

    edkmdb.h

    Cheers,

    Steve

  6. Andrei Ghita says:

    Hi Steve,

    edkmdb.h is included in the Exchange SDK: http://www.microsoft.com/…/details.aspx

Skip to main content