Article ID: 934877
Article Last Modified on 10/25/2007
APPLIES TO
- Microsoft Exchange Server 2003 Enterprise Edition
- Microsoft Exchange Server 2003 Standard Edition
- Microsoft Exchange 2000 Enterprise Server
- Microsoft Exchange 2000 Server Standard Edition
INTRODUCTION
This article describes how to enable the MAPI global catalog server reconnect logic to work correctly in a MAPI profile. This logic is added when you install either of the following Microsoft Exchange Server updates:
- Microsoft Exchange Server 2003 Service Pack 1 (SP1)
For more information, visit the following Microsoft Web site: - The August 2004 Exchange 2000 post-Service Pack 3 (SP3) Update Rollup
For more information, click the following article number to view the article in the Microsoft Knowledge Base:870540 Availability of the August 2004 Exchange 2000 Server Post-Service Pack 3 Update Rollup
MORE INFORMATION
If a global catalog server is unavailable, the MAPI global catalog server reconnect logic lets a MAPI program recover dynamically by releasing the unavailable server. Then, the MAPI global catalog server reconnect logic obtains a new referral from Exchange Server without starting a new MAPI session.
The MAPI global catalog reconnect logic uses the following properties in a MAPI profile:
- PR_PROFILE_ABP_ALLOW_RECONNECT
- Any value that is greater than zero enables the reconnect logic.
- PR_PROFILE_ABP_MTHREAD_TIMEOUT_SECS
- This property indicates the number of seconds that multiple threads that are accessing the address book at the same time should block while they wait for the first thread that enters the reconnect logic to finish.
- PR_PROFILE_SERVER_VERSION
- The Exchange Server address book provider (EMSABP32.dll) checks this property on the global profile section to determine the Exchange Server version. A value of 3000 indicates Exchange 2000.
Therefore, you must set the PR_PROFILE_ABP_ALLOW_RECONNECT property to a positive value to turn on the reconnect logic. Additionally, set the PR_PROFILE_SERVER_VERSION property to 3000 if there are no servers that are running Microsoft Exchange Server 5.5 servers in your organization.
These properties are set on the EMSABP profile section. Use the OpenProfileSection function and the following UID.
#define MUIDEMSAB "\xDC\xA7\x40\xC8\xC0\x42\x10\x1A\xB4\xB9\x08\x00\x2B\x2F\xE1\x82"
To enable the MAPI global catalog server reconnect logic to work correctly in a MAPI profile, follow these steps:
- Start Microsoft Visual C++ 6.0.
- On the File menu, click New.
- Click Win32 Console Application, type GCReconnect in the Project Name box, and then click OK.
- Click An empty project, and then click Finish.
- In the Workspace window, click the FileView tab.
- In the Workspace window, right-click Header Files, and then click Add Files to Folder.
- In the File name box, type Profiles.h, and then click OK.
- In the Microsoft Visual C++ dialog box, click Yes.
- In the Workspace window, right-click Profiles.h, and then click Open.
- In the Microsoft Visual C++ dialog box, click Yes.
Add the following code to the Profiles.h file.
#pragma once // PROF SECT GUIDS #define pbGlobalProfileSectionGuid "\x13\xDB\xB0\xC8\xAA\x05\x10\x1A\x9B\xB0\x00\xAA\x00\x2F\xC4\x5A" #define MUIDEMSAB "\xDC\xA7\x40\xC8\xC0\x42\x10\x1A\xB4\xB9\x08\x00\x2B\x2F\xE1\x82" // FLAGS #define MAPI_FORCE_ACCESS 0x00080000 STDMETHODIMP CreateProfile(LPSTR lpszProfileName, LPSTR lpszExchangeServer, LPSTR lpszMailbox); STDMETHODIMP DeleteProfile(LPSTR lpszProfileName); STDMETHODIMP OpenGlobalProfileSection(LPSTR lpszProfile, LPPROFSECT * lppProfSect); STDMETHODIMP SvcAdminOpenProfileSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpUID, LPCIID lpInterface, ULONG ulFlags, LPPROFSECT FAR * lppProfSect); STDMETHODIMP ProvAdminOpenProfileSection(LPPROVIDERADMIN lpProvAdmin, LPMAPIUID lpUID, LPCIID lpInterface, ULONG ulFlags, LPPROFSECT FAR * lppProfSect); STDMETHODIMP EnableReconnect(LPSTR lpszProfile);
- In the Workspace window, right-click Source Files, and then click Add Files to Folder.
- In the File name box, type Profiles.cpp, and then click OK.
- In the Microsoft Visual C++ dialog box, click Yes.
- In the Workspace window, right-click Profiles.cpp, and then click Open.
- In the Microsoft Visual C++ dialog box, click Yes.
Add the following code to the Profiles.cpp file.
#include <mapix.h> #include <mapiutil.h> #include <edkmdb.h> #include "Profiles.h" /*********************************************************************** STDMETHODIMP CreateProfile(LPSTR lpszProfileName, LPSTR lpszExchangeServer, LPSTR lpszMailbox) - lpszProfileName: [in] Name of profile to be created - lpszExchangeServer: [in] Name of the Exchange server - lpszMailbox: [in] Name of the mailbox This procedure is fairly straightforward. It creates a profile by using the MAPI interfaces. ***********************************************************************/ STDMETHODIMP CreateProfile(LPSTR lpszProfileName, LPSTR lpszExchangeServer, LPSTR lpszMailbox) { HRESULT hRes = S_OK; // Result from MAPI calls LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object // Get an IProfAdmin interface hRes = MAPIAdminProfiles(0, // Flags &lpProfAdmin); // Pointer to new IProfAdmin if(SUCCEEDED(hRes) && lpProfAdmin) { // Create a new profile hRes = lpProfAdmin->CreateProfile((LPTSTR)lpszProfileName, // Name of new profile NULL, // Password for profile NULL, // Handle to parent window NULL); // Flags if(SUCCEEDED(hRes)) { LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object // Get an IMsgServiceAdmin interface off the IProfAdmin interface hRes = lpProfAdmin->AdminServices((LPTSTR)lpszProfileName, // Profile that we want to modify NULL, // Password for that profile NULL, // Handle to parent window 0, // Flags &lpSvcAdmin); // Pointer to new IMsgServiceAdmin if(SUCCEEDED(hRes) && lpSvcAdmin) { // Create the new message service for Exchange hRes = lpSvcAdmin->CreateMsgService((LPTSTR)"MSEMS", // Name of service from MAPISVC.INF NULL, // Display name of service NULL, // Handle to parent window NULL); // Flags if(SUCCEEDED(hRes)) { // We now have to obtain the entry ID for the new service. // You can do this by obtaining the message service table // and by obtaining the entry that corresponds to the new service. LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services hRes = lpSvcAdmin->GetMsgServiceTable(0, // Flags &lpMsgSvcTable); // Pointer to table if(SUCCEEDED(hRes) && lpMsgSvcTable) { LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query SRestriction sres; // Restriction structure SPropValue SvcProps; // Property structure for restriction // 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; SvcProps.Value.lpszA = "MSEMS"; // Query the table to obtain the entry for the newly created message service. // This indicates the columns that we want to be returned from HrQueryAllRows enum {iSvcName, iSvcUID, cptaSvc}; SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc, PR_SERVICE_NAME_A, PR_SERVICE_UID }; hRes = HrQueryAllRows(lpMsgSvcTable, (LPSPropTagArray)&sptCols, &sres, NULL, 0, &lpSvcRows); if(SUCCEEDED(hRes) && lpSvcRows) { // Set up a SPropValue array for the properties that you have to configure. SPropValue rgval[2]; int i = 0; // First, the server name ZeroMemory(&rgval[i], sizeof(SPropValue) ); rgval[i].ulPropTag = PR_PROFILE_UNRESOLVED_SERVER; rgval[i++].Value.lpszA = lpszExchangeServer; // Next, the mailbox name ZeroMemory(&rgval[i], sizeof(SPropValue) ); rgval[i].ulPropTag = PR_PROFILE_UNRESOLVED_NAME; rgval[i++].Value.lpszA = lpszMailbox; hRes = lpSvcAdmin->ConfigureMsgService((LPMAPIUID)lpSvcRows->aRow[0].lpProps[1].Value.bin.lpb, // Entry ID of service to configure NULL, // Handle to parent window 0, // Flags i, // Number of properties that we are setting rgval); // Pointer to SPropValue array } if (lpSvcRows) FreeProws(lpSvcRows); } if (lpMsgSvcTable) lpMsgSvcTable->Release(); } } if (lpSvcAdmin) lpSvcAdmin->Release(); } } if (lpProfAdmin) lpProfAdmin->Release(); return hRes; } /*********************************************************************** STDMETHODIMP DeleteProfile(LPSTR lpszProfileName) - lpszProfileName: [in] Name of profile to delete This procedure is fairly straightforward. It deletes the indicated profile by using the MAPI interfaces. ***********************************************************************/ STDMETHODIMP DeleteProfile(LPSTR lpszProfileName) { HRESULT hRes = S_OK; LPPROFADMIN lpProfAdmin = NULL; // Get an IProfAdmin interface hRes = MAPIAdminProfiles(0, // Flags &lpProfAdmin); // Pointer to new IProfAdmin if(SUCCEEDED(hRes) && lpProfAdmin) { hRes = lpProfAdmin->DeleteProfile(lpszProfileName, 0); } if (lpProfAdmin) lpProfAdmin->Release(); return hRes; } STDMETHODIMP OpenGlobalProfileSection(LPSTR lpszProfile, LPPROFSECT * lppProfSect) { HRESULT hRes = S_OK; LPPROFADMIN lpProfAdmin = NULL; hRes = MAPIAdminProfiles(0, &lpProfAdmin); if(SUCCEEDED(hRes) && lpProfAdmin) { LPSERVICEADMIN lpSvcAdmin = NULL; hRes = lpProfAdmin->AdminServices((LPTSTR)lpszProfile, NULL, NULL, 0, &lpSvcAdmin); if(SUCCEEDED(hRes) && lpSvcAdmin) { hRes = lpSvcAdmin->OpenProfileSection((LPMAPIUID)&pbGlobalProfileSectionGuid, NULL, 0, lppProfSect); lpSvcAdmin->Release(); } lpProfAdmin->Release(); } return hRes; } STDMETHODIMP SvcAdminOpenProfileSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpUID, LPCIID lpInterface, ULONG ulFlags, LPPROFSECT FAR * lppProfSect) { HRESULT hRes = S_OK; // Note: We have to open the profile section with full access. // MAPI discriminates who can modify profiles, especially // in certain sections. The way to force access has changed in // different versions of Outlook. Therefore, there are two methods. See KB article 822977 // for more information. // First, let us try the easier method of passing the MAPI_FORCE_ACCESS flag // to OpenProfileSection. This method is available only in Outlook 2003 and in later versions of Outlook. hRes = lpSvcAdmin->OpenProfileSection(lpUID, lpInterface, ulFlags | MAPI_FORCE_ACCESS, lppProfSect); if(FAILED(hRes)) { // If this does not succeed, it may be because you are using an earlier version of Outlook. // In this case, use the sample code // from KB article 228736 for more information. Note: This information was compiled from that sample. // /////////////////////////////////////////////////////////////////// // MAPI will always return E_ACCESSDENIED // when we open a profile section on the service if we are a client. The workaround // is to call into one of MAPI's internal functions that bypasses // the security check. We build an interface to it, and then point to it from our // offset of 0x48. USE THIS METHOD AT YOUR OWN RISK! THIS METHOD IS NOT SUPPORTED! interface IOpenSectionHack : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE OpenSection(LPMAPIUID, ULONG, LPPROFSECT*) = 0; }; IOpenSectionHack** ppProfile = (IOpenSectionHack**)((((BYTE*)lpSvcAdmin) + 0x48)); // Now, we want to open the Services Profile Section and store that // interface with the Object hRes = (*ppProfile)->OpenSection(lpUID, ulFlags, lppProfSect); // /////////////////////////////////////////////////////////////////// } return hRes; } STDMETHODIMP ProvAdminOpenProfileSection(LPPROVIDERADMIN lpProvAdmin, LPMAPIUID lpUID, LPCIID lpInterface, ULONG ulFlags, LPPROFSECT FAR * lppProfSect) { HRESULT hRes = S_OK; hRes = lpProvAdmin->OpenProfileSection(lpUID, lpInterface, ulFlags | MAPI_FORCE_ACCESS, lppProfSect); if((FAILED(hRes)) && (MAPI_E_UNKNOWN_FLAGS == hRes)) { // The MAPI_FORCE_ACCESS flag is implemented only in Outlook 2002 and in later versions of Outlook. // // // Makes MAPI think we are a service and not a client. // MAPI grants us Service Access. This makes it all possible. *(((BYTE*)lpProvAdmin) + 0x60) = 0x2; // USE THIS METHOD AT YOUR OWN RISK! THIS METHOD IS NOT SUPPORTED! hRes = lpProvAdmin->OpenProfileSection(lpUID, lpInterface, ulFlags, lppProfSect); } return hRes; } STDMETHODIMP EnableReconnect(LPSTR lpszProfile) { HRESULT hRes = S_OK; LPPROFADMIN lpProfAdmin = NULL; hRes = MAPIAdminProfiles(0, &lpProfAdmin); if(SUCCEEDED(hRes) && lpProfAdmin) { LPSERVICEADMIN lpSvcAdmin = NULL; hRes = lpProfAdmin->AdminServices((LPTSTR)lpszProfile, NULL, NULL, 0, &lpSvcAdmin); if(SUCCEEDED(hRes) && lpSvcAdmin) { LPPROFSECT lpABProfSect = NULL; hRes = SvcAdminOpenProfileSection(lpSvcAdmin, (LPMAPIUID)&MUIDEMSAB, NULL, MAPI_MODIFY, &lpABProfSect); if(SUCCEEDED(hRes) && lpABProfSect) { SPropValue spReconnectProps[2] = {0}; spReconnectProps[0].ulPropTag = PR_PROFILE_ABP_ALLOW_RECONNECT; spReconnectProps[0].Value.l = 1; spReconnectProps[1].ulPropTag = PR_PROFILE_ABP_MTHREAD_TIMEOUT_SECS; spReconnectProps[1].Value.l = 10; hRes = lpABProfSect->SetProps(2, spReconnectProps, NULL); if(SUCCEEDED(hRes)) { LPPROFSECT lpGlobalProfSect = NULL; hRes = SvcAdminOpenProfileSection(lpSvcAdmin, (LPMAPIUID)&pbGlobalProfileSectionGuid, NULL, MAPI_MODIFY, &lpGlobalProfSect); if(SUCCEEDED(hRes) && lpGlobalProfSect) { SPropValue spServerVersion = {0}; spServerVersion.ulPropTag = PR_PROFILE_SERVER_VERSION; spServerVersion.Value.l = 3000; hRes = lpGlobalProfSect->SetProps(1, &spServerVersion, NULL); } if(lpGlobalProfSect) lpGlobalProfSect->Release(); } } if(lpABProfSect) lpABProfSect->Release(); } if(lpSvcAdmin) lpSvcAdmin->Release(); } if(lpProfAdmin) lpProfAdmin->Release(); return hRes; }
- In the Workspace window, right-click Source Files, and then click Add Files to Folder.
- In the File name box, type Main.cpp, and then click OK.
- In the Microsoft Visual C++ dialog box, click Yes.
- In the Workspace window, right-click Main.cpp, and then click Open.
- In the Microsoft Visual C++ dialog box, click Yes.
Add the following code to the Main.cpp file.
#include <mapix.h> #include <mapiutil.h> #include <stdio.h> #include <conio.h> //#include <strsafe.h> #include "Profiles.h" struct MYOPTIONS { BOOL bDisplayUsage; LPSTR lpszMailbox; LPSTR lpszServer; LPSTR lpszResolveTarget; }; void DisplayUsage() { printf("USAGE: GCReconnect [-?] -m MAILBOX -s SERVER -r RESOLVE NAME\n\n"); printf("\t[ARGUMENTS]\n"); printf("\t-?\n"); printf("\t OPTIONAL. Displays this usage information.\n\n"); printf("\t-m MAILBOX\n"); printf("\t REQUIRED. Specifies the mailbox to log on to.\n\n"); printf("\t-s SERVER\n"); printf("\t REQUIRED. Specifies the Exchange server where MAILBOX resides.\n\n"); printf("\t-r RESOLVE NAME\n"); printf("\t REQUIRED. Specifies the name to resolve with ResolveName\n"); } BOOL ParseArgs(int argc, char * argv[], MYOPTIONS * pRunOpts) { if(!pRunOpts) return FALSE; ZeroMemory(pRunOpts, sizeof(MYOPTIONS)); for(int i = 1; i < argc; i++) { if (_stricmp(argv[i], "-?") == 0) { // User requests usage. Exit. pRunOpts->bDisplayUsage = TRUE; return TRUE; } else if (_stricmp(argv[i], "-m") == 0) { // Mailbox name pRunOpts->lpszMailbox = argv[++i]; } else if (_stricmp(argv[i], "-s") == 0) { // Server name pRunOpts->lpszServer = argv[++i]; } else if (_stricmp(argv[i], "-r") == 0) { // Name to resolve pRunOpts->lpszResolveTarget = argv[++i]; } } if(!pRunOpts->lpszMailbox || !pRunOpts->lpszServer || !pRunOpts->lpszResolveTarget) return FALSE; return TRUE; } void DoMAPI(LPSTR lpszProfile, LPSTR lpszResolveTarget) { HRESULT hRes = S_OK; LPMAPISESSION lpSession = NULL; printf("Calling MAPILogonEx\n"); hRes = MAPILogonEx(0, lpszProfile, NULL, MAPI_NEW_SESSION | MAPI_EXTENDED | MAPI_NO_MAIL, &lpSession); if(SUCCEEDED(hRes) && lpSession) { LPADRBOOK lpAdrBook = NULL; LPENTRYID lpEID = NULL; ULONG cbEID = NULL; printf("Calling QueryIdentity\n"); hRes = lpSession->QueryIdentity(&cbEID,&lpEID); if (SUCCEEDED(hRes) && cbEID && lpEID) { printf("Calling OpenAddressBook\n"); hRes = lpSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &lpAdrBook); if(SUCCEEDED(hRes) && lpAdrBook) { int i = 0; printf("Starting OpenEntry loop. Hit any key to exit...\n"); while (!_kbhit()) { i++; printf("0x%08X : ",i); LPMAILUSER lpABUser = NULL; ULONG ulObjType = NULL; hRes = lpAdrBook->OpenEntry( cbEID, lpEID, NULL, NULL, &ulObjType, (LPUNKNOWN*) &lpABUser); if(SUCCEEDED(hRes)) { printf("OpenEntry succeeded.\n"); if (lpABUser) { SPropTagArray sTag; sTag.cValues = 1; sTag.aulPropTag[0] = PR_DISPLAY_NAME; ULONG ulVal = NULL; LPSPropValue lpVal = NULL; hRes = lpABUser->GetProps(&sTag,NULL,&ulVal,&lpVal); if(SUCCEEDED(hRes) && 1 == ulVal && lpVal && lpVal->ulPropTag == PR_DISPLAY_NAME && lpVal->Value.LPSZ) { printf("Got display name %s\n",lpVal->Value.LPSZ); } MAPIFreeBuffer(lpVal); } } else if(hRes == MAPI_E_END_OF_SESSION) { printf("Session ended. Retrying OpenEntry call.\n"); } else { printf("Error calling OpenEntry. HRESULT = 0x%08X\n", hRes); } if (lpABUser) lpABUser->Release(); lpABUser = NULL; } /* printf("Starting ResolveName loop. Hit any key to exit...\n"); while (!_kbhit()) { LPADRLIST lpAdrList = NULL; int cb = CbNewADRLIST(1); hRes = MAPIAllocateBuffer(cb, (LPVOID*)&lpAdrList); if(SUCCEEDED(hRes) && lpAdrList) { hRes = MAPIAllocateBuffer(sizeof(SPropValue), (LPVOID FAR*)&lpAdrList->aEntries[0].rgPropVals); if(SUCCEEDED(hRes) && lpAdrList->aEntries[0].rgPropVals) { lpAdrList->cEntries = 1; lpAdrList->aEntries[0].cValues = 1; lpAdrList->aEntries[0].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME; lpAdrList->aEntries[0].rgPropVals[0].Value.lpszA = lpszResolveTarget; hRes = lpAdrBook->ResolveName(0, 0, NULL, lpAdrList); if(SUCCEEDED(hRes)) { printf("Resolved %s successfully.\n", lpszResolveTarget); } else if(hRes == MAPI_E_END_OF_SESSION) { printf("Session ended. Retrying ResolveName call.\n"); } else { printf("Error calling ResolveName. HRESULT = 0x%08X\n", hRes); } } } else { printf("Error allocating new address list. HRESULT = 0x%08X\n", hRes); } if(lpAdrList)FreePadrlist(lpAdrList); lpAdrList = NULL; Sleep(1000); }*/ } else { printf("Error opening address book. HRESULT = 0x%08x\n", hRes); } } } else { printf("Error logging on. HRESULT = 0x%08X\n", hRes); } } void main(int argc, char* argv[]) { MYOPTIONS ProgOpts = {0}; if(!ParseArgs(argc, argv, &ProgOpts) || ProgOpts.bDisplayUsage) { DisplayUsage(); return; } printf("GC Reconnect Tester\n\n"); printf("\tMailbox: %s\n", ProgOpts.lpszMailbox); printf("\tServer: %s\n", ProgOpts.lpszServer); printf("\tResolve Name: %s\n\n", ProgOpts.lpszResolveTarget); HRESULT hRes = S_OK; hRes = MAPIInitialize(NULL); if(SUCCEEDED(hRes)) { char szProfile[256] = {0}; SYSTEMTIME SysTime = {0}; GetSystemTime(&SysTime); hRes = sprintf(szProfile, "GCReconnect%d%d%d%d%d%d", SysTime.wMonth, SysTime.wDay, SysTime.wYear, SysTime.wHour, SysTime.wMinute, SysTime.wSecond); if(SUCCEEDED(hRes)) { hRes = CreateProfile(szProfile, ProgOpts.lpszServer, ProgOpts.lpszMailbox); if(SUCCEEDED(hRes)) { hRes = EnableReconnect(szProfile); if(SUCCEEDED(hRes)) { printf("Profile \"%s\" created and enabled for reconnect.\n\n", szProfile); DoMAPI(szProfile, ProgOpts.lpszResolveTarget); } else { printf("Error enabling reconnect. HRESULT = 0x%08X\n", hRes); } } else { printf("Error creating profile. HRESULT = 0x%08X\n", hRes); } hRes = DeleteProfile(szProfile); if(SUCCEEDED(hRes)) { printf("Successfully deleted profile.\n"); } else { printf("Error deleting profile. HRESULT = 0x%08X\n", hRes); } } else { printf("Error formatting profile name string. HRESULT = 0x%08X\n", hRes); } MAPIUninitialize(); } else { printf("Error initializing MAPI. HRESULT = 0x%08X\n", hRes); } }
- On the Build menu, click Rebuild All.
REFERENCES
The latest Edkmdb.h file is available at the Microsoft Download Center.
The following file is available for download from the Microsoft Download Center:
Download the ExchangeSDKTools.exe package now.
For more information, click the following article number to view the article in the Microsoft Knowledge Base:
929439 A MAPI program may not obtain a referral server when the global catalog server to which the MAPI program points is shut down
Keywords: kbcode kbhowto kbexpertiseadvanced kbinfo KB934877