Skip to content

Instantly share code, notes, and snippets.

@hinchley
Created March 25, 2016 04:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hinchley/ce2db8d1cec3fef87f61 to your computer and use it in GitHub Desktop.
Save hinchley/ce2db8d1cec3fef87f61 to your computer and use it in GitHub Desktop.
Create an Outlook 2016 MAPI Profile
#include "stdafx.h"
#include <windows.h>
#include <mapix.h>
#include <mapiutil.h>
#include <edkmdb.h>
#include <atlbase.h>
#include <iostream>
#include <vector>
#include <map>
#include <sstream>
using namespace std;
#define PR_PROFILE_USER_SMTP_EMAIL_ADDRESS PROP_TAG(PT_TSTRING, pidProfileMin+0x41)
#define PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W PROP_TAG(PT_UNICODE, pidProfileMin+0x41)
#define PR_EMSMDB_SECTION_UID PROP_TAG(PT_BINARY, 0x3D15)
#define MAPI_FORCE_ACCESS 0x80000
STDMETHODIMP GetGlobalProfileSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpMapiUid, ULONG ulFlags, LPPROFSECT * lppProfSect);
STDMETHODIMP GetEMSMDBVarProfileSection(LPSERVICEADMIN lpSvcAdmin, LPPROFSECT lpGlobalProfSection, LPPROFSECT * lppProfSect);
HRESULT CreateProfile(LPSTR szProfile, LPSTR szSmtpAddress, LPSTR szDisplayName, LPSTR szOstPath)
{
HRESULT hRes = S_OK; // Result from MAPI calls.
LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
vector<SPropValue> rgvals;
SRestriction sres; // Restriction structure.
SPropValue sprops; // Property structure for restriction.
LPPROFSECT lpGlobalProfSection = nullptr;
LPPROFSECT lpEmsMdbVarProfSect = nullptr;
SPropValue spvSmtpAddressW;
SPropValue spvDisplayNameW;
SPropValue spvOfflineStore;
USES_CONVERSION;
std::map<ULONG, LPWSTR> PropValueMap
{
{
PR_DISPLAY_NAME_W,
A2W(szDisplayName)
},
{
PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W,
A2W(szSmtpAddress)
}
};
// This indicates columns we want returned from HrQueryAllRows.
enum { iSvcName, iSvcUID, cptaSvc };
SizedSPropTagArray(cptaSvc, sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };
string MSEMS = "MSEMS";
// Initialize MAPI.
if (FAILED(hRes = MAPIInitialize(NULL)))
{
cout << "Error initializing MAPI.";
goto error;
}
// Get an IProfAdmin interface.
if (FAILED(hRes = MAPIAdminProfiles(
0, // Flags.
&lpProfAdmin))) // Pointer to new IProfAdmin.
{
cout << "Error getting IProfAdmin interface.";
goto error;
}
// Create a new profile.
if (FAILED(hRes = lpProfAdmin->CreateProfile(
(LPTSTR)szProfile, // Name of new profile.
nullptr, // Password for profile.
0, // Handle to parent window.
0))) // Flags.
{
cout << "Error creating profile.";
goto error;
}
// Get an IMsgServiceAdmin interface off of the IProfAdmin interface.
if (FAILED(hRes = lpProfAdmin->AdminServices(
(LPTSTR)szProfile, // Profile that we want to modify.
nullptr, // Password for that profile.
0, // Handle to parent window.
0, // Flags.
&lpSvcAdmin))) // Pointer to new IMsgServiceAdmin.
{
cout << "Error getting IMsgServiceAdmin interface.";
goto error;
}
// Create the new message service for Exchange.
if (FAILED(hRes = lpSvcAdmin->CreateMsgService(
(LPTSTR)"MSEMS", // Name of service from MAPISVC.INF.
NULL, // Display name of service.
NULL, // Handle to parent window.
NULL))) // Flags.
{
cout << "Error creating Exchange message service.";
goto error;
}
// You now have to obtain the entry id for the new service.
// You can do this by getting the message service table
// and getting the entry that corresponds to the new service.
if (FAILED(hRes = lpSvcAdmin->GetMsgServiceTable(
0, // Flags.
&lpMsgSvcTable))) // Pointer to table.
{
cout << "Error getting Message Service Table.";
goto error;
}
// Set up restriction to query table.
sres.rt = RES_CONTENT;
sres.res.resContent.ulFuzzyLevel = FL_FULLSTRING;
sres.res.resContent.ulPropTag = PR_SERVICE_NAME;
sres.res.resContent.lpProp = &sprops;
sprops.ulPropTag = PR_SERVICE_NAME;
sprops.Value.lpszW = L"MSEMS";
// Query the table to obtain the entry for the newly created message service.
if (FAILED(hRes = HrQueryAllRows(lpMsgSvcTable, (LPSPropTagArray)&sptCols, &sres, NULL, 0, &lpSvcRows)))
{
cout << "Error querying table for new message service.";
goto error;
}
ZeroMemory(&spvSmtpAddressW, sizeof(spvSmtpAddressW));
spvSmtpAddressW.ulPropTag = PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W;
spvSmtpAddressW.Value.lpszW = PropValueMap[PR_PROFILE_USER_SMTP_EMAIL_ADDRESS_W];
rgvals.push_back(spvSmtpAddressW);
ZeroMemory(&spvDisplayNameW, sizeof(spvDisplayNameW));
spvDisplayNameW.ulPropTag = PR_DISPLAY_NAME_W;
spvDisplayNameW.Value.lpszW = PropValueMap[PR_DISPLAY_NAME_W];
rgvals.push_back(spvDisplayNameW);
// Experimental.
ZeroMemory(&spvOfflineStore, sizeof(spvOfflineStore));
spvOfflineStore.ulPropTag = PR_PROFILE_OFFLINE_STORE_PATH;
spvOfflineStore.Value.lpszA = szOstPath;
rgvals.push_back(spvOfflineStore);
if (FAILED(hRes = GetGlobalProfileSection(
lpSvcAdmin,
(LPMAPIUID)lpSvcRows->aRow->lpProps[iSvcUID].Value.bin.lpb,
MAPI_MODIFY,
&lpGlobalProfSection)))
{
cout << "Error attempting to get the Global Profile Section.";
goto error;
}
if (FAILED(hRes = lpGlobalProfSection->SetProps(rgvals.size(), rgvals.data(), nullptr)))
{
cout << "Error attempting to set the SMTP address.";
goto error;
}
if (FAILED(hRes = lpGlobalProfSection->SaveChanges(KEEP_OPEN_READWRITE)))
{
cout << "Error attempting to save after setting the SMTP address.";
goto error;
}
if (FAILED(hRes = GetEMSMDBVarProfileSection(lpSvcAdmin, lpGlobalProfSection, &lpEmsMdbVarProfSect)))
{
goto error;
}
if (FAILED(hRes = lpEmsMdbVarProfSect->SetProps(rgvals.size(), rgvals.data(), nullptr)))
{
cout << "Error setting properties on the EMS MDB variable profile section using the SMTP address.";
goto error;
}
if (FAILED(hRes = lpEmsMdbVarProfSect->SaveChanges(KEEP_OPEN_READWRITE)))
{
cout << "Error attempting to save after setting the SMTP address on the EMS MDB variable profile section";
goto error;
}
cleanup:
if (lpEmsMdbVarProfSect) lpEmsMdbVarProfSect->Release();
if (lpGlobalProfSection) lpGlobalProfSection->Release();
if (lpSvcRows) FreeProws(lpSvcRows);
if (lpMsgSvcTable) lpMsgSvcTable->Release();
if (lpSvcAdmin) lpSvcAdmin->Release();
if (lpProfAdmin) lpProfAdmin->Release();
MAPIUninitialize();
return 0;
error:
cout << " hRes = 0x" << hex << hRes << dec << endl;
goto cleanup;
}
STDMETHODIMP GetGlobalProfileSection(LPSERVICEADMIN lpSvcAdmin, LPMAPIUID lpMapiUid, ULONG ulFlags, LPPROFSECT * lppProfSect)
{
HRESULT hRes = MAPI_E_CALL_FAILED;
LPPROFSECT lpProfSect = nullptr;
LPPROFSECT lpEmsMdbVarProfSect = nullptr;
ULONG cValues = 0;
LPSPropValue lpProps = nullptr;
SizedSPropTagArray(1, spta) = { 1,{ PR_EMSMDB_SECTION_UID } };
*lppProfSect = nullptr;
hRes = lpSvcAdmin->OpenProfileSection(lpMapiUid, 0, MAPI_FORCE_ACCESS, &lpProfSect);
if (FAILED(hRes) || lpProfSect == nullptr)
{
return hRes;
}
hRes = lpProfSect->GetProps((LPSPropTagArray)&spta, 0, &cValues, &lpProps);
if (FAILED(hRes) || lpProps == nullptr || cValues == 0)
{
return hRes;
}
if (lpProps[0].ulPropTag != PR_EMSMDB_SECTION_UID)
{
hRes = lpProps[0].Value.err;
goto cleanup;
}
hRes = lpSvcAdmin->OpenProfileSection((LPMAPIUID)lpProps->Value.bin.lpb, 0, ulFlags, &lpEmsMdbVarProfSect);
if (FAILED(hRes) || lpEmsMdbVarProfSect == nullptr)
{
goto cleanup;
}
*lppProfSect = lpEmsMdbVarProfSect;
cleanup:
if (lpProps)
{
MAPIFreeBuffer(lpProps);
}
if (lpProfSect)
{
lpProfSect->Release();
}
return hRes;
}
STDMETHODIMP GetEMSMDBVarProfileSection(LPSERVICEADMIN lpSvcAdmin, LPPROFSECT lpGlobalProfSection, LPPROFSECT * lppProfSect)
{
HRESULT hRes = MAPI_E_CALL_FAILED;
SizedSPropTagArray(1, sptaStoreProviders) = { 1,{ PR_STORE_PROVIDERS } };
ULONG cValues = 0;
LPSPropValue lpProps = nullptr;
LPPROFSECT lpProfSect = nullptr;
if (!lpSvcAdmin || !lpGlobalProfSection || !lppProfSect)
{
return E_INVALIDARG;
}
*lppProfSect = nullptr;
if (FAILED(hRes = lpGlobalProfSection->GetProps(
(LPSPropTagArray)&sptaStoreProviders,
0,
&cValues,
&lpProps)) || cValues == 0 || lpProps == nullptr)
{
cout << "Error attempting to get the PR_STORE_PROVIDERS property." << endl;
goto cleanup;
}
if (lpProps->ulPropTag != sptaStoreProviders.aulPropTag[0])
{
hRes = lpProps->Value.err;
goto cleanup;
}
if (FAILED(hRes = lpSvcAdmin->OpenProfileSection(
(LPMAPIUID)lpProps->Value.bin.lpb,
0,
MAPI_FORCE_ACCESS | MAPI_MODIFY,
&lpProfSect)) || lpProfSect == nullptr)
{
cout << "Could not open the profile section using the PR_STORE_PROVIDERS property." << endl;
goto cleanup;
}
*lppProfSect = lpProfSect;
cleanup:
if (lpProps)
{
MAPIFreeBuffer(lpProps);
lpProps = nullptr;
}
return hRes;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 4) {
cout << "Usage: CreateMapiProfile.exe <ProfileName> <email@address.com> <OST Path>" << endl;
return 0;
}
LPSTR arg1 = new CHAR[lstrlenW(argv[1]) + 1];
LPSTR arg2 = new CHAR[lstrlenW(argv[2]) + 1];
LPSTR arg3 = new CHAR[lstrlenW(argv[3]) + 1];
WideCharToMultiByte(CP_ACP, 0, argv[1], -1, arg1, lstrlenW(argv[1]) + 1, 0, 0);
WideCharToMultiByte(CP_ACP, 0, argv[2], -1, arg2, lstrlenW(argv[2]) + 1, 0, 0);
WideCharToMultiByte(CP_ACP, 0, argv[3], -1, arg3, lstrlenW(argv[3]) + 1, 0, 0);
CreateProfile(arg1, arg2, arg2, arg3);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment