Click here to Skip to main content
Click here to Skip to main content

Reading an Outlook MSG File in C#

By , 8 Jul 2010
 
Demo application form

Introduction

This article is going to focus on how to dissect a msg file generated by Outlook. It covers how to read the basic properties of the mail message, attachments and any msg attachments (these need to be handled differently).

Using the Code

The code is pretty simple to use. You construct a new instance of the OutlookStorage.Message class, sending it the path to a msg file or a Stream containing an IStorage. The Stream constructor is provided so that it is easy to integrate with the Outlook drag and drop code in another of my articles and this is shown in the demo application.

private static void main()
{
    //create new Outlook message from file
    OutlookStorage.Message outlookMsg = new OutlookStorage.Message(@"C:\test.msg");
    DisplayMessage(outlookMsg);
}

private static void DisplayMessage(OutlookStorage.Message outlookMsg)
{    
    Console.WriteLine("Subject: {0}", outlookMsg.Subject);
    Console.WriteLine("Body: {0}", outlookMsg.BodyText);
    
    Console.WriteLine("{0} Recipients", outlookMsg.Recipients.Count);    
    foreach (OutlookStorage.Recipient recip in outlookMsg.Recipients)
    {
        Console.WriteLine(" {0}:{1}", recip.Type, recip.Email);
    }
    
    Console.WriteLine("{0} Attachments", outlookMsg.Attachments.Count);
    foreach (OutlookStorage.Attachment attach in outlookMsg.Attachments)
    {
        Console.WriteLine(" {0}, {1}b", attach.Filename, attach.Data.Length);
    }

    Console.WriteLine("{0} Messages", outlookMsg.Messages.Count);
    foreach (OutlookStorage.Message subMessage in outlookMsg.Messages)
    {
        DisplayMessage(subMessage);
    }
}

Save a Msg and All Attachments to the File System

This is an example on how to save a message and all associated attachments to the application path.

private static void main()
{
    //create new Outlook message from file
    OutlookStorage.Message outlookMsg = new OutlookStorage.Message(@"C:\test.msg");
}

private static void SaveMessage(OutlookStorage.Message outlookMsg)
{    
    outlookMsg.Save(outlookMsg.Subject.Replace(":", ""));    

    foreach (OutlookStorage.Attachment attach in outlookMsg.Attachments)
    {
        byte[] attachBytes = attach.Data;
        FileStream attachStream = File.Create(attach.Filename);
        attachStream.Write(attachBytes, 0, attachBytes.Length);
        attachStream.Close();
    }

    foreach (OutlookStorage.Message subMessage in outlookMsg.Messages)
    {
        SaveMessage(subMessage);
    }
}

Understanding the Code

To read the msg file produced by Outlook, there are two concepts to understand. The first is that an msg file is logically a MAPI object with MAPI properties and the second is that phyiscally the MAPI object and its properties are stored in an IStorage. Microsoft has kindly provided a specification on how the MAPI properties are mapped to the IStorage, so at this point I will defer to that and just go over the catches that popped up when figuring out how to save a sub message out of its parent.

Saving a Sub Message

Saving a sub message out of the parent message has a few catches. The property stream header needs to be padded and the name to id mapping storage needs to be copied to the sub message storage.

Fixing the Property Stream

MAPI property values can be stored in a sub storage, a sub stream or in the case of fixed size values (like an integer) a special sub stream called the property stream. The property stream consists of a variable length header and then an array of 16 byte pairs with a property identifier in the first 8 bytes and the property value in the second 8.

It is the variable length header that you should take note of. It is 8 bytes for an attachment or recipient storage, 32 bytes for a top level msg and 24 bytes for a sub msg. This means that if you want to extract a sub message and save it without its parent you need to pad the end of the header with 8 null bytes.

The Name to Id Mapping

The other catch for saving a sub message is the name to id mapping storage which only exists on the top level msg, but contains the mappings for the entire tree. So when saving a sub message, this storage needs to be copied to it before saving for it to be valid.

Conclusion

Everything is encapsulated in the OutlookStorage.cs file as I don't like to release stuff with dependencies and prefer to just be able to drop a CS into my projects to get a particular piece of functionality. There is a region in there under a separate licence for the code to decompress the compressed RTF, but it is clearly marked.

History

  • 8th July, 2010
    • Fixed memory leak
    • Fixed the "COM object that has been separated from its underlying RCW cannot be used" exception
    • Added better determination of attachment file name
  • 28th January, 2009: Original article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

