TNEF (Chapter 2): Old School

As discussed in Chapter 1 of this captivating series, MAPI contains an interface to allow developers to create and read TNEF data. This interface is the ITnef interface. There are only a few methods in this interface and they are, for the most part, self explanatory. The entire process of creating a TNEF stream can be done in just a few steps:

  1. Call OpenTnefStreamEx to get a TNEF stream to write into.

    Make sure you pass in TNEF_ENCODE since you’ll be creating TNEF. If you were reading TNEF, you’d pass in TNEF_DECODE instead. The other flags to worry about here are TNEF_BEST_DATA, TNEF_COMPATIBILITY, and TNEF_PURE. All of these just signal to MAPI how you want the properties you add to the TNEF stream treated. They will either be all converted to the old-school attributes (TNEF_COMPATIBILITY) – you shouldn’t use this one; some will be converted to attributes but also written to the attMAPIProps section (TNEF_BEST_DATA); or they will all just be written to the MAPI props and none of them written to the attributes (TNEF_PURE).

  2. Call EncodeRecips and pass in the Recipient table you get from a call to IMessage::GetRecipientTable on your message.

  3. Call AddProps passing in an SPropTagArray of non-transmittable prop tags and use the TNEF_PROP_EXCLUDE flag.

    There are essentially two schools of thought for building your TNEF: exclude the props you don’t want and let MAPI deal with the rest; or choose carefully which props you do want to include and add them each piecemeal. These are the reason for having the TNEF_PROP_EXCLUDE and TNEF_PROP_INCLUDE flags. One of them says here are the properties I don’t want you to encode (TNEF_PROP_EXCLUDE) and the other, TNEF_PROP_INCLUDE, says I want you to include all of these properties.

    There’s another method, SetProps, which does just that, sets the value of a property in the TNEF stream to a value you supply. This allows you to modify the data of the message you are trying to encode, or add additional properties that weren’t on the original.

    Back to AddProps for a moment. The flags that supports don’t stop with TNEF_PROP_INCLUDE and TNEF_PROP_EXCLUDE, There is also TNEF_PROP_ATTACHMENTS_ONLY which says “of the properties I’ve given you to work with, I only want you to include/exclude the ones that have to do with attachments. Contrast that with TNEF_PROP_MESSAGE_ONLY which says "”of the properties I’ve given you to work with, I only want you to include/exclude the ones that have to do with the message itself – not attachments.” Then there’s the CONTAINED flags: TNEF_PROP_CONTAINED and TNEF_PROP_CONTAINED_TNEF. TNEF_PROP_CONTAINED means that these properties are going on an attachment; and the TNEF_PROP_CONTAINED_TNEF means I have TNEF data I’m going to give you to put in an attachment – like if you already had a TNEF blob you wanted to include as an attachment, which I’ll demonstrate below.

  4. Once you get all your properties added and included/excluded properly, you call Finish and you’re done. One thing that makes it a little complicated though, is that you have to keep alive all the pointers and streams, etc you’re using in your TNEF until Finish is called, because that’s when all the internal work is actually done. So when you call Finish, that’s when MAPI says, Oh, ok, let me go get that recipient table you gave me. If you’ve already released it, then the process fails.

So it’s pretty easy to do this. This is essentially the way that MFCMAPI demonstrates how to do it (look in File.cpp under SaveToTNEF). There are problems associated with doing it this way when it comes to Unicode properties and when having multiple embedded messages.

The more complicated way to do this to work around some of the issues described above is to add the properties you want explicitly, including each attachment.

The basic difference in the strategy is that instead of calling AddProps with TNEF_PROP_EXCLUDE, call it with TNEF_PROP_INCLUDE and give it the SPropTagArray you get from a call to GetPropList on the message. You’ll need to filter out the non-transmittable properties (such as custom props and things like the Store EntryID). Once you add all the message props, call GetAttachmentTable and loop through each attachment and do one of two things, if it’s not an embedded message, just add the attachment data with AddProps on PR_ATTACH_DATA_BIN; otherwise, you’re going to recurse over yourself and build a TNEF stream from the embedded message. When you call Finish on it, then you’ll add it to the parent TNEF stream by calling AddProps with PR_ATTACH_DATA_OBJ and using the TNEF_PROP_CONTAINED_TNEF flag and give it the stream for your TNEF blob. Once you unwind all the way, you’ll have your “master” TNEF stream. Essentially, you’ll follow the steps here: https://msdn.microsoft.com/en-us/library/cc839833.aspx