The Synchronization Shuffle

I've given this solution to a couple customers so far and it appears to be working for them, so I thought I'd share it with the world.

The Problem:
You've written an application which uses IExchangeExportChanges::Synchronize to synchronize data between your back end database and Exchange. This application is typically deployed directly on an Exchange server, or a machine where the MAPI download has been installed. Occasionally, folder will have items in them which cannot be read through MAPI - typically these are messages which arrived via SMTP and which fail content conversion. From Outlook, you can usually move these messages around, but you can't open them. When the Synchronize method processes a folder containing one of these messages, it returns MAPI_E_CORRUPT_DATA and the whole folder sync is aborted.

Before we can discuss workarounds, we need to understand the problem: The client sets up to do a sync between the client and the server, possibly using IExchangeExportChanges::Config. Then the synchronization is started. During the synchronization, the client will ask the server to fill buffers with data. The server will fill these buffers with data representing the messages on the server. If the server has trouble dealing with one of the messages, for any reason, an error state is entered. If this error state is not resolved, then the entire synchronization operation fails. Exchange's MAPI doesn't know how to resolve the error state, so we fail.

Given that Exchange's implementation of IExchangeExportChanges has worked this way for over a decade, and that Exchange's MAPI implementation enters Extended support next year, there's little chance we'd be able to implement logic for resolving the error.

The Workaround:
If you dig around in edkmdh.h, you'll find an interesting interface: IExchangeExportChanges3, which introduces ConfigForSelectiveSync. This function is identical in use to Config except it adds a new parameter, lpMsgList. What this parameter allows us to do is to tell the synchronizer which messages we want it to sync. Now - we'll still have the problem that if the set of messages we've asked to synchronize includes one of these bad messages, we'll get MAPI_E_CORRUPT_DATA from Synchronize. But we can apply a little binary search style logic to get around the problem. Here's the logic:

  1. Try to sync using IExchangeExportChanges::Config - most folders will sync without problem, not requiring further processing
  2. If you get MAPI_E_CORRUPT_DATA, get the contents table and use ConfigForSelectiveSync in batches of 10-20 messages at a time. Most batches should not fail.
  3. If a batch fails, use ConfigForSelectiveSync to sync one message at a time. If it fails, you've identified a problem message, which you could report, move to another folder, delete, etc.

Of course, we could skip straight from step 1 to step 3, but on very large folders synchronizing a single message at a time could be rather slow. In fact, it might make sense to use even larger batches, say 1/10th the size of the folder. Then, if a batch fails, split it up into tenths and iterate until a single message at a time is being synchronized.

 

The Other Workaround:
If you're doing the synchronization manually using the Exchange Server Protocol Documentation (and that's a big IF, as it ain't easy), then you can implement code to resolve the error condition. The place to start in that document is section 2.2.4.3.4 errorInfo.