在 C# 中读取 Outlook MSG 文件






4.88/5 (110投票s)
如何在不使用 Outlook 对象模型的情况下在 C# 中读取 Outlook msg 文件。

引言
本文将重点介绍如何剖析 Outlook 生成的 msg 文件。它涵盖了如何读取邮件的基本属性、附件以及任何 msg 附件(这些需要以不同的方式处理)。
Using the Code
代码非常易于使用。您构造 OutlookStorage.Message
类的一个新实例,并向其发送 msg 文件的路径或包含 IStorage
的 Stream
。 提供了 Stream
构造函数,以便可以轻松地与我另一篇文章中的 Outlook 拖放代码集成,这在演示应用程序中有所展示。
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);
}
}
将 Msg 和所有附件保存到文件系统
这是一个关于如何将邮件和所有相关附件保存到应用程序路径的示例。
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);
}
}
理解代码
要读取 Outlook 生成的 msg 文件,需要理解两个概念。第一个是 msg 文件在逻辑上是一个具有 MAPI 属性的 MAPI 对象,第二个是物理上 MAPI 对象及其属性存储在 IStorage 中。 微软已经提供了关于如何将 MAPI 属性映射到 IStorage 的 规范,因此在这一点上我将参考该规范,并仅讨论在弄清楚如何从其父级保存子消息时弹出的问题。
保存子消息
从父消息中保存子消息有一些问题。 属性流标头需要填充,并且名称到 ID 映射存储需要复制到子消息存储中。
修复属性流
MAPI 属性值可以存储在子存储、子流或固定大小的值(如整数)的情况下,存储在名为属性流的特殊子流中。 属性流由一个可变长度的标头和一个由 16 字节对组成的数组组成,其中前 8 个字节是属性标识符,后 8 个字节是属性值。
您应该注意可变长度的标头。 对于附件或收件人存储,它是 8 个字节,对于顶级 msg,它是 32 个字节,对于子 msg,它是 24 个字节。 这意味着如果您想提取一个子消息并将其保存而无需其父消息,则需要用 8 个空字节填充标头的末尾。
名称到 ID 的映射
保存子消息的另一个问题是名称到 ID 映射存储,它仅存在于顶级 msg 上,但包含整个树的映射。 因此,在保存子消息时,需要将其存储复制到其中才能有效。
结论
所有内容都封装在 OutlookStorage.cs 文件中,因为我不喜欢发布带有依赖项的内容,并且更喜欢能够将 CS 放入我的项目中以获得特定功能。 其中有一个区域,在单独的许可证下用于解压缩压缩的 RTF 代码,但它已明确标记。
历史
- 2010 年 7 月 8 日
- 修复了内存泄漏
- 修复了 "COM 对象已与其底层 RCW 分离,无法使用" 异常
- 添加了对附件文件名的更好确定
- 2009 年 1 月 28 日:原始文章