Outlook interop code throws "message has been changed" error the second time when the code tries to save a mail item

0

First of all, this problem only happens on a few client machines and I can not replicate it on any of my test machines.

So I have the following test code in LINQPad:

var application = new Application();
var session = application.Session;
const string entryId = "arbitrary entry ID";
const string propertyName = "http://schemas.microsoft.com/mapi/string/{31A9B8DA-D4A0-4B96-87AE-01D6E9BCFCCE}/Test/0x0000001F";

// Save test property for the first time.
var mail = (MailItem)session.GetItemFromID(entryId);
var propertyAccessor = mail.PropertyAccessor;
propertyAccessor.SetProperty(propertyName, 1);
mail.Save();

Marshal.ReleaseComObject(propertyAccessor);
Marshal.ReleaseComObject(mail);

// Save test property for the second time.
mail = (MailItem)session.GetItemFromID(entryId);
var propertyAccessor = mail.PropertyAccessor;
propertyAccessor.SetProperty(propertyName, 2);
mail.Save();

Marshal.ReleaseComObject(propertyAccessor);
Marshal.ReleaseComObject(mail);
Marshal.ReleaseComObject(session);
Marshal.ReleaseComObject(application);

The second mail.Save() call has a 100% rate on these few client machines to throw the exception: System.Runtime.InteropServices.COMException (0x80040109): The operation cannot be performed because the message has been changed.

Since the above code properly releases the first mail object and retrieves the second mail object using entry ID again. The chance to have something changing the mail object between the second retrieval of the object and calling its Save() method is very low, not to mention the 100% reproducible rate.

I can only think that it looks like a bug in Outlook that it may permanently mark a mail object as changed as soon as anything calls the Save() method once.

Does any one know if there is a work around?

These machines are using the latest version of Office 2016.

c#
email
outlook
outlook-addin
asked on Stack Overflow Sep 28, 2017 by Leon Zhou

1 Answer

1

IMAP4 is one of the worst - every time you try to touch any of the store objects, it tries to sync. You can try to bypass the IMAP4 layer and go directly to the PST provider used as the underlying local storage. In Extended MAPI (C++ or Delphi), you can do that using the IProxyStoreObject interface. In case of languages other than C++ or Delphi, you can use Redemption and its RDOSession.Stores.UnwrapStore method - the message can be then opened from the unwrapped store using RDOStore.GetMessageFromID.


User contributions licensed under CC BY-SA 3.0