In Outlook 2007 and prior, a common pattern for solutions written using Exchange Client Extensions (ECE) involved intercepting when Outlook would open a message, so that the contents of the message could be modified. This technique was used in several solutions, from virus scanning to implementing e-mail archiving. The events available in ECEs made these sort of solutions possible and there were no comparable events to do similar work in the Outlook Object Model.
With Outlook 2010, we decided to cut ECEs completely. We anticipated that vendors who relied on ECEs as part of their solution would need additions to the object model to make it possible to migrate their code to Add-Ins. One of our additions was the BeforeRead event. The purpose of this event was to allow developers a chance to modify the MAPI message that Outlook was about to render before Outlook had done any work with the message. It worked well in our testing, and several vendors have already implemented solutions using it.
There’s a problem though: if you try to change the message class during BeforeRead to one of our secure message classes, such as “IPM.Note.SMIME”, Outlook acts as if you didn’t. You can do all the work to add the encrypted body type as an attachment, update all the attendant properties, and Outlook will still treat the message as if it were IPM.Note. It’s almost as if, despite what the documentation for BeforeRead says, Outlook was reading the properties on the message before handing it to the event.
Here’s what’s going on: The Outlook developers didn’t want to sprinkle special case code for encrypted messages all through their code base. Instead, they created a wrapper interface, IMAPISecureMessage, which implements IMessage and a few other functions. This wrapper interface handles most of the details of encrypted messages. The very first thing Outlook does when it opens a message from a store is to wrap it in this IMAPISecureMessage interface. This wrapped object is what you get when you ask for the MAPIOBJECT from the BeforeRead event.
One of the quirks of this object is that it postpones any initialization until you actually do something with it, such as call GetProps or SetProps. At this point, it runs through its initialization routine, one portion of which reads the message class of the message and makes decisions on whether and how to handle encryption. So the BeforeRead documentation was technically correct – Outlook hadn’t read any properties on the message when the event started. But using BeforeRead to update the message class still wouldn’t work since the first call to SetProps to update the message runs through code that reads the previous message class!
Fortunately, we anticipated this scenario and documented a way around it: IMAPISecureMessage::GetBaseMessage. This function, when called on a message which has been wrapped for security, returns the underlying wrapped message. This message can then be read from and written to without triggering the setup logic. As long as all of this work is done before anyone tries to read or write properties via the secure wrapper, any changes made will be fully reflected in what Outlook renders.
So, if you’re using BeforeRead in your Add-In solution and want to make sure all of your changes are reflected, you’ll need to do something like this:
- Outlook calls their BeforeRead event, from which you have access to the MAPIOBJECT.
- Call GetMapiObject to get the wrapped message.
- Query this object for IMAPISecureMessage.
- From this interface, call GetBaseMessage to get an unwrapped message.
- Update this message object using regular MAPI functions (GetProps, SetProps, etc.)
- Release everything and return from the event.
- Outlook now reads from the wrapped message which triggers the setup logic, which now recognizes all of the changes you’ve made.
Some notes about the documentation for IMAPISecureMessage and GetBaseMessage. This is brand new documentation and we’ve found a few errors in it, all of which are slated for correction in a future update to the MSDN:
- We left out some constant definitions: http://blogs.msdn.com/b/stephen_griffin/archive/2010/12/16/some-mapi-constants.aspx
- The documentation references a non-existent header file, exchsec.h. There is currently no header file you can include to get this interface. You’ll have to write the interface definition by hand.
- The vtable we give for IMAPISecureMessage is missing an entry at the beginning. This means GetBaseMessage should be the 6th entry in the vtable, not the 5th as shown.