Andrei Smolin

How to solve synchronization conflict when modifying Outlook item in ItemAdd event

Environment. I see this issue in the Sent Items folder on an Exchange account having Cached Exchange Mode (see e.g. here) turned on. Reportedly, turning the Cached Mode off solves the issue. The Outlook version used (Outlook 365 in my case) seems to be irrelevant as Google reports a similar conflict to occur in Outlook 2007.

Issue. In the ItemSend event of the Sent Items folder, your code adds a UserProperty to the Outlook item that the event handler supplies (and saves the item). To detect the issue easily, I suggest that your code also change the MailItem.Category property before saving the item. The issue shows itself as follows: after the add-in handles the event, the category is visible in the Explorer view for several seconds; then the category mark as well as the UserProperty disappear. During that time, mail items are generated in Sync Issues and Sync Issues | Conflicts folders.

Solution. Do not use the ItemAdd event to modify the item in this case. Instead, add the UserProperty while sending the email – in the ItemSend event. This solution may be a limited one; see below.

Background

Adding a UserProperty to an Outlook item in the ItemAdd event is a usual practice. At docs.microsoft.com, Microsoft demonstrates doing this in the Contacts folder. Getting any issue in that event isn’t expected.

COM objects should be released

Outlook is known for a great number of misbehavior scenarios starting with your code leaving a COM object unreleased. That means that the first step to solving any issue with your code using the Outlook Object Model (OOM) is to make sure you release all COM objects your code creates. I wrote several times on this:

  • If a call to an Outlook (actually, any Office application!) property/method returns an object, that object is a COM object and you should release it.
  • Don’t chain COM calls such as {Application reference}.ActiveExplorer().Selection.Item(1).Subject. Instead, split that code line so that every COM call in this chain returns a value to a separate variable, use the variables and release them as soon as possible.
  • Don’t use foreach loops on COM collections; use for loops instead.
  • COM objects received in events should be released; Add-in Express developers must not do this because Add-in Express manages these COM objects for them.

Check for background information on how .NET manages COM objects in Why doesn't Excel quit.

Collecting information

I’ve decided to study the additions and changes to the items in system folders. Add-in Express provides a handy class to let you process events of the Outlook.Items collection. Here’s how I create instances of that class:

OutlookItemsEventsClass1 eventsConflicts;
OutlookItemsEventsClass1 eventsDeletedItems;	
OutlookItemsEventsClass1 eventsLocalFailures;	
OutlookItemsEventsClass1 eventsSentMail;	
OutlookItemsEventsClass1 eventsServerFailures;	
OutlookItemsEventsClass1 eventsSyncIssues;	
private void AddinModule_AddinInitialize(object sender, EventArgs e)	
{	
	eventsConflicts = new OutlookItemsEventsClass1(this);	
	eventsConflicts.ConnectTo(ADXOlDefaultFolders.olFolderConflicts, true);
 
	eventsDeletedItems = new OutlookItemsEventsClass1(this);	
	eventsDeletedItems.ConnectTo(ADXOlDefaultFolders.olFolderDeletedItems, true);
 
	eventsLocalFailures = new OutlookItemsEventsClass1(this);	
	eventsLocalFailures.ConnectTo(ADXOlDefaultFolders.olFolderLocalFailures, true);
 
	eventsSentMail = new OutlookItemsEventsClass1(this);	
	eventsSentMail.ConnectTo(ADXOlDefaultFolders.olFolderSentMail, true);
 
	eventsServerFailures = new OutlookItemsEventsClass1(this);	
	eventsServerFailures.ConnectTo(ADXOlDefaultFolders.olFolderServerFailures, true);
 
	eventsSyncIssues = new OutlookItemsEventsClass1(this);	
	eventsSyncIssues.ConnectTo(ADXOlDefaultFolders.olFolderSyncIssues, true);	
}

In methods handling the ItemAdd and ItemChange events (not shown here), I only write debug messages (see below).  When examining the log that I got, pay attention to the time, event, folder, subject, the number of conflicts, the number of UserProperty objects.

