Miscellaneous Outlook Redemption Objects
or Welcome to the world of Extended MAPI
What is missing from the Outlook Object Model?
While Outlook does expose a lot of Extended MAPI
properties via its object model, there are many (hundreds) of properties
that are only accessible via Extended MAPI. Some of these properties were
probably deemed not important enough to be included in the Outlook object
model, some others are Exchange Server or PST specific and hence didn't
quite fit into the object model. If you browse Outlook newsgroups archives,
there are plenty of questions like "Whenever I access a recipient address,
Outlook gives me an Exchange address, but how do I get an SMTP address?",
"How do I find out the size of all messages in a folder?" or "How do I find
out sender's e-mail address?". The answer to all of these and many other
questions is "Use Extended MAPI". This doesn't make the majority of VBA and
.Net developers feel warm and cozy, given the fact that VB and .Net cannot
directly access Extended MAPI interfaces and structures. If you want to get
a list of the available properties, download OutlookSpy.
Outlook
2007 did introduce the PropertyAccessor object, but but it has a number of
frustrating limitations: it will not allow to set many MAPI properties
that Outlook deems "important", it will not let you read all of the MAPI
types, and it will not let you retrieve large binary properties, date/time
properties are sometimes rounded off to the next minute.
To address the most glaring omissions of the Outlook object model, Redemption includes a utility object - Redemption.MAPIUtils. See below examples on how to use this object.
Note that this object is being deprecated in favor of the RDOSession object - while it Ok to use the methods that do not require an active MAPI session (such as HrGetOneProp, HrSetOneProp), methods that requite a session (such as AddressBook, GetItemFromID) require MAPIUtils object to obtain a MAPI session. You can set its MAPIOBJECT property to Namespace.MAPIOBJECT from the Outlook Object Model, but otherwise MAPIUtils will silently log to the default profile, which might not be what you want. RDOSession object requires a MAPI session for most of its functionality and will raise an error if there is no MAPI session.
To access the comprehensive range of the functionality exposed by the MAPI system, Active Directory , and Exchange Web Services, use the RDOSession object.
Accessing raw Extended MAPI properties
A short overview of Extended MAPI properties
Most Extended MAPI objects (messages, folders, recipients, attachments, etc) are implemented as amorphous bags of properties. Unlike regular databases, MAPI objects can store both required properties (such as Entry ID) and any other properties that might only exist on a particular instance of an object. An example is a folder that contains both regular email messages and contacts; obviously these two kinds of messages are very different with different sets of properties. From the Extended MAPI point of view both kinds are messages (IMessage interface), the only difference is the sets of properties, but Extended MAPI doesn't care about that.
To access Extended MAPI properties, MAPI defines IMAPIProp interface from which most of Extended MAPI interfaces are derived (IMessage, IMsgStore, IMAPIFolder, IMailUser, IDistList etc). IMAPIProp interface has GetProps() method for retrieving any number of properties that particular object contains.
Every Extended MAPI property is set and retrieved using a 4 bytes index: 2 upper bytes contain property id, while 2 lower bytes represent the type of the property (string, integer, binary array, etc). For example, e-mail message subject (PR_SUBJECT) has a hex value of 0x0037001E, where 0x0037 is property id and 0x001E signals its type as PT_STRING8.
Below is a table of Extended MAPI property types:
|
||
Property type |
Hex value |
Description |
|
||
PT_UNSPECIFIED |
0000 |
Reserved, not accessible to VB |
PT_NULL |
0001 |
null value |
PT_I2, PT_SHORT |
0002 |
signed 16 bit value |
PT_I4, PT_LONG |
0003 |
signed or unsigned 32 bit value |
PT_R4, PT_FLOAT |
0004 |
32 bit floating point |
PT_R8, PT_DOUBLE |
0005 |
64 bit floating point |
PT_CURRENCY |
0006 |
currency (64 bit integer) |
PT_APPTIME |
0007 |
date type |
PT_ERROR |
000A |
32 bit error value |
PT_BOOLEAN |
000B |
boolean |
PT_OBJECT |
000D |
embedded object, not accessible to VB |
PT_I8, PT_LONGLONG |
0014 |
64 bit signed integer, not accessible to VB |
PT_STRING8 |
001E |
8 bit string |
PT_UNICODE |
001F |
unicode string |
PT_SYSTIME |
0040 |
date type |
PT_CLSID |
0048 |
OLE GUID |
PT_BINARY |
0102 |
byte array |
|
For example, PR_SENDER_EMAIL_ADDRESS Extended MAPI property tag has a hex ID of 0C1F and type PT_STRING8, therefore its 4 bytes property tag (hex) is &H0C1F001E. To access this property using Redemption, use the code below:
dim utils, MailItem,
PrSenderEmailAddress, SenderEMail
'Get the first item in the inbox, can be any other item |
Note that the same result can be achieved using SafeXXXItem object and its Fields collection:
dim sItem, MailItem,
PrSenderEmailAddress, SenderEMail 'Get
the first item in the inbox, can be any other item |
Besides the types above Extended MAPI can also deal with multivalued (array) properties. To indicate a multivalued (array) property, "OR" its property type with a (hex) 1000, e.g. to indicate that a property is of type PT_MV_STRING8, "OR" PT_STRING8 (001E) with 1000: it will be 101E in hex. Example: PR_EMS_AB_PROXY_ADDRESSES property is a multivalued property of type PT_MV_STRING8 with a property id of 800F (hex). Its 4 bytes property tag is &H800F101E. If you are curious, this property is not exposed through the Outlook object model, but it has all e-mail addresses (EX, SMTP, FAX, etc) for an Exchange Server user (which is AddressEntry.MAPIOBJECT in Outlook object model, IMailUser in Extended MAPI).
To find out what properties a particular object exposes and their property tags, get MdbView or OutlookSpy.
Note that Redemption exposes thousands of MAPI tags through the MAPITags enumeration (e.g. MAPITags.PR_SUBJECT_W).
So far so good. But what do you do if you want to store your own custom data with an item? How do you make sure that a property tag you select will not conflict with somebody's else property tag? The answer is named properties. While all properties are still accessed using 4 bytes property tags, Extended MAPI provides mechanism for making sure that your own property will not conflict with another named property. To create a custom property, generate a GUID (it is a good idea to use the same GUID for all of your custom properties) and an ID for each of your property. The ID can either be an integer or a string. Given the GUID and ID, Extended MAPI will return a unique for this particular item (or even for a message store) 4 bytes property tag.
This is exactly how Outlook implements UserProperties collection. The GUID for all Outlook's UserProperty's is {00020329-0000-0000-C000-000000000046} (it is PS_PUBLIC_STRINGS symbolic value in Extended MAPI). For example, to access Outlook's Categories property, the GUID is {00020329-0000-0000-C000-000000000046} and property ID is "Keywords" (again, MdbView or OutlookSpy will show you the GUID and its ID for any property). Here is how you can access Categories without using Outlook object model:
dim utils, MailItem,
PrCategories, Categories
'Get the first item in the inbox, can be any other item "{00020329-0000-0000-C000-000000000046}", _ "Keywords", true)
'make sure we have
the right property type - PT_MV_STRING8 'for
PT_MV_STRING8 type properties we get back an array of strings |
The same result using SafeXXXItem object:
dim sItem, MailItem,
PrCategories, Categories 'Get
the first item in the inbox, can be any other item
'make sure we have
the right property type - PT_MV_STRING8
'for PT_MV_STRING8 type properties we get back an array of strings |
The example above is probably a little bit artificial since you can access the same property using Outlook object model - the advantage is you get back an array of strings instead of a comma separated string. In some cases you might have to access named properties with a GUID different from PS_PUBLIC_STRINGS Outlook uses for UserProperties.
Examples of accessing raw Extended MAPI properties from VB or VBA
Below are some examples of accessing some Extended MAPI properties using Redemption. Note that you can access these properties using SafeXXXItem objects and their Fields collection - see examples above.
Internet headers - PR_TRANSPORT_MESSAGE_HEADERS property. If you have spent any time in the Outlook developer newsgroups, you know how often this question is asked:
dim utils, oItem,
PrHeaders, Subject
'Get
the first item in the inbox, can be any other mail item |
PR_SENDER_EMAIL_ADDRESS - this property is not exposed through the Outlook object model, but it comes quite handy:
dim utils, MailItem,
PrSenderEmailAddress, SenderEMail
'Get
the first item in the inbox, can be any other mail item |
Default SMTP address of an Exchange user:
dim utils, MailItem,
PrSMTPAddress, SMTPAddress
'Get
the first item in the inbox, can be any other mail item 'It is assumed that the
recipient is an Exchange Server user.
PrSMTPAddress) |
Set a custom icon on a message. This property is not officially documented or supported, and the valid value ranges differ for the different versions of Outlook. The example below sets an "Open Envelope" icon on a mail item. You might want to use this if you don't want to show that an e-mail item was forwarded or replied. Play with the different values, see what custom icons you can set. You cannot set your own truly custom icons, only whatever Outlook already has using an integer index (256 in the example below):
dim utils, PrIconIndex, MailItem
'Get the first item in the inbox, can be any other mail item 'this value corresponds to the
"Open Envelope" icon. Try different values just for the fun of
it! |