This project has moved and is read-only. For the latest updates, please go here.

Will the Outlook 2010 MAPI Samples work with Outlook 2013?

May 27, 2014 at 7:14 AM
Hi,

I have used these samples to write an Address Book provider which worked in Outlook 2007 and 2010.

Now the client is upgrading to Outlook 2013. My Address Book Provider is listed under Address Books in the Outlook Account Settings however it does not appear to actually be loaded.

If I click the "Change" button I am supposed to get a dialog window provided by the Address Book dll. Instead I get an error message :

"This e-mail account cannot be configured. Run outlook and select this profile before attempting to make changes to the address book provider."

If I set breakpoints in the code and launch Outlook 2013 in the Visual Studio 2013 debugger, the breakpoints do not hit. There are no logging outputs either.

Will I be able to fix this and get the address book working again?

Kim Groves
May 27, 2014 at 7:34 AM
Hi,

Please disregard previous post.

What happened is that the Visual Studio Project for the Address Book Provider had a Post Build Event to copy the DLL and PDB file to the Outlook folder for debugging and testing. The path in the post build event script was incorrect as a result of upgrading from Outlook 2007 to Outlook 2013. So the DLL file was actually not available for MAPI to load per the settings in MAPISVC.Inf.

So, yes, this sample code will work with Outlook 2013.

Kim Groves
Mar 2, 2016 at 1:42 PM
I am working on the address book provider again. I am not sure about my last comment. The address book provider will not load in outlook 2013 or mfcmapi.

The dll file is not in the Mapisvc.inf file.

In the wrapped PST provider, the file install.bat calls the dll via rundll32 to add the service to the correct mapi profile.

However install.bat in sample address book provider uses a simple copy command to update the mapi profile. Different outlook versions are detected.

Is there any reason not to add the utils.cpp code from wrap PST to my address book project? So I can make use of Fgetcomponetpath() etc

Thanks,

Kim Anthony Groves
Apr 4, 2016 at 1:41 PM
Hi,

I have overlooked something crucial in implementing the MergeWithMAPISVC code from Wrap PST into my address book project.
I had this line in utils.h :

{ _T("S_ABP"), _T("PR_RESOURCE_TYPE"), 0L, _T("MAPI_STORE_PROVIDER")},

It should be :

{ _T("S_ABP"), _T("PR_RESOURCE_TYPE"), 0L, _T("MAPI_AB_PROVIDER")},

Seems obvious now but it took a while to figure out.
Apr 19, 2016 at 11:50 AM
I also like to uncomment the line
OutputDebugString(szMsg);
in the _Output() function if I want to check what is happening in MergeWithMAPISVC using Dbgview.exe

When you are testing your project in Visual Studio, you might like to include this Post Build Event in your project properties :
rundll32 $(TargetPath) MergeWithMAPISVC
Now when you build your project, the entries in MAPISVC.INF will refresh automatically. I realise that the MAPISVC.INF file will not normally change. This step can be very helpful if you are using SVN with multiple development machines. Now you don’t have to wonder if MAPISVC.INF is correct for the current project version. Building the project will tidy that up for you.
(Note: Outlook will look for your Provider DLL in your project Debug output folder when you use %targetpath%)
Please be careful if you are also testing a setup program with your Provider.
Once the provider has been installed in Outlook, Outlook will load the DLL file that was copied in from MAPISVC.INF.
If you use MergeWithMAPISVC and update the path to the DLL file, this is not automatically updated into the Outlook profile.
You should remove the Provider within Outlook (which requires an application restart) then when you re-add the provider, the updated path in MAPISVC.inf will be used. This is particularly important if you want the Visual Studio debugger to be triggered on breakpoints in the Provider code.
(These comments are based on personal experience so please forgive any incorrect phrases as I am an end-user of MAPI.)

I have also altered UTILS.H and UTILS.CPP so that the entries in MAPISVC.INF are automatically updated to the correct DLL shortened file path instead of using the hard coded path C:\AddrBook\ in the Wrapped PST Sample code. This ties into my other comments about calling MergeWithMAPISVC from the post-build event.