5:43:22 PM --- ItemChange in Sent Items
5:43:22 PM Subject="Subject string"
5:43:22 PM Conflicts.Count=0
5:43:22 PM Categories="Category string"
5:43:22 PM UserProperties.Count=1
5:43:22 PM ---
5:43:23 PM --- ItemAdd in Sync Issues\Conflicts
5:43:23 PM Subject="Subject string"
5:43:23 PM Conflicts.Count=1
5:43:23 PM Categories="Category string"
5:43:23 PM UserProperties.Count=1
5:43:23 PM ---
5:43:23 PM --- ItemChange in Sent Items
5:43:23 PM Subject="Subject string"
5:43:23 PM Conflicts.Count=0
5:43:23 PM Categories="Category string"
5:43:23 PM UserProperties.Count=1
5:43:23 PM ---
5:43:23 PM --- ItemChange in Sent Items
5:43:23 PM Subject="Subject string"
5:43:23 PM Conflicts.Count=0
5:43:23 PM Categories="Category string"
5:43:23 PM UserProperties.Count=1
5:43:23 PM ---
5:43:24 PM --- ItemChange in Sync Issues\Conflicts
5:43:24 PM Subject="Subject string"
5:43:24 PM Conflicts.Count=1
5:43:24 PM Categories="Category string"
5:43:24 PM UserProperties.Count=1
5:43:24 PM ----
5:43:24 PM ---- ItemAdd in Sync Issues
5:43:24 PM Subject="Modification Resolution"
5:43:24 PM Conflicts.Count=0
5:43:24 PM Categories=
5:43:24 PM UserProperties.Count=0
5:43:24 PM ---
5:43:24 PM --- ItemChange in Sent Items
5:43:24 PM Subject="Subject string"
5:43:24 PM Conflicts.Count=0
5:43:24 PM Categories="Category string"
5:43:24 PM UserProperties.Count=1
5:43:24 PM ---
5:43:37 PM --- ItemChange in Sent Items
5:43:37 PM Subject="Subject string"
5:43:37 PM Conflicts.Count=0
5:43:37 PM Categories="Category string"
5:43:37 PM UserProperties.Count=1
5:43:37 PM ---

The first thing you’ll notice is the number of ItemChange events. My test add-in directly caused only the first one: it occurred when the test add-in saved the modified item. All other events are produced by Exchange; you can find an explanation on the Outlook for Developers forum.

After studying Conflicts.Count, you’ll find that, according to the log above, the item is left in this state: there’re no conflicts, the category string is there, the UserProperty is there, too. This contradicts to what you’ll see if you test this scenario: in the Outlook UI, the item’s category is missing and checking the UserProperty programmatically shows UserProperties.Count equal to zero.

That is, although you can see that the category disappears right when you look at it, the ItemChange event doesn’t confirm this.

Here I’ve noticed the item Subject=”Modification Resolution” created in the Sync Issues folder. That item is the clue to solving the issue.

Modification resolution log

Note. To see the Sync Issues and Sync Issues | Conflicts folders in the Navigation Pane, press {CTRL+6} when in the Outlook Explorer window. You can press {CTRL+%number%} to find out other options.

The Modification Resolution item contains the log below:

17:43:23 Message class: {SU:IPM.Note}
17:43:23 Mail Conflict Resolution
17:43:23 Local subject: {SU:Subject string}
17:43:23 Remote subject: {SU:Subject string}
17:43:23 Local Message Entry ID: {CB:70, LPB:%EntryId-like string here%}
17:43:23 Remote Message Entry ID: {CB:70, LPB: %EntryId-like string here%
17:43:23 Local Message ChgKey: {CB:20, LPB:%omitted%}
17:43:23 Remote Message ChgKey: {CB:22, LPB:%omitted%}
17:43:23 Local Message PCL: {CB:44, LPB: %EntryId-like string here%}
17:43:23 Remote Message PCL: {CB:23, LPB:%omitted%}
17:43:23 Checking local modifications
17:43:23 Ignore property: 0x3FFA001F
17:43:23 Merge named property: Keywords
17:43:23 Compare named property: 0x85400102
17:43:23 Compare named property: %UserProperty name here%
17:43:23 Getting remote properties
17:43:23 Checking remote modifications
17:43:23 MVString (using local) named property: Keywords
17:43:23 Compare (conflict) named property: 0x85400102
17:43:23 Local: {CB:66, LPB: %EntryId-like string here%}
17:43:23 Remote: {Error (0x8004010F)}
17:43:23 Not equal (conflict) named property: 0x85400102
17:43:23 Local modification: {14:43:21.0095  17/02/2020 [DD/MM/YYYY]}
17:43:23 Remote modification: {14:43:23.0148  17/02/2020 [DD/MM/YYYY]}
17:43:23 Conflict generated, remote item is winner

Studying the modification resolution log

The property 0x3FFA001F that they ignore is PidTagLastModifierName; see here.

The Keywords named MAPI property above is the string that you access via the Category property in OOM; see here.

The conflict occurs when comparing the property 0x85400102 on the local and remote items.  This property is a mystery. It is only mentioned in several modification resolution logs published on the web. One of these logs is created in Outlook 2007 (in German). To proceed,  you should know that the hexadecimal value of a MAPI property has two parts: the ID and type. Googling for 0x8540 helps: SearchCode.com reports that StdAfx.h C/C++ header file – part of MFCMAPI (github) – contains this line: #define dispidPropDefStream 0x8540. Googling for dispidPropDefStream points to PidLidPropertyDefinitionStream which is here.

Searching a little further supplies the missing context and information: the 0x85400102 named property stores the stream of the UserProperties collection.

This makes sense: the log appears to tell us that the conflict occurred when comparing these streams on the local and remote items. The local item is okay: we know that the code added a UserProperty to it, and the stream is initialized. The remote is not okay: getting the 0x85400102 property produces the 0x8004010F = MAPI_E_NOT_FOUND exception.

Getting a working solution

So, on Cached Exchange we are trying to add a UserProperty to the item supplied by the ItemAdd event of the Sent Items folder. The section above shows that doing this creates a conflict when Outlook compares the local item with the remote copy and finds the UserProperties stream not initialized on the remote – not a surprise since only the local copy was changed by your code. Thus, the local and remote items are different, Outlook solves the conflict: it declares that the remote item is correct and replaces the local with a copy of the remote. I read somewhere and the log above confirms that: Outlook saves a copy of the modified local item to Sync Issues | Conflicts so that you could use it for your needs.

On a side note. I haven’t found a simple way to use that copy: in my case, it was required to show the category string to the user and the UserProperty must be on the item when it’s selected.

Again, the local item has a UserProperty on it while the remote item doesn’t have even the stream to hold UserProperties.

Well, what about creating that UserProperty when sending an email?

I admit that the idea made me uneasy: it contradicts to what I know about sending UserProperties. It suffices to say: never in my life I have recommended anyone to send UserPorperties out. There are stories that start with sending out an Outlook item with a UserProperty on it. Typically, these UserProperties appeared missing on the receiving end (stackoverflow.com). Sometimes, they produced a strange attachment named winmail.dat (find a solution on social.msdn.microsoft.com). Also, it is good to know that in older Outlook versions, UserProperties were transferred differently (support.microsoft.com).

Obviously, this issue is an exception in my practice. So, I’ve tried sending an email with a UserProperty and it works for me: the email added to Sent Items has that UserProperty on it.

Since this is limited to Cached Exchange, below is a service method. It requires referencing an interop for Outlook 2010 or above; _MailItem being MailItem minus events :).

