I originally wrote this document as the first appendix of D.A.M.E . We dropped it from the final manuscript for scheduling reasons.
Outside of the standard APIs, a Microsoft Exchange application may find it useful to duplicate certain client functions, such as creating and manipulating folder shortcuts, message links, or view descriptors.
All of the file formats documented here apply only to the Microsoft Exchange client software in version 4.0 and related minor versions (4.1, etc.). They are not guaranteed to work on other MAPI clients, or later versions of Microsoft Exchange.
The Exchange Client supports a special file format through which users can create Windows Explorer-style shortcuts to folders within MAPI message stores. In Windows 95 and Windows NT 4.0, a desktop shortcut is a component object supporting IShellLink, made persistent within the filesystem as a file with the .LNK suffix through the IPersistFile or IPersistStream interfaces. The Windows Explorer shell creates shortcuts when the user drags an object onto the desktop, or else when the user drags an object into another folder with both the Control and Shift keys depressed. When the user opens the shortcut, the shell queries the shortcut object for the identity of its destination (via IShellLink::GetPath or IShellLink::GetIDList), then opens that destination object instead.
Exchange Client folder shortcuts are not component objects in the manner of shell links. They are simply binary files with the .XNK suffix, associated with the Exchange Client through entries in the system registry, into which the Exchange Client writes sufficient information to locate and display the folder again upon request. The Exchange Client creates folder shortcuts when the user invokes the File - Create Shortcut command. When the user opens a folder shortcut, the shell passes the shortcut file to the Exchange Client, which reads the necessary information from the file to create a new viewer window on that folder.
The shortcut file consists of a block of header information, describing the window in which to display the folder, followed by a series of long-term entry identifiers, describing a series of MAPI objects to open, each against the previous object in the series. The first object in the series must be a message store, and each object thereafter a folder within that message store.
The header consists of 14 32-bit DWORDs, as depicted in Table A-1.
| Byte Offset | Type | Contents |
| 0 | DWORD | Version number of folder shortcut structure. This must have the value 5. |
| 4 | DWORD | Object type code, as might be returned by IMAPIProp::OpenEntry. This must have the value MAPI_FOLDER (3). |
| 8 | DWORD | Must be zero (0). |
| 12 | DWORD | Version number of following window definition fields. This must have the value 5. |
| 16 | DWORD | Code passed to the ShowWindow API when displaying the viewer window: e.g. SW_SHOWNORMAL, SW_SHOWMINIMZED, etc. |
| 20 | DWORD | Origin of left-hand border of viewer window, in screen pixels. |
| 24 | DWORD | Origin of top border of viewer window, in screen pixels. |
| 28 | DWORD | Width of viewer window, in screen pixels. |
| 32 | DWORD | Height of viewer window, in screen pixels. |
| 36 | DWORD | Offset within window of splitter bar between the folder (left-hand) and message (right-hand) panes of the viewer window. |
| 40 | DWORD | Nonzero if the viewer window is displaying the folder pane. |
| 44 | DWORD | Nonzero if the viewer window is displaying the toolbar. |
| 48 | DWORD | Nonzero if the viewer window is displaying the status bar. |
| 52 | DWORD | Byte count of remainder of file |
Table A-1 Header of a folder shortcut file
The remainder of the file describes each object in turn that the Exchange Client must open in order to display the sought folder. It consists of a sequence of records, each describing a MAPI object to open, and following the format given in Table A-2, concluded by a terminating sequence of two zero bytes. The final byte count field in the file header contains the sum of the sizes of these records and the size of the terminator.
| Byte Offset | Type | Contents |
| 0 | DWORD | Size of record, in bytes, including this field |
| 4 | DWORD | Object type code, as might be returned by IMAPIProp::OpenEntry. This must have the value MAPI_STORE (1) for the first record, and MAPI_FOLDER (3) for each succeeding record. |
| 8 | DWORD | Byte count of next field |
| 12 | n/a | Data of persistent entry identifier for this object |
| unknown | 4, 5, 6, or 7 BYTEs | Pad bytes sufficient to make the size of this record a multiple of 4. Every record must have at least 4 pad bytes. |
Table A-2 Record for one MAPI object in a folder shortcut file. The byte offset given is relative to the beginning of the record, not the beginning of the file.
Every folder shortcut will have at least two of these records: one for the store to open on the session, and one for a folder to open on the store. A folder shortcut may specify additional folder objects to open as well, with the Exchange Client opening each new folder entry identifier on the previous MAPI folder object in the sequence. The Exchange Client will display the final object opened.
When attaching a message to another message under composition, a user may elect to include a link to the message instead of attaching the entire message. An icon representing the linked message then appears in the message under composition. This message link references the destination message, containing enough information to allow a recipient of the outer message to open the link and thus open the linked message.
To create a message link within a message, the Exchange Client creates an attachment on the outer message with PR_ATTACH_METHOD as ATTACH_OLE and PR_ATTACH_TAG as OID_OLE2_STORAGE, such that the attachment contains a structured storage instance IStorage. The Exchange Client stamps this storage (via WriteClassStg) with the class identifier {00020D09-0000-0000-C000-000000000046}, marking it as a message link, then creates a set of streams in the root storage to describe the destination message. Table A-3 summarizes these streams and their contents.
| Name | Contents |
| MailMsgAttMdb | Long-term entry identifier of the MAPI message store hosting the destination message |
| MailMsgAttFld | Long-term entry identifier of the folder containing the destination message |
| MailMsgAttMsg | Long-term entry identifier of the destination message |
| MailMsgAttSubject | The subject of the destination message, as used in the icon representing the link. NUL-terminated string of 8-bit characters in the code page of the link creator. |
| MailMsgAttIcon | A metafile of the icon representing the link, as returned by GetMetaFileBitsEx. |
Table A-3 Streams in the storage for a message link
Chapter 8 briefly touched upon the format of view descriptors, the messages that encode the views used by the Exchange Client.
All views are messages of class IPM.Microsoft.FolderDesign.NamedView in the associated contents table of some folder.
Common views appear in the top bin of the Views - Personal Views cascade menu. They always appear, regardless of the current folder. Users cannot modify common views.
Common views are stored in the default message store, in the IPM_COMMON_VIEWS folder identified by the PR_COMMON_VIEWS_ENTRYID property on this store. (This property is valid only if the FOLDER_COMMON_VIEWS_ENTRYID flag is set in the PR_VALID_FOLDER_MASK property on the store.) The Exchange Client setup program installs common views into a message store, where they are available to all users of that store.
Personal views appear in the bottom bin of the View - Personal Views cascade menu. They always appear, regardless of the current folder. Users define and modify personal views.
Personal views are stored in the default message store, in the IPM_VIEWS folder specified by the PR_VIEWS_ENTRYID property on this store. (This property is valid only if the FOLDER_VIEWS_ENTRYID flag is set in the PR_VALID_FOLDER_MASK property on the store.) Only the user that created a personal view has access to it.
Folder views appear in the View - Folder Views cascade menu. The views for a particular folder appear only when that folder is the current folder. A user must have owner privilege on a folder in order to define and modify its views.
Folder views are stored in the folder itself.
Sticky views do not appear on any menu; rather, they implement the current view on a folder. When a user modifies a view on a folder, the Exchange Client saves that view in order to associate it with the folder, so that when the user returns to the folder, the same view will reappear.
Sticky views are stored in the default message store, in the IPM_VIEWS folder specified by the PR_VIEWS_ENTRYID property on this store. (This property is valid only if the FOLDER_VIEWS_ENTRYID flag is set in the PR_VALID_FOLDER_MASK property on the store.) This lets every user have a unique sticky view on a public folder common to all users. The sticky view keeps the record key of its target folder as PR_VD_VIEW_FOLDER.
When the user opens a folder, the Exchange Client first looks for a sticky view for that folder; this view will either reference another named view, or else will contain the view descriptor for the view. If no sticky view exists for the folder, Exchange will use the initial view for that folder, as defined by the folder's PR_DEFAULT_VIEW_ENTRYID property. If no such initial view exists, Exchange will use its default Normal view.
Table A-4 summarizes the schema of view descriptors.
| Name | Type | ID | Description |
| PR_MESSAGE_CLASS | PT_TSTRING | 0x001A | Distinguishes view descriptors from other messages in the associated contents table. All VDs have message class IPM.Microsoft.FolderDesign.NamedView. |
| PR_VD_BINARY | PT_BINARY | 0x7001 | All the column definitions for the view, compressed into a single property. |
| PR_VD_STRINGS | PT_TSTRING | 0x7002 | The display names for all the column definitions for the view, compressed into a single property. |
| PR_VD_FLAGS | PT_LONG | 0x7003 | Flags global to the entire view descriptor. |
| PR_VD_LINK_TO | PT_BINARY | 0x7004 | If set, the long-term entry identifier of the view descriptor to use. Used for sticky views referencing existing named views. |
| PR_VD_VIEW_FOLDER | PT_BINARY | 0x7005 | The record key of the folder to which this view applies. |
| PR_VD_NAME | PT_TSTRING | 0x7006 | The display name of this view. |
| PR_VD_VERSION | PT_LONG | 0x7007 | Version of the view descriptor schema. Must have value 8. |
| PR_VD_COLLAPSE_STATE | PT_BINARY | 0x7008 | The expand/collapse state of the view on the current folder. |
Table A-4 Schema of view descriptors. Conjoin the values in the second and third columns with the PROP_TAG macro to create a tag for each of these properties. Note that PR_MESSAGE_CLASS is standard.
Every view descriptor has the message class IPM.Microsoft.FolderDesign.NamedView, distinguishing it from other messages in the associated contents table of a folder, such as form definition messages.
The view descriptor compresses all its column definitions into a single binary property. The next section (Cracking the View Streams) calls this the binary view stream.
The view descriptor compresses all displayable strings associated with its column defintions into a single string property. The next section (Cracking the View Streams) calls this the string view stream.
Bit 0x00000010 in this flags property indicates that the view does not use its own view definition, but instead references the view definition of another view via the PR_VD_LINK_TO property. This allows a user to set a named view on a folder as a sticky view.
#define VDF_LINK 0x00000010
As of August 1996, Microsoft had not publically defined any other fields in this property. Leave them alone.
When a view has VDF_LINK set in its flags property, this property contains the long-term entry identifier of the view descriptor for the referenced named view.
Common views, personal views, and sticky views all reside in a folder other than that to which they apply. This property names the target folder for such views, specifying it as a MAPI record key.
Every view has a display name, appearing in cascades beneath the View menu and elsewhere in the user interface.
By embedding a version number in the view descriptor, the Exchange Client protects application code from damaging view descriptors created by potential future versions of Exchange. The schema described in this document, and as created by versions 4.0 and 4.1 of the Exchange Client, has version number 0x00000008.
Never read or write a view descriptor with an unrecognized version number.
For a categorized view, Exchange keeps the expand/collapse state of the viewed contents table in this property. This is the value retrieved by IMAPITable::GetCollapseState and set by IMAPITable::SetCollapseState.
The view descriptor encodes most of its information into two stream-style properties, the binary view stream (PR_VD_BINARY) and the string view stream (PR_VD_STRINGS). In order to create or manipulate a view, application code must read and write this stream format. Since these two properties are streams, application code must read and write them serially.
The first block of data in the binary view stream is the VD structure.
struct VD
{
ULONG ulReserved1
ULONG ulReserved2
ULONG ulVersion;
ULONG ulReserved3;
ULONG ulReserved4;
ULONG cvcd; // # of VCDs
ULONG ivcdSort; // VCD to sort
ULONG cCat; // # of categorized columns
ULONG ulCatSort; // Sort order for categorized columns
ULONG ulReserved5;
ULONG ulReserved6;
ULONG ulReserved7;
ULONG ulReserved8;
ULONG ulReserved9;
ULONG ulReserved10;
};
Applications must zero all reserved fields in this structure.
A version number, which must match the value in PR_VD_VERSION.
Count of view column descriptors (VCDs) in the rest of the binary view stream.
0-based index in the pending VCD sequence of the VCD defining the column to use for sorting the view.
Count of categorized columns in the view, or 0 if the view is not categorized at all.
Sort order flags for the categorized columns. The most significant bit is the sort order flag for the highest level (leftmost) grouping in the viewer; the 2nd most significant bit is the sort order for the 2nd highest level grouping, etc. The flag is set for descending sort orders and cleared for ascending sort orders.
Immediately following the VD structure in the binary stream come the VCD structures, specifying the columns in the view. The total count of VCDs appeared in the preceding VD structure.
struct VCD
{
ULONG vcds;
ULONG cx;
ULONG ulReserved1;
ULONG ulFlags;
ULONG ulReserved2;
ULONG ulReserved3;
MAPINAMEID mnid;
};
Each VCD structure appears in the stream interleaved with other information for the VCD. If the VCD specifies a MAPI named property by having VCDF_NAMEDPROP set in its flags field, the GUID for that named property will immediately follow the VCD in the binary view stream; the application reading and writing the VCD must set the subfield within the MAPINAMEID field from this value. Furthermore, if that named property has a string identifier, as noted by the ulKind field within the named property structure, the Unicode string will immediately follow the GUID in the binary view stream; again, the application must marshall and unmarshall this value as necessary to build a VCD from the data in the stream.
After reading the VCD structure from the binary view stream, an application should read the display name for that column from the accompanying string view stream.
Identifies the property that the view will display in this column. (VCDS denotes "view column descriptor selector.") For most columns this is simply the property tag of the desired property; however, PROP_TAG( PT_NULL, 0x0004) encodes a blank column, and PROP_TAG( PT_NULL, 0x0005) the action-icon column used in remote viewers. Table A-5 lists common VCDS values, together with the view column descriptor flags that Exchange applies by default to each value.
Column width, specified in characters or pixels. If the VCD has the flag VCDF_BITMAP set, the width is considered in pixels.
Always specify widths in characters wherever possible. This makes views less dependent on the fonts installed on a particular client workstation.
View column descriptor flags, as listed in Table A-6.
As with the VD structure, applications should zero reserved fields.
| View column descriptor selector | Default cx | Default flags |
| PROP_TAG( PT_NULL, 0x0004) (Blank "drag column," must be first) | 7 | BITMAP, NO_CUSTOM, NOT_SORTABLE |
| PROP_TAG( PT_NULL, 0x0005) (Action icon) | 20 | BITMAP, CENTER_JUSTIFY, SORTDESCENDING, SORTDLG, RCOLUMNSDLG |
| PR_IMPORTANCE | 10 | BITMAP, CENTER_JUSTIFY, SORTDESCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_SENSITIVITY | 20 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_MESSAGE_CLASS | 20 | BITMAP, CENTER_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_HASATTACH | 13 | BITMAP, CENTER_JUSTIFY, SORTDESCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_SENT_REPRESENTING_NAME ("Sender") | 23 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, RCOLUMNSDLG, SORTDLG, GROUPDLG |
| PR_DISPLAY_TO | 20 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_SUBJECT | 39 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_MESSAGE_DELIVERY_TIME ("Received") | 26 | LEFT_JUSTIFY, SORTDESCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_CLIENT_SUBMIT_TIME ("Submit time") | 26 | LEFT_JUSTIFY, SORTDESCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG |
| PR_MESSAGE_SIZE | 9 | RIGHT_JUSTIFY, SORTDESCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG, RCOLUMNSDLG |
| PR_DISPLAY_NAME | 60 | LEFT_JUSTIFY, SORTASCENDING |
| PR_MESSAGE_DOWNLOAD_TIME ("Count time") | 18 | RIGHT_JUSTIFY, SORTDESCENDING, SORTDLG, RCOLUMNSDLG |
| PR_BODY | 20 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG |
| PR_PARENT_DISPLAY ("In Folder" in search viewers) | 20 | LEFT_JUSTIFY, SORTASCENDING, MOVEABLE, COLUMNSDLG, SORTDLG, GROUPDLG |
| PR_CONVERSATION_TOPIC | 20 | LEFT_JUSTIFY, SORTASCENDING,MOVEABLE, COLUMNSDLG, SORTDLG,GROUPDLG |
| PR_CONVERSATION_INDEX | 1 | BITMAP, NO_CUSTOM, SORTDLG |
Table A-5. Some common view column descriptor selectors and their default attributes.
| Flag | Value | Meaning |
| VCDF_BITMAP | 0x00000008 | Set when the column width value is measured in pixels. |
| VCDF_NOT_SORTABLE | 0x00000020 | Set if the column cannot specify a sort. |
| VCDF_SORTDESCENDING | 0x00000040 | Set for a column whose default is to sort in descending order. When clear, the default is ascending order. |
| VCDF_MOVABLE | 0x00000100 | Set if the column can be moved relative to other columns in the view. |
| VCDF_COLUMNSDLG | 0x00000200 | Set when the column is applicable to the column dialog. |
| VCDF_SORTDLG | 0x00000400 | Set when the column is applicable to the sort dialog. |
| VCDF_GROUPDLG | 0x00000800 | Set when the column is applicable to the group dialog. |
| VCDF_NAMEDPROP | 0x00001000 | Set when mnid contains a named MAPI property to be displayed in the column. This also requires reading the named property information from the binary view stream. |
| VCDF_RCOLUMNSDLG | 0x00002000 | Set when the column is applicable to the remote columns dialog. |
| VCDF_MULTIVALUED | 0x00004000 | Set when the column contains a multivalued property. |
Table A-6 View column descriptor flags
Following all VCD definitions in the binary and string view streams, the view specifies the restriction to apply to the rows of the contents table. A restriction is specified as a MAPI SRestriction structure, flattened onto the two streams of the view. Reading a restriction is a recursive operation, as befits the recursive nature of a SRestriction. The topmost node in the restriction always has type RES_COMMENT.
To read a node of the restriction, read sizeof(SRestriction) bytes off of the binary view stream. The value found in restriction type field rt will specify what subsequent reads to make in order to unmarshall the restriction structure completely.
To read a set of properties of the restriction, first read a ULONG off of the binary view stream. This specifies the count of the number of properties to read. Next, read a vector of that many SPropValue structures off of the binary view stream. For each entry in the resulting vector, the property value type field will specify what subsequent reads to make in order to unmarshall the property structure completely.
Check the Microsoft FTP server for more information on
internal Microsoft Exchange formats: ftp://ftp.microsoft.com/developr/MAPI.
My WWW site may also contain additional samples and information: http://www.angrygraycat.com/goetter/book/.
Originally written: 13 August 1996
Published on WWW: 3 October 1996
Last modified: 13 August 1998
Copyright 1996-1998 Ben Goetter. All rights reserved.