// UTILS.H
// These strings will be inserted into MAPISVC.INF exactly as specified here. Just add and remove entries as necessary
// %s will be expanded to the current short name path of the DLL that contains this code
// ie if you use rundll32 c:\addressbook\sabp32.dll MergeWithMAPISVC
// Then %s will be c:\addres~1\
static SERVICESINIREC aWrapPSTServicesIni[] =
{
{_T(“Services”), _T(“SABP”), 0L, _T(“Sample Contacts”)},

{ _T(“SABP”), _T(“Providers”), 0L, _T(“S_ABP”) },
{ _T(“SABP”), _T(“PR_DISPLAY_NAME”), 0L, _T(“Sample Contacts”) },
{_T(“SABP”), _T(“PR_SERVICE_DLL_NAME”), 0L, _T(“%ssabp.dll”)},
{_T(“SABP”), _T(“PR_SERVICE_ENTRY_NAME”), 0L, _T(“ServiceEntry”)},
{_T(“SABP”), _T(“PR_RESOURCE_FLAGS”), 0L, _T(“SERVICE_SINGLE_COPY | SERVICE_NO_PRIMARY_IDENTITY”)},
{_T(“SABP”), _T(“PR_SERVICE_SUPPORT_FILES”), 0L, _T(“%ssabp.dll”)},
{_T(“SABP”), _T(“PR_SERVICE_DELETE_FILES”), 0L, _T(“%ssabp.dll”)},
{_T(“SABP”), _T(“WIZARD_ENTRY_NAME”), 0L, _T(“WizardEntry”) },

{_T(“S_ABP”), _T(“PR_RESOURCE_TYPE”), 0L, _T(“MAPI_AB_PROVIDER”)},
{_T(“S_ABP”), _T(“PR_PROVIDER_DLL_NAME”), 0L, _T(“%ssabp.dll”)},
{_T(“S_ABP”), _T(“PR_DISPLAY_NAME”), 0L, _T(“Sample Contacts”)},
{_T(“S_ABP”), _T(“PR_PROVIDER_DISPLAY”), 0L, _T(“Sample Contacts”)},

{NULL, NULL, 0L, NULL}
};
// ..
// UTILS.CPP
// $–HrSetProfileParameters———————————————-
// Add values to MAPISVC.INF
// —————————————————————————–
STDMETHODIMP HrSetProfileParameters(SERVICESINIREC *lpServicesIni)
{
HRESULT hRes = S_OK;
TCHAR szSystemDir[MAX_PATH+1] = {0};
TCHAR szServicesIni[MAX_PATH+12] = {0}; // 12 = space for “MAPISVC.INF”
UINT n = 0;
TCHAR szPropNum[10] = {0};
TCHAR dllpath[MAX_PATH];
HMODULE hm = NULL;
TCHAR szDllPathValue[MAX_PATH + 1] = { 0 }; // Check for Key Values that need to be expanded to include the current DLL path
TCHAR dllpath_buffer[_MAX_PATH]; // Split the running DLL Path into components and recombine into dllpath after shortening
TCHAR dlldrive[_MAX_DRIVE];
TCHAR dlldir[_MAX_DIR];
TCHAR dllfname[_MAX_FNAME];
TCHAR dllext[_MAX_EXT];
errno_t err;
long length = 0;

Log(true,_T(“HrSetProfileParameters()\n”));

if (!lpServicesIni) return MAPI_E_INVALID_PARAMETER;

GetMAPISVCPath(szServicesIni,CCH(szServicesIni));

if (!szServicesIni[0])
{
UINT uiLen = 0;
uiLen = GetSystemDirectory(szSystemDir, CCH(szSystemDir));
if (!uiLen)
return MAPI_E_CALL_FAILED;

Log(true,_T(“Writing to this directory: \”%s\”\n”),szSystemDir);

hRes = StringCchPrintf(
szServicesIni,
CCH(szServicesIni),
_T(“%s\%s”),
szSystemDir,
_T(“MAPISVC.INF”));
}
Log(true,_T(“Writing to this file: \”%s\”\n”),szServicesIni);
// Get the Path to the DLL
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)&MergeWithMAPISVC,
&hm))
{
int ret = GetLastError();
Log(true, _T(“GetModuleHandle returned %d\n”), ret);
}
GetModuleFileNameW(hm, dllpath, sizeof(dllpath));
err = _wsplitpath_s(dllpath, dlldrive, _MAX_DRIVE, dlldir, _MAX_DIR, dllfname,
_MAX_FNAME, dllext, _MAX_EXT);
if (err != 0)
{
Log(true, _T(“Error splitting the path. Error code %d.\n”), err);
}
// Combine the drive and directory back into the path that we need
wsprintf(dllpath_buffer, _T(“%s%s”), dlldrive, dlldir);
// MAPISVC.INF requires the shortened 8.3 file path
length = GetShortPathNameW(dllpath_buffer, dllpath, MAX_PATH);
Log(true, _T(“DLL Path: \”%s\”\n”), dllpath);
//
// Loop through and add items to MAPISVC.INF
//
n = 0;

while(lpServicesIni[n].lpszSection != NULL)
{
LPTSTR lpszProp = lpServicesIni[n].lpszKey;
LPTSTR lpszValue = lpServicesIni[n].lpszValue;

// Switch the property if necessary

if ((lpszProp == NULL) && (lpServicesIni[n].ulKey != 0))
{

hRes = StringCchPrintf(
szPropNum,
CCH(szPropNum),
_T(“%lx”),
lpServicesIni[n].ulKey);

if (SUCCEEDED(hRes))
lpszProp = szPropNum;
}

// If the value needs to have the DLL path expanded, do so
hRes = StringCchPrintf(
szDllPathValue,
CCH(szDllPathValue),
lpszValue,
dllpath);

//
// Write the item to MAPISVC.INF
//

WritePrivateProfileString(
lpServicesIni[n].lpszSection,
lpszProp,
szDllPathValue,
szServicesIni);
Log(true, _T(“%s -> %s = %s\n”), lpServicesIni[n].lpszSection, lpszProp, szDllPathValue);
n++;
}

// Flush the information – ignore the return code
WritePrivateProfileString(NULL, NULL, NULL, szServicesIni);

return hRes;
}
Apr 19, 2016 at 12:29 PM
GetShortPathNameW can only return a Short File Name if one has been stored for the file in question. Otherwise the long filename will be returned.
It is possible for Short File name generation to be turned off either in the system registry or on a volume by volume basis.
From command prompt, run fsutil 8dot3name query or fsutil 8dot3name query c: to confirm if short file names are on or off on the volume which is hosting the MAPI Provider.
Also use dir /x to confirm that short file names are stored for the DLL file of your provider.
If the provider is installed on the c: drive, short file names are probably on by default. Other drives are probably off by default.
As long as the provider is installed on the c: drive it should work with MAPI.
This may change in the future.
Your software installer may need to set the short file name of the provider dll ie.
fsutil file setshortname MAPIProvider.dll mapipr~1.dll
or similar.
This information was based from
https://blogs.msdn.microsoft.com/winsdk/2013/10/09/getshortpathname-doesnt-return-short-path-name/