none
MAPI_E_INVALID_PARAMETER from attempt to programmatically add mailbox on Windows 8 RRS feed

  • Question

  • The following article provides code for adding another user's mailbox to an existing Exchange profile programmatically.

    [I am prevented from posting a link... you will have to google for "How to programmatically add additional mailboxes to an existing mail profile", it is the first hit, a technet blog.]

    The code works okay on Windows 7 and earlier, but on Windows 8, CreateProvider returns MAPI_E_INVALID_PARAMETER. It doesn't matter which version of Outlook is installed on Windows 8, it will always fail in the same way. Presumably there has been an undocumented change to this interface for Windows 8.

    How can one add another user's mailbox to an existing Exchange profile programmatically on Windows 8?

    Wednesday, June 19, 2013 2:41 AM

All replies

  • Please do show your actual code.

    Do all the properties that you pass to the call have the expected values?

    Are you sure you pass the valid MAPIUID?


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Wednesday, June 19, 2013 4:55 PM
  • The code is provided by ThomasNoack in the article I described, to which I am prevented from linking. I shall paste the code here, copied from the article verbatim (I hope Thomas does not mind):

    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;
    }

    Thursday, June 20, 2013 11:41 PM
  • What happens when you step through the code? Are all the parameters correct when you are about to call CreateService?

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, June 21, 2013 2:02 AM
  • Yes, the code works on earlier versions of Windows.
    Sunday, June 23, 2013 11:47 PM
  • I understand that, but what happens when your run your code under the debugger on Windows 8? Can you set a breakpoint on the CreateService line and look at the parameters? Do they all have expected values?

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Sunday, June 23, 2013 11:52 PM
  • Windows 7:

    PR_DISPLAY_NAME (PT_UNICODE): [Nick A. Lawton]
    PR_PROFILE_MAILBOX (PT_STRING8): [nick@sbsdev.local]
    PR_PROFILE_SERVER (PT_STRING8): [sbsdev.local]
    PR_PROFILE_SERVER_DN (PT_STRING8): [sbsdev.local]
    PR_EMSMDB_SECTION_UID (PT_BINARY): [(16:730366B8CA9FD34B8932DBA488C1F0BF) s f    K 2      ]

    Windows 8:

    PR_DISPLAY_NAME (PT_UNICODE): [Nick A. Lawton]
    PR_PROFILE_MAILBOX (PT_STRING8): [nick@sbsdev.local]
    PR_PROFILE_SERVER (PT_STRING8): [sbsdev.local]
    PR_PROFILE_SERVER_DN (PT_STRING8): [sbsdev.local]
    PR_EMSMDB_SECTION_UID (PT_BINARY): [(16:A34107059D89E946A3996172444E8EFF)  A     F  arDN  ]
    Wednesday, June 26, 2013 2:41 AM
  • sbsdev.local is not a valid PR_PROFILE_SERVER_DN - it must be a distinguished name.

    nick@sbsdev.local is not a valid PR_PROFILE_MAILBOX - it must be an EX type address

    Try to add a delegate mailbox through Outlook and take a look at it in OutlookSpy:

    1. Click IMAPISession button

    2. Click AdminServices

    3. in the IMsgServiceAdmin window, go to the GetProviderTable tab.

    4. Locate the row corresponding to the delegate mailbox

    5. Double click on the PR_PROVIDER_UID propety

    6. Click on the "..." button next to the Value edit box, click "Copy As Hex"

    7. In the IMsgServiceAdmin window, click the OpenProfileSection button, paste from the clipboard the value set in (7)

    8. Click Ok to open the IProfSect window


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Wednesday, June 26, 2013 8:11 PM
  • Hi Dmitry,

    thanks for the pointers within Outlook Spy.

    Here are the values as derived from OutlookSpy:"

    ProviderTable::PR_PROVIDER_UID: 158EE2BB22EC9744853B5D7F632519A9

    Relevant Props:
    ->PR_PROFILE_MAILBOX_A::/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Nick
    ->PR_PROFILE_SERVER_DN_A::/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn=MSX
    ->PR_PROFILE_SERVER_A::MSX
    ->PR_DISPLAY_NAME_W::Nick A. Lawton

    Here is what is set when calling Create provider (passing DNs rather than primitives)

    Setting 5 properties:
    Setting PR_DISPLAY_NAME (PT_UNICODE): [Nick A. Lawton]
    Setting PR_PROFILE_MAILBOX (PT_STRING8): [/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Nick]
    Setting PR_PROFILE_SERVER (PT_STRING8): [MSX]
    Setting PR_PROFILE_SERVER_DN (PT_STRING8): [/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn=MSX]
    Setting PR_EMSMDB_SECTION_UID (PT_BINARY): [(16:A34107059D89E946A3996172444E8EFF)  A     F  arDN  ]

    Comparison
    ----------
    The following values were compared (OutlookSpy/PropArray):

    PR_DISPLAY_NAME_W/PR_DISPLAY_NAME(_W) - Same
    PR_PROFILE_MAILBOX_A/PR_PROFILE_MAILBOX - Same
    Setting PR_PROFILE_SERVER_A/PR_PROFILE_SERVER - Same
    PR_PROFILE_SERVER_DN_A/PR_PROFILE_SERVER_DN - Same
    PR_EMSMDB_SECTION_UID/PR_EMSMDB_SECTION_UID - Same


    Conclusion
    ----------
    The values being set into the proparray that is passed to createprovider are the same as the values as appear in the profile section when it is successfully added by MAPI.
    I had indeed already tried using distinguished names, but my example had not included them (the effect was the same).


    I also made a standalone validation application, (which is simply so when the prop arrays and behaviour (at least currently) describe the outcome.

    The additional code is below (ie: turn the example into a standalone exe):



    #include <windows.h>
    #include <stdio.h>
    #include <mapix.h>
    #include <MAPITAGS.H>
    #include <MAPIUTIL.H>
    #include <edkmdb.h>
    #include <sstream>

    using std::ostringstream;


    #ifndef PR_EMSMDB_SECTION_UID
    #define PR_EMSMDB_SECTION_UID           PROP_TAG(PT_BINARY, 0x3D15)
    #endif

    #ifndef MAPI_FORCE_ACCESS
    #define MAPI_FORCE_ACCESS       0x00080000
    #endif



    #ifdef _AMD64_
    #define MSMAPI32PATH "C:\\Program Files\\Common Files\\System\\MSMAPI\\1033\\MSMAPI32.DLL"
    #else
    #define MSMAPI32PATH "C:\\Program Files (x86)\\Common Files\\System\\MSMAPI\\1033\\MSMAPI32.DLL"
    #endif

    void doDelegateTest()
    {
    //for testing only: has no/limited cleanup or unwinding - link dependency: mapi32.lib 
    CoInitialize(NULL);
    HMODULE hmapilib = NULL;

    if (!hmapilib)
    {
    hmapilib = LoadLibrary(MSMAPI32PATH); 
    if (hmapilib)
    {
    MAPIINITIALIZE *pMAPIInitialize = (MAPIINITIALIZE*)GetProcAddress(hmapilib,"MAPIInitialize");

    HRESULT hr = pMAPIInitialize(NULL); 
    if (hr != S_OK) 
    {
    FreeLibrary(hmapilib);
    exit(0);
    }
    }
    else
    {
    printf("check path - its not there");
    exit(0);
    }
    }

    wchar_t* MailboxDisplay = _wcsdup(L"Nick A. Lawton");
    char* MailboxDN = _strdup("/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Nick");
    char* ServerName = _strdup("MSX");
    char* ServerDN = _strdup("/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Configuration/cn=Servers/cn=MSX");
    _ExchangeDelegateTest_AddMailbox("ExchangeDelegateTest", MailboxDisplay, MailboxDN, ServerName, ServerDN);
    }

    int main(int argc, char* argv[])
    {
    doDelegateTest();
    }


    Furthermore, as a test, I created an example Delegate Provider/store wrapper and verified that it works on Windows 7 and earlier.
    Under windows 8, Service entry of that delegate provider does not even get called with the property array passed (so it can initialise itself), but this does work on earlier O/S versions.

    My thinking is that something has changed in Windows 8 in the implementation of the CreateProvider call whereby the interface has either been broken or performs some obscure validation.
    The behaviour has definitely changed (in some way) in relation to its predecessors.

    Thursday, June 27, 2013 2:53 AM
  • Is opening a support case with MS an option?

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Thursday, June 27, 2013 3:03 PM
  • Yes, absolutely. I just thought I would post here since it appeared to be likely that someone else may have experienced the same. I'll post back after we reach some sort of conclusion with Microsoft.

    Thanks again for your help.

    Monday, July 1, 2013 7:06 AM
  • Hi, just an update on this thread with respect to progress (or lack there of) having raised a support request with Microsoft.
    Were advised that they were liasing with their development team, however any (and there have been many) e-mail we send (either to the team lead or the person directly responsible for the call) remains unanswered.
    We will continue to prompt for an update on the issue, but I thought it best that I at least honor my commitment to provide an update within this thread.
    Wednesday, October 23, 2013 12:58 AM
  • Hi, another update. In running this code, we were using the MS SDK and header includes that are distributed with current versions of visual studio (and were running the code mentioned verbatim).
    We subsequently discovered (after much digging) that there is a seperate update for MAPI C++ header includes and once we updated them, the code now works.
    For those attempting to access the delegate functionality, I suggest downloading Outlook2010MAPIHeaders.EXE (which will exapand an updated set of headers for your development environment).

    Thanks again to Dmitry for his efforts in assisting in resolving the issue.

    Wednesday, October 30, 2013 4:31 AM
  • Do you know what was different in the newer version of the SDK? Was it one of the property tags having an invalid value?

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Wednesday, October 30, 2013 4:35 AM
  • It was not one of the property arrays/tags/values - but one of the other arguments passed to the interface function.
    The updated SDK changes many of the MAPI interface functions arguments from ULONG to ULONG_PTR (Most of the changes relate to the treatment of ulUIParam).
    The SDK headers (currently) distributed with Visual Studio have the function prototypes (for some arguments) as ULONG (ie: they lack provisions for 64 bit).
    The suprising thing is that it is only the Windows 8/64 bit MAPI subsystem throws the error (and only for the CreateProvider call).
    The same code on Windows 7/64 works (perhaps earlier versions of the subsystem were more accomodating in light of the fact that the size of the argument changed).

    Specifically relating to adding delegates:
    Old: MAPIMETHOD(CreateProvider) (THIS_ LPTSTR lpszProvider, ULONG cValues, LPSPropValue lpProps, ULONG ulUIParam, ULONG ulFlags, MAPIUID FAR *lpUID) IPURE;
    New: MAPIMETHOD(CreateProvider) (THIS_ LPTSTR lpszProvider, ULONG cValues, LPSPropValue lpProps, ULONG_PTR ulUIParam, ULONG ulFlags, MAPIUID FAR *lpUID) IPURE;

    It took so long to determine this because we had assumed that the distributed SDK was correct/current and did not know about the revision.
    All of the other MAPI interface functions we called (which also use the ulUIParam argument) did not have problems - even with the old headers when running on Windows 8/64.
    Also, in our testing schedule when diagnosing the problem, the test as to whether the problem existed on 32 bit Outlook on Win 8 was identified as a fail (so we had unfortunately discounted portability issues, and only noticed when we retested on 8.1)..
    In this case though, to the credit of the subsystem, the MAPI Error retuned now seems obvious (things always seem obvious after the problems resolved!).
    Thanks again for your help.
    Wednesday, October 30, 2013 10:18 PM