That is entry point of profile provider.
This provider is like any other provider in MAPI.
HRESULT STDMAPIINITCALLTYPE PRProviderInit(
HINSTANCE hInstance, // GetModuleHandle("MAPI32.DLL")
LPMALLOC lpMalloc, // MAPIGetDefaultMalloc()
LPALLOCATEBUFFER lpAllocateBuffer, // &MAPIAllocateBuffer
LPALLOCATEMORE lpAllocateMore, // &MAPIAllocateMore
LPFREEBUFFER lpFreeBuffer, // &MAPIFreeBuffer
ULONG ulFlags, // 0, MAPI_USE_GIVEN_PROFILE, MAPI_NT_SERVICE, MAPI_USE_DEFAULT
ULONG ulMAPIVer, // CURRENT_SPI_VERSION = 0x10010
ULONG *lpulProviderVer,
LPIPRPROVIDER *lppPRProvider); // [in] HKEY if MAPI_USE_GIVEN_PROFILE set.
// [out] IPRProvider
How that can be useful ?
We can write our custom provider to store profiles anywhere we want. We can store profiles in Active Directory or anoter databases such MS SQL/Oracle etc. So if user logons from another computer inside domain it can always get the same Outlook configuration. I know that it not so easy for store providers but we can just try to solve that.
Another case using exchange provider from inside NT Service (IIS). Since default provider stores it’s data in the registry the service should be started under some user account or user should be logged on before initializing MAPI subsystem. Exchange MAPI implementation and Outlook’s one are different but both using the same approach. Since Microsoft dropped support of profiles based on temporary files in Outlook 2003 we can always make another one :).
Profile Provider does:
So that is main Profile provider interface:
DEFINE_OLEGUID(IID_IPRProvider, 0x000203F6, 0, 0);
DECLARE_MAPI_INTERFACE_PTR(IPRProvider, LPIPRPROVIDER);
#define MAPI_IPRPROVIDER_METHODS(IPURE) \
MAPIMETHOD(GetLastError) \
(THIS_ HRESULT hResult, \
ULONG ulFlags, \
LPMAPIERROR FAR * lppMAPIError) IPURE; \
MAPIMETHOD(Shutdown) \
(THIS_ ULONG FAR * lpulFlags) IPURE; \
MAPIMETHOD(OpenProfile) \
(THIS_ LPMAPISUP lpMAPISupport, \
LPSTR FAR * lpszProfileName, \
LPSTR lpszPassword, \
ULONG ulFlags, \
ULONG_PTR ulUIParam, \
ULONG FAR * lpScrambleSecurityIDSize, \
LPVOID FAR * lppScrambleSecurityID, \
ULONG* p8_var_10, \
LPMAPIPROFILE FAR * lppMAPIProfile) IPURE; \
MAPIMETHOD(CreateProfile) \
(THIS_ LPSTR lpszProfileName, \
LPSTR lpszPassword, \
ULONG ulFlags) IPURE; \
MAPIMETHOD(DeleteProfile) \
(THIS_ LPSTR lpszProfileName, \
ULONG ulFlags) IPURE; \
MAPIMETHOD(ChangeProfilePassword) \
(THIS_ LPSTR lpszProfileName, \
LPSTR lpszOldPassword, \
LPSTR lpszNewPassword, \
ULONG ulFlags) IPURE; \
MAPIMETHOD(GetProfileTable) \
(THIS_ ULONG ulFlags, \
LPMAPITABLE FAR * lppTable) IPURE; \
MAPIMETHOD(CopyProfile) \
(THIS_ LPSTR lpszOldProfileName, \
LPSTR lpszOldPassword, \
LPSTR lpszNewProfileName, \
ULONG_PTR ulUIParam, \
ULONG ulFlags) IPURE; \
MAPIMETHOD(OpenProperty) \
(THIS_ ULONG ulPropTag, \
LPCIID lpiid, \
ULONG ulInterfaceOptions, \
ULONG ulFlags, \
LPUNKNOWN FAR * lppUnk) IPURE; \
MAPIMETHOD(SetDefaultProfile) \
(THIS_ LPSTR lpszProfileName, \
ULONG ulFlags) IPURE; \
MAPIMETHOD(ListDeferredDeletes) \
(THIS_ ULONG ulFlags, \
LPSTR FAR * lppszProfilesName) IPURE; \
#undef INTERFACE
#define INTERFACE IPRProvider
DECLARE_MAPI_INTERFACE_(IPRProvider, IUnknown)
{
BEGIN_INTERFACE
MAPI_IUNKNOWN_METHODS(PURE)
MAPI_IPRPROVIDER_METHODS(PURE)
};
OpenProfile method is very similar to IMSProvider::Logon on success it returns pointer to the profile interface.
It’s interface is easy enough:
DEFINE_OLEGUID(IID_IMAPIProfile, 0x000203F7, 0, 0);
DECLARE_MAPI_INTERFACE_PTR(IMAPIProfile, LPMAPIPROFILE);
#define MAPI_IMAPIPROFILE_METHODS(IPURE) \
MAPIMETHOD(OpenSection) \
(THIS_ LPMAPIUID lpUID, \
ULONG ulFlags, \
LPPROFSECT FAR * lppProfSect) IPURE; \
MAPIMETHOD(DeleteSection) \
(THIS_ LPMAPIUID lpUID) IPURE; \
#undef INTERFACE
#define INTERFACE IMAPIProfile
DECLARE_MAPI_INTERFACE_(IMAPIProfile, IUnknown)
{
BEGIN_INTERFACE
MAPI_IUNKNOWN_METHODS(PURE)
MAPI_IMAPIPROFILE_METHODS(PURE)
};
To register custom profile provider we need to add a section to the well-known win.ini file that reside in the windows directory.
;That is registration of custom profile manager
[MAPI]
Profile DLL=D:\work\ProfileManager\debug\ProfileManager.dll
Easy enough ? Not so strainforward :). Since Outlook XP MAPI checks process name and if it is “OUTLOOK.EXE” it always use standard profile provider. So just copy “OUTLOOK.EXE” to “MSOUTLOOK.EXE” and custom provider will be loaded if it exist.
Also we can use these interface to obtain restricted profile sections. Restricted profile sections - that is provider section and by default MAPI allows access only from within provider context. If you try to open such sections via IMAPISession::OpenProfileSection or IMsgServiceAdmin::OpenProfileSection or IProviderAdmin::OpenProfileSection you will get MAPI_E_NO_ACCESS. Microsoft solved that problem since Outlook XP. There is special flag MAPI_FORCE_ACCESS to pass to OpenProfileSection. For Outlook 2000 you need to use some hacking method. See that article http://support.microsoft.com/?kbid=228736 for details. Using above interfaces you can get access to any section under all versions of Outlook & Exchange.
From: http://support.microsoft.com/?kbid=228736
File: Profiles.cpp
...
///////////////////////////////////////////////////////////////////
// HACK CENTRAL. This is a MAJOR hack. MAPI will always return E_ACCESSDENIED
// when we open a profile section on the service if we are a client. The workaround
// (HACK) is to call into one of MAPI's internal functions that bypasses
// the security check. We build a Interface to it and then point to it from our
// offset of 0x48. USE AT YOUR OWN RISK! NOT SUPPORTED!
interface IOpenSectionHack : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE OpenSection(LPMAPIUID, ULONG, LPPROFSECT*) = 0;
};
IOpenSectionHack** ppProfile = (IOpenSectionHack**)((((BYTE*)pServices) + 0x48));
...
In that snippet IOpenSectionHack interface is actualy IMAPIProfile interface.
Code snipped below shows how to open profile section using above interfaces PRProviderInit function
HRESULT OpenProfileSection(
LPCSTR ProfileName, //NULL for obtaining default profile
LPMAPIUID lpUID, //section UID
ULONG ulFlags, //access flags (MAPI_MODIFY,MAPI_CREATE)
IProfSect** lppPrfSect) //pointer to the section
{
LPIPRPROVIDER profile = NULL;
LPMAPIPROFILE mapiprofile = NULL;
HRESULT hResult;
//initialize if we return nothing
*lppPrfSect = NULL;
ULONG version = 0;
hResult = PRProviderInit(
GetModuleHandleW(L"MAPI32.DLL"),
MAPIGetDefaultMalloc(),
&MAPIAllocateBuffer,
&MAPIAllocateMore,
&MAPIFreeBuffer,
0,
CURRENT_SPI_VERSION,
&version,
&profile);
if(SUCCEEDED(hResult))
{
ULONG lpDummy = 0;
ULONG ScrambleSecurityIDSize = 0;
LPVOID ScrambleSecurityID = NULL;
hResult = profile->OpenProfile(
(LPMAPISUP)profile,
(LPSTR*)&ProfileName,
"",
MAPI_USE_DEFAULT,
NULL,
&ScrambleSecurityIDSize,
&ScrambleSecurityID,
&lpDummy,
&mapiprofile);
if(SUCCEEDED(hResult))
{
hResult = mapiprofile->OpenSection(lpUID, ulFlags, lppPrfSect);
mapiprofile->Release();
} // if
profile->Release();
} // if
return hResult;
}
And small piece of code how to use it
MAPIUID uidInternal =
{
0x92, 0x07, 0xf3, 0xe0,
0xa3, 0xb1, 0x10, 0x19,
0x90, 0x8b, 0x08, 0x00,
0x2b, 0x2a, 0x56, 0xc2
};
LPPROFSECT pProfSect = NULL;
hResult = OpenProfileSection(
NULL, //default profile
&uidInternal,
MAPI_MODIFY,
&pProfSect);
if(SUCCEEDED(hResult))
{
pProfSect->Release();
} // if
To make life easier :) there is a working sample of custom profile provider. It was successfuly tested under some version of Outlook against Personal Folders provider and Exchange provider.
Some words about this sample:
Profiles are stored in the C:\Profiles directory as Windows ini files. File that contains default profile has SYSTEM attribute.
Properties are stored as PropTag=Hex Value. See some snippet below
[8503020000000000C000000000000046] //Section UID
300B0102=1A854565A0D15A4D82590A941218B0CA //Property definition
[65675FC40FEF3748A49C3D4A109486B9]
3D0A001E=6D737073742E646C6C00
3D13001E=7B36343835443236322D433241432D313144312D414433452D3130413043393131433943307D00
3D0F101E=6D737073742E646C6C00
3D0B001E=50535453657276696365456E74727900
30090003=20000000
3D09001E=4D53505354204D5300
3001001E=506572736F6E616C20466F6C6465727300
3D000102=31CBD185ED83B14DB041753168D4A528
Sample implements all property types including multivalue properties.
Important files:
MAPIinternals.h: contains interfaces definitions
ProfileStore.cpp: contains code that reads and writes from/to ini file
ProfSect.cpp: implements IProfSect interface using MAPI based IPropData implementation
PRProvider.cpp: contains IPRProvider implementation.
MAPIProfile.cpp: contains IMAPIProfile implementation.
ProfileManager.cpp: provider entry point
What else should be done for that sample:
Authors:
Sergey Golovkin (MAPI33.NET Author)
www.mapi33.adexsolutions.com
Henry Gusakovsky (MAPISpy Author)
www.mapispy.com