Bodu.Formats.Outlook Namespace
- Packages
-
Bodu.Formats.Outlook 0.7.1Bodu.Formats.Outlook.Msg 0.7.1Bodu.Formats.Outlook.Pst 0.7.1
Purpose
Bodu.Formats.Outlook is the single, flattened namespace shared by three packages: the container-free MAPI value model (Bodu.Formats.Outlook) and the two read-only readers built on it - Bodu.Formats.Outlook.Msg, which opens one message (.msg / MS-OXMSG) over the Bodu.IO.Compound OLE2 container, and Bodu.Formats.Outlook.Pst, which opens a personal-folders mail store (.pst / MS-PST, Unicode and ANSI formats) over the Bodu.IO.Pst node database. The package names keep their suffixes; the public types do not - a .msg recipient and a .pst recipient are the same OutlookRecipient over the same MapiPropertyCollection, and code written against one reader's property surface carries over to the other. The container-specific decoders stay internal under Bodu.Formats.Outlook.Msg and Bodu.Formats.Outlook.Pst.
Both readers open a disposable session that owns the container, decode every MAPI property into the shared model, and layer curated conveniences (subject, sender, time stamps, recipients, attachments, the text / HTML / compressed-RTF bodies, named-property resolution) over the raw tag-addressed surface. Neither writes, emulates a MAPI session, or de-encapsulates RTF.
Static documentation
- Introduction - the three packages, the "which package do I need" table, and the headline types.
- Core concepts - property-tag anatomy, named-property resolution, recipients and attachment methods, code pages, compressed RTF, validation levels, and the resource limits.
- Getting started - install and minimal samples for both readers.
- Reading
.msgfiles - opening a message, the conveniences, recipients, attachments, nested messages. - Properties and named properties - the raw property surface, tags and wire types, resolving named properties.
- Bodu.IO.Pst introduction and the PST runnable sample - the container beneath the mail-store reader.
- Binary Formats & I/O topic overview - the readers alongside their containers.
Key types
Value model - Bodu.Formats.Outlook
- MapiPropertyTag - the 32-bit tag (MS-OXCDATA §2.11.4):
Idin the high word, baseTypein the low word with the0x1000multi-valued flag stripped,IsMultiValued, andIsNamed(identifier ≥0x8000); constructed from(id, type)or a rawuint, or viaForMultiValue(id, elementType). - MapiPropertyType - the 16-bit wire-type codes:
Int16/Int32/Int64,Float/Double,Currency,AppTime,ErrorCode,Boolean,Object,String8(code-paged) /Unicode,SystemTime,Guid,Binary, plusUnspecified/Null. - MapiProperty - one decoded property:
Tagand a CLRValue(arrays for multi-valued properties). - MapiPropertyCollection - the tag-addressed, first-occurrence-ordered bag on every object.
TryGetValueby tag or by identifier,GetAll(id),Contains(tag),Count, the sharedEmpty, and the typed accessors that probe the plausible wire types and returnnullwhen absent:GetString,GetInt32,GetInt64,GetBoolean,GetDouble,GetDateTime,GetGuid,GetBinary,GetStringArray. - MapiNamedProperty - the durable identity behind a named identifier: a property-set GUID with either a numeric
Idor a stringName. - MapiPropertyIds - the curated
PidTag*identifiers the conveniences use (Subject,SenderName,ClientSubmitTime,Body,Html,RtfCompressed,AttachMethod,AttachLongFilename,MessageCodepage,ContainerClass, …); any property is reachable by raw identifier regardless. - OutlookRecipient - one recipient:
RecipientType,DisplayName,EmailAddress,AddressType, and itsProperties. OutlookRecipientType -Originator/To/Cc/Bcc. - OutlookAttachmentMethod -
None/ByValue/ByReference/ByReferenceResolve/ByReferenceOnly/EmbeddedMessage/Ole.
Message session - Bodu.Formats.Outlook.Msg
- OutlookMessage - the disposable
.msgsession. FactoriesOpenRead(path /Stream) andOpen(Stream, options, leaveOpen), the cheapIsMsgFileprobe;Properties; the conveniencesSubject(prefix marker stripped),SenderName,SenderEmailAddress,MessageClass,InternetMessageId,TransportMessageHeaders,SentTime,ReceivedTime;RecipientsandAttachments(lazy, storage-index order);BodyText/BodyHtml/BodyRtf;TryGetNamedPropertyId/TryGetPropertyNameover the__nameid_version1.0mapping shared with nested messages;EmbeddedDepth. - OutlookAttachment - one attachment:
Method(inferred asByValuewhen a payload exists but the property is absent),FileName,ContentId,MimeTag,Size;OpenContentStream()for a by-value payload,OpenMessage()for an embedded message (a nested session sharing the root's container - disposing it is a no-op).
Mail-store session - Bodu.Formats.Outlook.Pst
- OutlookMailStore - the disposable
.pstsession. FactoriesOpenRead(path /Stream) andOpen(Stream, options, leaveOpen), theIsPstFileprobe;Properties,DisplayName,RootFolder; the store-wideTryGetNamedPropertyId/TryGetPropertyNameover the name-to-id map node. Single-threaded; every view is bound to the session's lifetime. - OutlookMailFolder - one folder:
DisplayName,ContainerClass, the declaredMessageCount/UnreadCount,HasSubfolders; the streamingEnumerateSubfolders(search folders excluded),EnumerateMessages, andEnumerateAssociatedMessages. - OutlookMailMessage - one message: the same convenience surface as
OutlookMessage(Subject,SenderName,MessageClass,InternetMessageId,TransportMessageHeaders, the time stamps),Recipients(recipient-table order),Attachments, the three bodies,EmbeddedDepth. - OutlookMailAttachment - one attachment with the same surface as
OutlookAttachment;OpenContentStream()streams a deferred payload block by block,OpenMessage()returns the embeddedOutlookMailMessagewith code-page inheritance.
Options
- OutlookMessageReaderOptions -
ValidationLevel(CompoundValidationLevel,Compatible),ReadStrategy(CompoundReadStrategy,Buffered),DecompressRtf(true),MaxEmbeddedMessageDepth(16),MaxDecompressedRtfBytes(64 MiB),MaxInlineAttachmentBytes(1 MiB). - OutlookMailStoreReaderOptions -
ValidationLevel(PstValidationLevel,Compatible),BlockCacheSize(256;0disables),DecompressRtf(true),MaxNodeDataLength(256 MiB),MaxEmbeddedMessageDepth(16),MaxDecompressedRtfBytes(64 MiB),MaxInlineAttachmentBytes(1 MiB).
Errors
- OutlookFormatException - the family base; catch it to handle both formats.
- OutlookMsgFormatException - not a compound file, a malformed container (the CompoundFileFormatException is wrapped as
InnerException), an MS-OXMSG violation under strict validation, or a tripped depth / RTF limit. - OutlookPstFormatException - a messaging-level MS-PST violation (an invalid table row under strict validation, a malformed name-to-id map, a tripped limit). Container corruption is not wrapped: it propagates as the PstFileException family, and a failed open throws PstFileFormatException or PstUnsupportedFormatException directly.
Example
using Bodu.Formats.Outlook;
// Value model: address a property by tag, or by identifier through a typed accessor.
var subjectTag = new MapiPropertyTag(MapiPropertyIds.Subject, MapiPropertyType.Unicode);
Console.WriteLine($"{subjectTag} named={subjectTag.IsNamed} multi={subjectTag.IsMultiValued}"); // 0x0037001F named=False multi=False
using Bodu.Formats.Outlook;
// .msg: open, read the conveniences, save the by-value attachments.
using var message = OutlookMessage.OpenRead("invoice.msg");
Console.WriteLine($"{message.Subject} - {message.SenderName} ({message.SentTime:u})");
foreach (OutlookAttachment attachment in message.Attachments)
{
if (attachment.Method != OutlookAttachmentMethod.ByValue)
continue;
using Stream content = attachment.OpenContentStream();
using FileStream target = File.Create(attachment.FileName ?? "attachment.bin");
content.CopyTo(target);
}
using Bodu.Formats.Outlook;
// .pst: walk the folder hierarchy and resolve a named property store-wide.
using var store = OutlookMailStore.OpenRead("archive.pst");
var keywords = new MapiNamedProperty(new Guid("00020329-0000-0000-C000-000000000046"), "Keywords");
store.TryGetNamedPropertyId(keywords, out ushort keywordsId);
foreach (OutlookMailFolder folder in store.RootFolder.EnumerateSubfolders())
{
foreach (OutlookMailMessage item in folder.EnumerateMessages())
{
string[] categories = item.Properties.GetStringArray(keywordsId) ?? Array.Empty<string>();
Console.WriteLine($"{folder.DisplayName}: {item.Subject} [{string.Join(", ", categories)}]");
}
}
using Bodu.Formats.Outlook;
using Bodu.IO.Pst;
// Options and errors: strict validation, tighter limits, and the two failure surfaces.
var options = new OutlookMailStoreReaderOptions
{
ValidationLevel = PstValidationLevel.Strict,
MaxEmbeddedMessageDepth = 4,
MaxInlineAttachmentBytes = 256 * 1024,
};
try
{
using var store = OutlookMailStore.Open(File.OpenRead("suspect.pst"), options);
Console.WriteLine(store.DisplayName);
}
catch (PstFileException ex) { Console.WriteLine($"container: {ex.Error}"); }
catch (OutlookPstFormatException ex) { Console.WriteLine($"messaging: {ex.Message}"); }
Notes
- One namespace, three packages. Install
Bodu.Formats.Outlook.MsgorBodu.Formats.Outlook.Pstfor a format; both referenceBodu.Formats.Outlooktransitively. Reference the model package alone from a library that only handles properties. - Conveniences never throw for absence.
Subject,SenderName, the time stamps,FileName, and the rest returnnullwhen the property is missing; the typedGet*accessors do the same. Only structural violations and tripped limits raise the format exceptions. - Code pages.
String8properties decode through the object's message code page, then its internet code page, then Windows-1252, with child objects inheriting; the readers registerCodePagesEncodingProviderso Windows code pages resolve on every platform. - Compressed RTF is bounded.
BodyRtfdecompressesPidTagRtfCompressed(MS-OXRTFCP) on first access; the declared size lies outside the payload's checksum, soMaxDecompressedRtfBytesis enforced before allocation at every validation level. SetDecompressRtf = falseto keep the raw payload only. - Large attachments stream. A by-value payload above
MaxInlineAttachmentBytesis left in the container and served only throughOpenContentStream()- in the.msgreader sector by sector underCompoundReadStrategy.Streaming, in the.pstreader block by block always. - Two error styles. The
.msgreader wraps container failures in OutlookMsgFormatException; the.pstreader lets PstFileException propagate and reserves OutlookPstFormatException for messaging-level violations. Catch OutlookFormatException for the family and the container family beneath it. - See also: the introduction, core concepts, and getting started; the Bodu.Formats.Outlook guides; and the containers Bodu.IO.Compound and Bodu.IO.Pst.
Classes
- MapiProperty
Represents a single decoded MAPI property: a MapiPropertyTag paired with its CLR value.
- MapiPropertyCollection
Provides a read-only, tag-addressed collection of decoded MAPI properties with typed convenience accessors.
- MapiPropertyIds
Provides curated well-known MAPI property identifiers (the
PidTag*constants of MS-OXPROPS).
- OutlookAttachment
Represents one attachment of an OutlookMessage: a typed view over the attachment storage's decoded properties, with access to the by-value payload or the nested attached message.
- OutlookFormatException
Represents an error raised when Outlook-format content is structurally invalid or cannot be decoded.
- OutlookMailAttachment
Represents one attachment of an OutlookMailMessage: a typed view over the attachment object's decoded properties, with access to the by-value payload or the nested attached message.
- OutlookMailFolder
Represents one folder of a mail store: its decoded properties and streaming access to its subfolders and messages.
- OutlookMailMessage
Represents one message of a mail store: its decoded properties with typed conveniences over the well-known scalars.
- OutlookMailStore
Provides a disposable, read-only session over an Outlook personal-folders mail store (a
.pstfile, MS-PST Unicode or ANSI format): the store properties, the folder hierarchy, and the messages within it, decoded into the shared MAPI value model.
- OutlookMailStoreReaderOptions
Controls how an OutlookMailStore reads its PST container and decodes message content.
- OutlookMessage
Provides a disposable, read-only session over an Outlook message (
.msg/ MS-OXMSG), exposing every decoded MAPI property together with curated conveniences for the common message fields.
- OutlookMessageReaderOptions
Controls how an OutlookMessage opens its container and handles malformed content.
- OutlookMsgFormatException
Represents an error raised when a
.msgfile is structurally invalid or violates the MS-OXMSG format.
- OutlookPstFormatException
Represents a violation of the Outlook personal-folders messaging conventions (MS-PST) while reading a mail store.
- OutlookRecipient
Represents one recipient of a message: a typed view over the recipient's decoded properties.
Structs
- MapiNamedProperty
Represents the identity of a MAPI named property: a property-set GUID combined with either a numeric identifier or a string name.
- MapiPropertyTag
Represents a 32-bit MAPI property tag: a 16-bit property identifier in the high word and a 16-bit property type in the low word.
Enums
- MapiPropertyType
Specifies the 16-bit MAPI property type codes (the
PT_*constants) carried in the low word of a MapiPropertyTag.
- OutlookAttachmentMethod
Specifies how an attachment's content is stored or referenced (the
PidTagAttachMethodvalues).
- OutlookRecipientType
Specifies how a recipient participates in a message (the
PidTagRecipientTypevalues).