The MAPI/CDO download package now works on Windows Server 2008 and Vista. The main blocker to getting this to work was the fact that in Windows Server 2008 and Vista, mapi32.dll was marked as a system file. Any attempt to replace it would be blocked or undone. Exchange's MAPI has always replaced mapi32.dll with its own version, so it couldn't work on those operating systems.
The fix is modify Exchange's MAPI to take advantage of the MAPI Stub Library mechanism. We moved Exchange's MAPI binaries out of system32/syswow64 and placed them under C:\Program Files\ExchangeMAPI ("Program Files (x86)" on a 64 bit machine). We also renamed Exchange's mapi32.dll binary. It's now called ExMAPI32.dll.
Update: Looks like the Ehlo blog picked this up. Welcome Ehlo readers! I was involved in getting this update done, so lemme know if you have any questions.
Well, that didn't take long. We just released the latest MAPI download this weekend and yesterday we got a case from a customer who's application no longer functioned when they upgraded. The issue was quickly resolved, and I was asked to communicate some of the details in case anyone else runs in to it.
The important detail here is that in order to get Exchange's MAPI to work on Vista and Windows Server 2008, we had to make it work with the MAPI Stub Library. So we installed our binaries under Program Files and set the following registry keys*:
The first key tells the stub library where to find an Extended MAPI implementation called ExchangeMAPI, and the second key tells the stub library that when an application loads it, the default MAPI implementation to which all calls should be directed is ExchangeMAPI. In other words, when you load the stub library, it will in turn load exmapi32.dll.
This is the same as what Outlook does when it's installed - it registers itself and sets the Default key to "Microsoft Outlook".
Now, this customer had a product which had worked for years with either Outlook's or Exchange's implementation of MAPI. Apparently, they had had problems in the past with systems where Outlook was installed, but the default mail client had been set to something else, such as Hotmail. So, to work around this problem, following the advice given in Explicitly Mapping MAPI Calls to MAPI DLLs they wrote a key under HKLM\Software\Microsoft\Windows Messaging Subsystem\MSMapiApps mapping their application to the "Microsoft Outlook" implementation of MAPI. This worked on machines where Outlook was installed, since there would be a Microsoft Outlook key from which to read DLLPathEx. And on a machine where Exchange's MAPI was installed, since Exchange overwrote the stub library nothing written in any of those keys mattered.
Now, of course, it matters. When they set their application to use "Microsoft Outlook", but Outlook isn't installed, the stub doesn't know where to turn. It can't find the default mail client. It can't just start loading random MAPI binaries, so it turns to it's fallback mechanism, which is to look for MAPI32x.dll. Since that's not there either, it gives up and fails the call to MAPIInitialize.
There's not a perfect workaround here - there's no way to tell the stub that there are two MAPI implementations your application can work with and just use whichever one is available. The simplest solution here is probably to not use the MSMapiApps key and either accept whatever MAPI implementation is the default or load MAPI binaries manually and bypass the stub. The latter is what MFCMAPI does. You could also do some runtime detection of whether "Microsoft Outlook" or "ExchangeMAPI" are listed under the Mail key and set the MSMapiApps key appropriately before loading MAPI. Or, depending on where your application is meant to be installed, pick an implementation of MAPI and insist that it be the one installed with your app. No matter what you choose, this issue illustrates why I stressed to the commenter of my previous post on this update: "The move from system32 to program files is a fairly large and potentially destabilizing move though. You should definitely test your applications with this update."
*64 bit complicates this a bit by redirecting the registry keys. So for those of you playing along at home on a 64 bit box, look under HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Clients\Mail
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:
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 188.8.131.52.4 errorInfo.
Randy Byrne and I just published an article in the MSDN showing how to use the Exchange Server Protocols documentation to create a contact. This is the first opportunity I've had to publicly show off the add-in architecture I built in to MFCMAPI last year. Over the next few months, I hope to produce a few more add-ins as we work our way through the protocol documentation. Right now I'm writing a parser for the recurrence blob and it's not as difficult as I thought it'd be. As Randy said in a mail announcing this article: "Long live MFCMAPI!"
The June 2008 Second Release (build 184.108.40.2067) is live: http://www.codeplex.com/MFCMAPI
Yeah - that's right - second release. I had promised myself I wouldn't put out multiple updates in a month as I figured that could get confusing, but there was a regression in the June 2008 release that I had to fix ASAP- output to XML no longer output property values! I lay complete blame on my tester - send your flame mails directly to him.
Here's the change list - see the Issue Tracker on Codeplex for more details, or look at the code:
In a comment on my post about the MAPI download working with Vista, JP pointed out that something in the updated DLLs is changing the current working directory. I got a chance to look at this today and here is what I found: this is the MAPI Stub's doing. In order to support some wonky old MAPI implementations, the stub library will change the current working directory to the directory of the MAPI implementation it's loading. As far as I can tell, the stub has always done this. It's only being noticed with Exchange's MAPI now because now Exchange's MAPI can be loaded through the stub library.
There's two ways to work around this if it's causing problems in your apps. One is to realize the directory might be changed on you and change it back:
The other way to work around the issue is to load Exchange's MAPI directly. You can pull the path from the same key the stub library uses (HKEY_LOCAL_MACHINE\SOFTWARE\Clients\Mail\ExchangeMAPI::DLLPathEx) and load the DLL yourself. If you're already dynamically loading MAPI this wouldn't be a major change. If you're statically linking mapi32.lib, then you'll need to use the first workaround.
The June 2008 Release (build 220.127.116.116) is live: http://www.codeplex.com/MFCMAPI
I've been digging through the protocol docs. This update represents my first pass harvesting the easy stuff, like property names and flag values. I definitely plan to get to the more complex stuff, like recurrence blobs. Some of the work done here was putting in plumbing to make that possible, like consolidating all of the property parsing code into one code path. I've also introduced Smart View, which replaces the Flag column, allowing MFCMAPI to parse more kinds of data automatically. Expect to see this feature expanded greatly as I work my way through the protocol documentation.
That's right - version 1.0 of the Exchange Server Protocol Documentation has been published in the MSDN:
Here's the press release:
And the page with links to all the licenses if you want to make use of the documentation:
As noted before, support for this documentation is conducted through the forums. Enjoy