You Mean You Want the OOM To Actually Work?

We had a customer report recently that when he tried to fetch certain properties using the Outlook Object Model, all he got was garbage. This turned in to an interesting foray into the world of dual dispatch interfaces and VB.

The properties in question were these:

 ContactItem: 
    Email1EntryID 
    Email2EntryID 
    Email3EntryID
MailItem: 
    ReceivedByEntryID 
    ReceivedOnBehalfOfEntryID

Note that all of them are entry IDs, but they're not the commonly used entry ID properties like EntryID or StoreID.

If you ask for one of these properties using VB/VBA, all you get is garbage:

     strEntryID = objContactItem.Email1EntryID
    Debug.Print strEntryID
    On Error Resume Next
    Set objRec = Application.Session.GetRecipientFromID(strEntryID)
    If objRec Is Nothing Then
        Debug.Print "GetRecipientFromID failed"
    Else
        Debug.Print objRec.name
        Debug.Print objRec.entryID
    End If

I'll spare you the C++ code, but suffice it to say that requesting this property via the IDispatch interface works.

What's going on? The key detail here is how Outlook builds the VARIANT property to return it. It doesn't use something like SAFEARRAY. It doesn't covert the entry ID from a binary format to a hex encoded string. Instead, it uses VT_BSTR, setting the number of bytes as the length prefix and the binary blob as the string.

This actually works, as long as you never copy the BSTR. And when you're fetching the property with C++, you can just pass the pointer around and treat it however you like. But not in VB. Any manipulation of the variant in VB, even assigning it to a variable, results in an implicit copy of the data. And the routines doing the copy think they're dealing with a NULL terminated string, so they truncate the binary blob at the first NULL.

Of course, I filed a bug on all this, but ultimately we had to reject it. The only way to fix this is to change the form of the data returned by the property, which would break all existing C++ code that uses the property. Maybe for a future version of Outlook we'll look at adding new properties to expose these entry IDs as hex encoded strings.

However, if you're using Outlook 2007, you have a workaround - the PropertyAccessor handles binary properties correctly. You just need to know what properties to fetch. The MailItem properties, ReceivedByEntryID and ReceivedOnBehalfOfEntryID are already documented as PR_RECEIVED_BY_ENTRYID and PR_RCVD_REPRESENTING_ENTRYID. The contact properties, like the other contact linking props I documented before, are named properties in the PSETID_Address namespace. Here they are (in C++ form):

 #define dispidEmail1OriginalEntryID 0x8085
#define dispidEmail2OriginalEntryID 0x8095
#define dispidEmail3OriginalEntryID 0x80A5

And here's how you can use them from VB:

     Const dispidEmail1OriginalEntryID As String =
        "https://schemas.microsoft.com/mapi/id/{00062004-0000-0000-C000-000000000046}/80850102"
    Set oPA = objContactItem.PropertyAccessor
    strEntryID = oPA.BinaryToString(oPA.GetProperty(dispidEmail1OriginalEntryID))
    Debug.Print strEntryID
    Set objRec = Application.Session.GetRecipientFromID(strEntryID)
    If objRec Is Nothing Then
        Debug.Print "GetRecipientFromID failed"
    Else
        Debug.Print objRec.name
        Debug.Print objRec.entryID
    End If