RDOFolderSynchronizer object |
RDOFolderSynchronizer allows to synchronize Exchange folder contents (regular or hidden messages) and subfolders. This object is not available for the stores other than Exchange (PST, IMAP, etc).
RDOFolderSynchronizer object wraps the ICS (Incremental Change Synchronization) API in MAPI (IExchangeImportContentsChanges, IExchangeImportHierarchyChanges, IExchangeExportChanges, etc). This is the same API used by the Exchange cached store provider to synchronize its folders with an Exchange Server.
Returned by:
RDFolder2.ExchangeSynchronizer
ICS API is the only supported method of synchronizing Exchange folders. You can use folder events (RDOItems.ItemChange / ItemAdd / ItemRemove), but that requires the code to be running at all times; plus events can be dropped under heavy loads. Also, if you are monitoring multiple folders, you can easily run out of the 255 RPC channels/process limit imposed by the Exchange Server.
ICS API does not require persistent connection to the folder, you can synchronize a folder at any moment, be that every minute or once a month.
To synchronize messages in a given folder (RDFolder2 object) follow the steps below:
Retrieve RDOFolderSynchronizer object by calling RDFolder2.ExchangeSynchronizer
Call RDOFolderSynchronizer.SyncItems method (or SyncHiddenItems in case of hidden messages or SyncFolders in case of subfolders) passing the value of the RDOSyncMessagesCollection.SyncData property from the previous sync cycle. When running for the very first time, pass an empty string; in this case Exchange will return all existing items in the folder in the RDOSyncMessagesCollection collection. The value of the SyncData property must be persisted on the per-folder basis, i.e. you cannot use the value returned by synching a different folder. This value can thought of as a cookie that Exchange will use next time to synchronize the folder.
Loop through the items in the returned RDOSyncMessagesCollection (or RDOSyncFoldersCollection if calling SyncFolders). If you need to identify messages in the folder, it is better to use the RDOSyncMessageItem.SourceKey property (corresponds to the PR_SOURCE_KEY property in MAPI): the value of the entry id is not guaranteed to remain the same between sessions, while source key stays the same for any given message or folder. If you know the value of the source key, you can retrieve the corresponding message using RDOExchangeStore.GetMessageFromSourceKey method.
Save the value of the RDOSyncMessagesCollection.SyncData property for use next time (at step 2).
If your code modifies Outlook items, and you need to filter out and disregard changes made by your code, compare the value of the PR_CHANGE_KEY MAPI property (you can access it using RDOMail.Fields() and convert to a hex string using MAPIUtils.HrArrayToString) retrieved immediately after saving the changed item (RDOMail.Save) with the value of the corresponding (match by the PR_SOURCE_KEY property) RDOSyncMessageItem.ChangeKey property retrieved later. If the values are the same, your code was the last to modify the item, if it is different, the item was modified at a later time after you called RDOMail.Save.
The example below synchronizes the contents of the default Inbox folder. On the first run it will report all items in the folder as changed (since the sync never ran before), displays a message boxes to wait to the user to modify something in the folder (new message, modify an existing message, delete a message), then performs the sync again and reports the items that were added/modified/deleted while the prompt was displayed.
MAPI_NO_CACHE = &H200
' Optional SQL restriction:
' in most case no restriction is used ' retrieve the
folder to be synchronized ' otherwise
Redemption will attempt to reopen the online version of the folder
' this is just an example... ' since we are using strPreviousSyncData from the sync above, only messages added/modified/deleted while the message box
' above was displayed will be returned if SyncItems.Count = 0 Then Debug.Print "There were no changes in the folder" Else
Debug.Print "There were " & SyncItems.Count & " changes in the folder. The
list of changes follows:" if Item.IsNewMessage Then Debug.Print "New: " & Item.Item.Subject
Else
End If EndIf
' remember the value of the strPreviousSyncData property, we will need it next time strPreviousSyncData = SyncItems.SyncData ' strPreviousSyncData now needs to be persisted to be used in the next sync instead of using an empty string |
Derived from: IDispatch |
|||
|
|||
Methods |
|||
|
|||
SyncItems(PreviousSyncData, SQLRestriction) |
Synchronizes the folder contents (messages); returns an instance of the RDOSyncMessagesCollection object.
PreviousSyncData - the value returned by RDOSyncMessagesCollection.SyncData property during the previous sync cycle. For the very first sync, pass an empty string.
SQLRestriction - optional, string. An optional SQL restriction (e.g. " MessageClass = 'IPM.Post' "). The restriction must never change on subsequent syncs, or all items in the folder will be returned
|
see example above |
|
SyncHiddenItems(PreviousSyncData, SQLRestriction) |
Synchronizes the folder associated contents (hidden messages); returns an instance of the RDOSyncMessagesCollection object.
PreviousSyncData - the value returned by RDOSyncMessagesCollection.SyncData property during the previous sync cycle. For the very first sync, pass an empty string.
SQLRestriction - optional, string. An optional SQL restriction. The restriction must never change on subsequent syncs, or all items in the folder will be returned
|
||
SyncFolders(PreviousSyncData, SQLRestriction) |
Synchronizes the subfolders; returns an instance of the RDOSyncFoldersCollection object.
PreviousSyncData - the value returned by RDOSyncFoldersCollection.SyncData property during the previous sync cycle. For the very first sync, pass an empty string.
SQLRestriction - optional, string. An optional SQL restriction. The restriction must never change on subsequent syncs, or all subfolders in the folder will be returned
|
||
|
|||
Events |
|||
|
|||
OnProgress(Step, Progress, ByRef Cancel) |
Occurs every time Redemption calls IExchangeExportChanges::Synchronize() MAPI method.
Step - integer Progress - integer Cancel (by reference) - boolean, set this parameter to TRUE to cancel the sync process. |
|
|
OnSyncFolder(Folder, ByRef Cancel)
|
Occurs each time ICS sends a subfolder change/delete notification.
Folder - RDOSyncFolderItem object.
Cancel (by reference) - boolean, set this parameter to TRUE to cancel the sync process.
|
|
|
OnSyncHiddenItem(Item, RyRef TargetMessage, ByRef Cancel)
|
Occurs each time ICS sends a hidden message add/change/delete notification.
Item - RDOSyncMessageItem object representing added/changed/deleted message in the folder.
TargetMessage (by reference) - RDOMail object. Redemption passes NULL to the event handler. You optionally can return a message from your callback; in this case ICS will populate it with the changed properties from the source message (see an example below)
Cancel (by reference) - boolean, set this parameter to TRUE to cancel the sync process.
|
|
|
OnSyncItem(Item, RyRef TargetMessage, ByRef Cancel)
|
Occurs each time ICS sends a message add/change/delete notification.
Item - RDOSyncMessageItem object representing added/changed/deleted message in the folder.
TargetMessage (by reference) - RDOMail object. Redemption passes NULL to the event handler. You optionally can return a message from your callback; in this case ICS will populate it with the changed properties from the source message (see an example below)
Cancel (by reference) - boolean, set this parameter to TRUE to cancel the sync process.
|
private RDOSession session = null; private RDOFolderSynchronizer synchronizer = null; private string strSyncData = "";
private void button1_Click(object sender, EventArgs e) { if (session == null) { session = new RDOSession(); session.Logon("Test", "", false, true, 0, false); } RDOFolderSynchronizer synchronizer = ((RDOFolder2)session.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts)).ExchangeSynchonizer; synchronizer.OnSyncItem += new IRDOFolderSynchronizerEvents_OnSyncItemEventHandler(synchronizer_OnSyncItem); RDOSyncMessagesCollection items = synchronizer.SyncItems(strSyncData, ""); strSyncData = items.SyncData; } void synchronizer_OnSyncItem(RDOSyncMessageItem Item, out RDOMail TargetMessage, ref bool Cancel) { //Item parameter if (Item.Kind != rdoSyncItemKind.sikDeleted) { //just a simple example of a user notification. //We check above for RDOSyncMessageItem.Kind != rdoSyncItemKind.sikDeleted //since for the deleted messages we cannot access the RDOSyncMessageItem.Item //property and retrive any message properties MessageBox.Show(Item.Item.Subject); } //TargetMessage parameter - if this parameter is set to an RDOMail object by the event handler, //ICS will populate the message properties. //This is optional, you can simply do nothing. //This parameter is only valid for the sikChanged notifications, other notification //kinds (sikDeleted and sikReadStatusChanged) are easy to handle explicitly TargetMessage = null; //we may set it below, it is an optional stepif (Item.Kind == rdoSyncItemKind.sikChanged) { //this check is not really necessary, you can return a new/existing message //We do this just for the simplicity sake so that we won't have to search for an existing message if (Item.IsNewMessage) { //lets create a new message in the Deleted Items folder and let Exchange populate its properties //this is optional and not very real-life like, but why not? TargetMessage = session.GetDefaultFolder( rdoDefaultFolders.olFolderDeletedItems).Items.Add("IPM.Note");} } //Cancel parameter //this is a good place to display a progress UI and allow the user to cancel //the sync by setting the Cancel parameter to true //Note that RDOFolderSynchronizer.SyncItems/SyncHiddenItems will return a //MAPI_E_USER_CANCEL error if the sync is canceled. //Cancel = true; }
|
|
|