bool IsCachedExchange(Outlook._MailItem mailItem)
{	
    bool result = false;	
    Outlook.Account account = mailItem.SendUsingAccount;	
    Outlook.Store store = account.DeliveryStore;	
    result = store.IsCachedExchange;	
    Marshal.ReleaseComObject(store);	
    Marshal.ReleaseComObject(account);	
    return result;	
}

Here’s the same method in VB.NET:

Function IsCachedExchange(mailItem As Outlook._MailItem) As Boolean
    Dim result As Boolean = False
    Dim account As Outlook.Account = mailItem.SendUsingAccount
    Dim store As Outlook.Store = account.DeliveryStore
    result = store.IsCachedExchange
    Marshal.ReleaseComObject(store)
    Marshal.ReleaseComObject(account)
    Return result
End Function

Good luck!

2 Comments

  • https://secure.gravatar.com/avatar/030ea955e0b606381804c5924d17e993?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Mark McCray says:

    Great Job Andrei!

    This described exactly what was happening to my customers. Your solution fixes one problem, the missing stream property, but I still have to modify the mail item in the initial ItemAdd event of the SentItemsFolder to indicate if the user saved the email or not. This happens very quickly as you might imagine. As soon as the “conflict” occurs my local changes are lost! (An the user is prompted a second time to save). Funny, they don’t like that…

    The big question is WHY does the first sync conflict ignore the local changes and make the remote win? This seems like a bug to me. Based on the logs, it is because the date the remote item was modified is later than my local modification, but again why? Shouldn’t the remote item start with the SAME modified date as the original? I suppose Microsoft must be making some sort of modifications to the remote item that we are unable to detect. I have checked the ItemProperties both before and after and the only changes are the dates, with the remote (seemingly unmodified) modified date being later than my date.

    At this present time, I am considering saving off the EntryID of the item, and the second time I see it, just changing the ItemProperty again and be done with it. But that sure seems like a hack to me.

    Once again, thanks for the article. You saved me tons of time. I love your products and especially the Add-In Express team!

    Keep it up!

    Mark

  • https://secure.gravatar.com/avatar/29957f26ad2d8ba527fd9cc8cfa7b2e0?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Andrei Smolin (Add-in Express Team) says:

    Thank you, Mark.

    > Funny, they don’t like that…

    Strange people, I suppose. :)

    > The big question is WHY does the first sync conflict ignore the local changes and make the remote win?

    I suppose this is a design choice, as I don’t see a way to find out which of the two copies is better: you only have properties and their values.

    I would look for a way to use the item in Sync Issues | Conflicts. Or, maybe, it is possible to do the save after a while (1 minute may be okay I suppose; look in a VERY lengthy topic at https://www.add-in-express.com/forum/read.php?SHOWALL_1=1&FID=5&TID=15644#nav_start).

Post a comment

Have any questions? Ask us right now!