David Ewen
Software Developer (Senior) Beacon
Australia Australia
Member
I am currently working for Beacon Technology as a Senior Software Developer on WPF/WCF applications and have been working in the industry for 8 years. My day job usually involves programming with C# but I have been known to mess around with just about everything.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionEncoding Problemmemberarvelius1 May '13 - 22:44 
QuestionAttached vs EmbeddedmemberSharpi90098 Feb '13 - 5:33 
AnswerRe: Attached vs Embeddedmemberfryezz27 Mar '13 - 6:44 
QuestionHow to Open the signed ,msg filememberV G S Naidu A20 Jan '13 - 19:25 
QuestionHow to display images into RichTextBox from Outlook message file(.MSG)?memberRameshMorasa30 Aug '12 - 21:41 
GeneralMy vote of 5memberJustDeveloper31 Jul '12 - 5:11 
QuestionPls add a new Revision with SendermemberDePaule4 Jul '12 - 23:08 
QuestionHow to i make this reverse?memberphantomlakshan17 May '12 - 4:56 
QuestionReply-tomemberjanipiombo8 May '12 - 8:57 
AnswerRe: Reply-tomemberziotullio14 Sep '12 - 21:41 
Questionopen attachment on double clickmemberSaurabhSavaliya26 Apr '12 - 23:37 
QuestionEncoding problem when part of a WCF methodmemberzaf23 Apr '12 - 1:02 
QuestionDisposing fails on wrong filesmemberlacroix22 Mar '12 - 9:25 
GeneralMy vote of 5memberProEnggSoft22 Mar '12 - 6:29 
QuestionInvalid Cast in OleCompound Files AttachmentsmemberMember 805282315 Mar '12 - 14:28 
GeneralMy vote of 5memberchris obi9 Mar '12 - 1:24 
QuestionMessage details just from attachmentmemberchris obi9 Mar '12 - 1:22 
QuestionVote of 5memberchris obi23 Feb '12 - 5:04 
QuestionHow to get the date?memberstan9210 Jan '12 - 19:55 
AnswerRe: How to get the date?memberMember 937670823 Aug '12 - 7:51 
GeneralRe: How to get the date?memberJames H14 Sep '12 - 2:50 
GeneralRe: How to get the date?memberGregg Katz17 Apr '13 - 4:56 
QuestionProperty values 3007 & 3008memberStef_H31 Dec '11 - 3:42 
AnswerRe: Property values 3007 & 3008memberMember 937670823 Aug '12 - 7:45 
SuggestionMessage.FromAddress propertymemberxeondev11 Dec '11 - 23:37 
QuestionExtract top thread from a messagememberkpant12 Oct '11 - 21:51 
Questionhow so create an MSGmemberRussGreen13 Aug '11 - 2:50 
AnswerRe: how so create an MSGmemberkevinmrkr11 Nov '11 - 3:39 
AnswerRe: how so create an MSGmemberUsman Sarfraz7 Dec '11 - 20:44 
GeneralMy vote of 5memberMichael Hachen3 Aug '11 - 4:08 
QuestionHow to print .msg file without outlook?memberMember 175651527 Jun '11 - 2:55 
QuestionHow to convert an eml message to outlook message?memberMember 175651527 Jun '11 - 2:44 
QuestionThis saved my life- awesome dudememberMember 233428824 Jun '11 - 5:07 
GeneralHandle embedded images in RTF messagememberpkumarra26 May '11 - 22:50 
GeneralMy vote of 5memberPhilippe Devaux19 May '11 - 23:59 
GeneralHow to save attachments from msg file?memberNareshMacha16 May '11 - 10:30 
GeneralRe: How to save attachments from msg file?memberMember 98864056 Mar '13 - 13:22 
GeneralGood Projectmemberhex7050320 Apr '11 - 5:13 
GeneralMissing RecipientmemberXavier Laurent12 Apr '11 - 5:09 
GeneralRe: Missing RecipientmemberShishir Mundra5 May '11 - 20:05 
GeneralPR_REPLY_TIME [modified]memberensiferum8885 Apr '11 - 5:23 
GeneralMy vote of 5memberMartin Welker18 Mar '11 - 5:41 
QuestionHow to get mail adress from exchange server mail?memberMarkuss2117 Mar '11 - 5:47 
GeneralPriority and Received Datememberikalyan15 Mar '11 - 5:09 
QuestionImages / DisplayNamemembercyntonix8 Mar '11 - 2:29 
GeneralInvalidCastException extracting attachment from MSG file [modified]membermgrack1 Mar '11 - 17:20 
GeneralModifing the subject of emailmemberhugetool7 Feb '11 - 0:16 
GeneralRe: Modifing the subject of emailmemberDavid Ewen8 Feb '11 - 18:52 
GeneralRe: Modifing the subject of emailmemberhugetool9 Feb '11 - 21:15 
QuestionHow to read further Mapi properties?memberMarkuss214 Feb '11 - 1:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 8 Jul 2010
Article Copyright 2009 by David Ewen
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid