Thursday, July 24, 2008
Outlook Entry IDs Made Easy
When developing for Outlook you will quickly realize that there's more than one way of doing things. For example, take a look at the number of APIs for interacting with Outlook:
It's important to understand the relationship between MAPI and the OOM:
At its simplest, an Entry ID is a variable length byte buffer, sometimes represented with a simple byte array. In MAPI, most Entry IDs are represented by the ENTRYID structure:
I solved the problem by creating a class (yet another format!) to facilitate manipulation of all these formats:
- Outlook Object Model (OOM)
- MAPI
- CDO
- Office/Outlook COM Add-in (_IDTExtensibility2)
- Exchange Client Extensions
It's important to understand the relationship between MAPI and the OOM:
- The MAPI API dates back to the early Outlook days. It's used for the messaging and storage subsystems in Outlook.
- The Outlook Object Model sits on top of MAPI and wraps a minimal amount of its functionality. It also exposes some of the Outlook UI to the developer.
At its simplest, an Entry ID is a variable length byte buffer, sometimes represented with a simple byte array. In MAPI, most Entry IDs are represented by the ENTRYID structure:
mapidefs.h
typedef struct {
BYTE abFlags[4];
BYTE ab[MAPI_DIM];
} ENTRYID, FAR *LPENTRYID;This structure usually comes wrapped in the SBinary structure.mapidefs.h
typedef struct _SBinary {
ULONG cb;
LPBYTE lpb;
} SBinary, FAR *LPSBinary;In the Outlook Object Model things take a turn for the worst:- Most times you see Entry IDs as a BSTR that is hex encoded (i.e. the strings look like this: "000F1329EC29A0382BC...").
- Sometimes it still uses a BSTR, but it doesn't encode the buffer, instead using the BSTR as a binary blob. Although inconsistent, this is perfectly valid.
- Sometimes it's useful to represent the Entry ID as a SAFEARRAY of VARIANT VT_UI1 (this is the format VB uses for its byte arrays). For example, I've needed to have a function that can return a property any type from a MAPI object that needs to be called through automation (i.e. VB). For example, the IMAPIUtils::HrGetOneProp method from the Redemption Library does this.
I solved the problem by creating a class (yet another format!) to facilitate manipulation of all these formats:
class EntryIdNow whenever I get an Entry ID in any format, the first thing I do is convert it to an EntryId instance. This adds many convenient functionalities:
{
public:
EntryId(EntryId const& entryId);
// All explicit so that it's always clear what we are doing.
explicit EntryId(BSTR const* str, bool hexEncoded = true);
explicit EntryId(SBinary const* binary);
explicit EntryId(SAFEARRAY const* array);
explicit EntryId(unsigned long count, unsigned char const* bytes);
// Again, no implicit conversion
CComBSTR toString(bool hexEncoded = true) const;
CComSafeArraytoSafeArray() const;
std::vectortoByteArray() const;
// ... add more as you need them
// TODO: Add more custom helper functions.
bool isLongTermId() const;
// TODO: Add comparison operators for convenience and ordered
// container support (such as std::map keys). When comparing
// Entry Ids, remember you need to use the
// IMAPISupport::CompareEntryIDs method.
private:
std::vector<unsigned char> m_buffer;
}
- Standardized interface to all ids.
- Easy comparison of entry ids.
- EntryId is a copyable class and can be passed by value.
- EntryId can be held in a STL container.
- Your other helper classes and methods can use the EntryId as parameter instead of other formats.