[This is now documented here: http://msdn.microsoft.com/en-us/library/bb821132.aspx]
[This sample is now part of the Outlook MAPI Code Samples!]
In the Outlook 2003 Integration API, we documented the Replication API. This is a nifty API for implementing replication between a wrapped PST and your own custom back end, which should be a great way to simplify implementation of a MAPI Message Store Provider without incurring all the pain involved in writing one from scratch. Unfortunately, we neglected to document how to create a wrapped PST. This documentation (which should eventually be part of the MSDN) corrects this oversight. Much thanks to Fergus Wilson of Meridio for his assistance in testing this sample.
Please post back any problems you encounter with this sample or the docs so I can correct them. Thanks!
The sample code can be downloaded here: http://stephengriffin.members.winisp.net/wrappst/wrappst.zip
Wrapping the PST provider in preparation for using the Replication API The Replication API can only be used in conjunction with a Wrapped PST provider. This document defines a Wrapped PST provider and gives instructions for constructing one.
Wrapped PST Provider: A wrapped PST provider is a custom MAPI message store provider which uses the PST provider as the back end for storing data. Most functions in a basic wrapped PST provider will proxy their arguments directly to the underlying PST provider. This allows the developer doing the wrapping to focus on the portions of the provider specific to their business needs without needing to invest time in writing a store provider from scratch.
Some functions and classes do need special logic in them to inform the PST provider that it has been wrapped so the Replication API can function. This document outlines what needs to be done in these functions.
The remaining code in a wrapped PST can be used to change default behavior, such as supporting additional properties not native to a PST by overriding GetProps, or allowing OpenEntry to do special processing on a message before handing it back to the client.
Constants: The following constants are used in this documentation. Note that some changes and clarifications have been made to the constant definitions given in the Replication API.
#define MDB_OST_LOGON_UNICODE ((ULONG) 0x00000800) #define MDB_OST_LOGON_ANSI ((ULONG) 0x00001000) // OffLineFileInfo typedef struct { ULONG ulVersion; // Structure version MAPIUID muidReserved; ULONG ulReserved; DWORD dwAlloc; // Number of primary source keys DWORD dwReserved; LTID ltidReserved1; LTID ltidReserved2; } OLFI; #define PR_OST_OLFI PROP_TAG(PT_BINARY, 0x7C07) #define OLFI_VERSION 0x01 // UID of an NST provider const MAPIUID g_muidProvPrvNST = { 0xE9, 0x2F, 0xEB, 0x75, 0x96, 0x50, 0x44, 0x86, 0x83, 0xB8, 0x7D, 0xE5, 0x22, 0xAA, 0x49, 0x48 }; // This is a clarification of the definitions of PXIHC and PXICC // mentioned in the Replication API DECLARE_MAPI_INTERFACE_PTR(IExchangeImportHierarchyChanges,PXIHC); DECLARE_MAPI_INTERFACE_PTR(IExchangeImportContentsChanges,PXICC); // It is essential that the FEID, MEID and SKEY structures // be wrapped in this pack pragma or they will be the wrong size #pragma pack(1) struct FEID { BYTE abFlags[4]; MAPIUID muid; WORD placeholder; LTID ltid; }; struct MEID { BYTE abFlags[4]; MAPIUID muid; WORD placeholder; LTID ltidFld; LTID ltidMsg; }; struct SKEY { GUID guid; BYTE globcnt[6]; }; #pragma pack()
MSProviderInit This is the entry point when your provider is being loaded by Outlook. Any message store provider needs to implement this function and expose it as an entry point in the store provider’s DLL. This is a well known function name which does not change.
Implementation:
ServiceEntry This is the entry point when your provider is being configured, such as through the Mail Control Panel. The prototype for this function is MSGSERVICEENTRY, but the name to be used is not mandated. Instead, it is advertised in the PR_SERVICE_ENTRY_NAME property of your profile. In this documentation, we use the name ServiceEntry. Be sure to expose the function as an entry point in the store provider’s DLL.
CMSProvider This is the wrapped implementation of IMSProvider. It is required to wrap this interface to provide some special processing needed to make the wrapped PST work.
Member functions not documented here can just pass their parameters into the underlying wrapped object. The IUnknown functions are implemented normally, making sure that the reference count on the underlying object is maintained.
The constructor should accept the LPMSPROVIDER object being wrapped.
The following functions need special handling: IMSProvider::Logon
IMSProvider::SpoolerLogon
CSupport This is the wrapped implementation of IMAPISupport. It is required to wrap this interface to provide some special processing needed to make the wrapped PST work.
The constructor should accept the LPMAPISUP object being wrapped and a LPPROFSECT object which will be used in OpenProfileSection.
The following functions needs special handling: IMAPISupport::OpenProfileSection
When pbNSTGlobalProfileSectionGuid is requested, return the profile section which has been cached in your implementation.
IMAPISupport::ModifyStatusRow
The PST provider will attempt to set a status row with PR_IDENTITY_DISPLAY, PR_IDENTITY_ENTRYID, and PR_IDENTITY_SEARCH_KEY. These values will be based on the user specified in PR_PROFILE_USER and assume the user has an EX address type. If this status row is allowed, then later calls to QueryIdentity can fail if no transport can handle this address type. This failure can in turn prevent creation of some items like contacts and appointments. Simply clearing these values will fix this problem. A more advanced fix would be to set a custom status row. Even more advanced would be to wrap the IMSLogon object returned in IMSProvider::Logon to implement a custom or wrapped status entry.
Using the Replication API The documentation of the Replication State Machine describes how to use the API to perform synchronization between an external store and the wrapped PST. See the functions DoSync, SyncDownloadFolders and SyncUploadFolders for sample code illustrating the API.
[10/6/05 - 10:39 AM] Clarified IMSProvider::Logon
[10/17/05 - 5:45PM] Added IMAPISupport::ModifyStatusRow, updated sample code
[11/11/05] Fixed sample URL
Hello, I didnt understand how to load this wrappst ? Ive compiled the source and when I start outlook I can see wrappst icon. But is there a need to create a new profile with a new service ? thx.
It's a MAPI store provider, so yes, it will need to be loaded in a profile. You can load it in a new profile or add it to an existing one.
Can I use this to create a msgStore using a local (well on mapped drive) mdb (access database) so as to create a shared pst (openable by many uses).
Thanks.
MrC
No - I don't think that would work. A PST can only be accessed by one user.
Hi! I wrote a program to deal with my messagebox (Exchange 2003 is in use). But there's a small problem - after a some time it crashes with error 0x8004010E - MAPI_E_NOT_ENOUGH_RESOURCES. I've looked through my code and found no mistakes. So it looks like program uses more and more memory each time I open my mailbox. I have no idea about the reason of such a strange behavior... :(
P.S. All handles are closed and I used Release() and FreeProws() methods to free objects.
Thanks in advance.
P@thfinder - I don't see what your question has to do with the wrapped PST sample. You should take your question to the MAPI mailing list: http://peach.ease.lsoft.com/archives/mapi-l.html