MAPIEx:扩展 MAPI 包装器






4.93/5 (134投票s)
一个(希望)完整的扩展 MAPI 包装器,适用于 WinXP、WinCE 和 .NET
引言
我多年来一直在使用简单的 MAPI,最近,我需要以编程方式发送一些邮件,这才想起了可怕的 Outlook 安全警告。因此,我决定研究一下扩展 MAPI。我发现有大量的示例,它们可以完成某一项或另一项功能,但没有一个真正能满足我需要的与邮件相关的所有功能。这个类,希望能够涵盖普通用户需要完成的所有事情,包括发送和接收邮件(带附件、多个收件人、抄送、密送等)。我没有添加任何用于操作信息存储或服务(information stores or services)的代码。请参阅 CodeProject 上关于此功能的示例 CMapiAdmin
。我还使用 Unicode 和 ANSI 字符串测试了这个类。
使用 CMapiEx
我包含了示例 TestMAPI
(链接在上文)来演示两种情况。该项目已更新到 Visual Studio 8 (2005),但我相信它可以适应几乎所有版本的 Visual Studio 和 Embedded Visual Studio。它已在 WinXP 上使用 Office 2003 进行测试,也在 Windows Mobile 2003 上进行测试。只需稍作修改,移除类中的 MFC 代码(基本上就是 CString
),该类就可以在 Windows SmartPhone 上运行。一些较新的代码,包括 Outlook Forms 支持和 AddressBook
支持,在 Windows Mobile 中不受支持。
要使用该类,只需执行以下操作:
- 调用
CMAPIEx::Init()
和Login()
来初始化和登录 MAPI。 - 使用
OpenMessageStore()
打开所需的存储,例如,“SMS”(NULL
表示默认存储)。 - 要接收邮件,请调用
OpenInbox()
和GetContents()
,然后为每封邮件循环调用GetNextMessage()
(可以选择只读取未读邮件)。 - 使用
HasAttachment
和SaveAttachments()
来访问邮件的附件。
void ReceiveTest(CMAPIEx& mapi)
{
if(mapi.OpenInbox() && mapi.GetContents()) {
CMAPIMessage message;
while(mapi.GetNextMessage(message,TRUE)) {
printf("Message from '%s' subject '%s'\n",
message.GetSenderName(),message.GetSubject());
if(message.HasAttachments()) {
printf("saving attachments...");
message.SaveAttachments(MSG_ATTACHMENT_FOLDER);
printf("done\n");
}
}
}
}
要发送邮件,请调用 OpenOutbox()
和 CMapiMessage::Create()
。然后,设置邮件属性,再调用 CMapiMessage::Send()
。
void SendTest(CMAPIEx& mapi)
{
if(mapi.OpenOutbox()) {
CMAPIMessage message;
if(message.Create(&mapi,IMPORTANCE_LOW)) {
message.SetSenderName(FROM_NAME);
message.SetSenderEmail(FROM_EMAIL);
message.SetSubject(_T("Subject"));
message.SetBody(_T("Body"));
message.AddRecipient(TO_EMAIL);
message.AddRecipient(TO_EMAIL2);
message.AddAttachment(MSG_ATTACHMENT);
if(message.Send()) printf("Sent Successfully");
}
}
}
使用 Logout()
和 CMAPIEx::Term()
来退出 MAPI。很明显,如果您想在 Unicode 项目中使用 CMAPIEx
,您将不得不改用 wprintf
。
RTF/HTML 支持
很多人要求支持 HTML 邮件。经过大量的研究和测试,我发现 HTML 通常存储在 PR_RTF_COMPRESSED
属性中,并且如果它包含文本 \\fromhtml
,则可以解码为 HTML。感谢 Lucian Wischik 的示例,MAPIEx 中的解码代码基本是从他的示例中提取的(请参阅源代码以获取详细地址)。我更进一步,允许您使用 HTML 设置 RTF
属性,以便发送 HTML 电子邮件。请查看上方示例 TestMAPI 项目中的 SetRTF 示例,了解更多详情。
CMAPIEx 的 .NET 包装器
我最初编写 NetMAPI
包装器是作为概念验证的;它除了基本功能外,几乎没有其他功能。然而,在过去的几个月里,我收到了大量关于 .NET 的问题和增强请求,所以我已完全重写了 NetMAPI
,使其成为 Win32 DLL CMAPIEx
之上的一个薄层。NetMAPI
支持 C++ 接口中提供的几乎所有功能。上面包含的示例包含一个 .NET 控制台项目 TestNetMAPI
,演示了其用法。
要使用该包装器,首先请确保您的程序已引用 NetMAPI
,并且编译好的 MAPIEx.dll 位于您的输出文件夹或您的路径中。然后,使用 NetMAPI.Init()
和 Login
函数进行访问。
if(NetMAPI.Init()) {
NetMAPI mapi=new NetMAPI();
if(mapi.Login()) {
// Do some MAPI stuff here...
mapi.Logout();
}
NetMAPI.Term();
}
在 .NET 中接收邮件(假设您已成功登录)
- 打开您要访问的消息存储
- 然后打开收件箱并获取内容表
- 使用
GetNextMessage
迭代消息 - 完成后别忘了
Dispose
消息!
public static void ReceiveTest(NetMAPI mapi)
{
if(mapi.OpenInbox() && mapi.GetContents()) {
mapi.SortContents(false);
MAPIMessage message;
StringBuilder s=new StringBuilder(NetMAPI.DefaultBufferSize);
while(mapi.GetNextMessage(out message,true)) {
Console.Write("Message from '");
message.GetSenderName(s);
Console.Write(s.ToString()+"' (");
message.GetSenderEmail(s);
Console.Write(s.ToString()+"), subject '");
message.GetSubject(s);
Console.Write(s.ToString()+"', received: ");
message.GetReceivedTime(s,"%m/%d/%Y %I:%M %p");
Console.Write(s.ToString()+"\n");
// use message.GetBody() to get the ANSI text body
// use message.GetRTF() to get the RTF or decoded HTML email
message.Dispose();
}
}
}
发送邮件(假设您已成功登录)
- 打开您要访问的消息存储
- 打开发件箱
- 创建一个新消息,可以设置其优先级
- 设置其属性、收件人和附件
- 调用
Send
public static void SendTest(NetMAPI mapi)
{
if(mapi.OpenOutbox()) {
MAPIMessage message=new MAPIMessage();
if(message.Create(mapi,MAPIMessage.Priority.IMPORTANCE_LOW)) {
message.SetSenderName("Noel");
message.SetSenderEmail("noel@nospam.com");
message.SetSubject("Subject");
// user SetBody for ANSI text, SetRTF for HTML and Rich Text
message.SetBody("Body");
message.AddRecipient("noel@nospam.com");
message.AddRecipient("noel@nospam2.com");
if(message.Send()) Console.WriteLine("Sent Successfully");
}
}
}
请参阅 TestNetMAPI
中的 ContactsTest
和 FoldersTest
函数以获取更多示例。
使用 Logout
和 NetMAPI.Term()
调用来关闭包装器。要使用 Unicode,请将 CMAPIEx
编译为 Unicode,并将 NetMAPI
中的 DefaultCharSet
变量更改为 CharSet.Unicode
。
未来改进
我想添加对日历和任务项的更多支持。Outlook 2010 支持是另一个即将推出的功能,目前尚未进行任何测试。
我无法弄清楚如何在 Windows Mobile 中创建一次性 EntryID
,因为 IAddressBook
接口未实现。这有一个令人讨厌的影响,即直到您手动打开发件箱中的邮件并点击发送,否则电子邮件将不会在这些设备上发送。如果有人知道如何做到这一点,请告诉我,我会进行更新。
此代码可以免费使用,前提是版权声明保留在文件顶部,并且任何增强功能或错误修复都会在此处发布给社区。我希望它能帮助一些人跳过我所经历的痛苦!
历史
- 2005 年 7 月 1 日 - 初始发布
- 2005 年 7 月 13 日 - 进行了小的错误修复和添加,详情请参见下文
- 2005 年 8 月 25 日 - 进行了小的修改和一个 .NET 包装器(见下文)
- 2006 年 1 月 27 日 - 添加了大量用于处理文件夹和消息的新命令(见下文)
- 2006 年 5 月 16 日 - 添加了 RTF 和 HTML 支持
- 2006 年 6 月 2 日 - 添加了外部文件夹支持,并修复了几个小错误(感谢所有报告问题的人)
- 2006 年 8 月 21 日 - 重新组织了文件,添加了读取联系人、通知的支持,并修复了一些小错误
- 2006 年 10 月 2 日 - .NET 包装器完全重写,提供从 .NET 到 MAPIEx 的全面访问
- 2006 年 11 月 1 日 - 添加了对许多新字段的支持,添加了
IMessage
表单支持和可写联系人 - 2009 年 12 月 4 日 - 提高了性能,完成了联系人功能并添加了初步的日历支持
详细历史记录
- 修复了附件中的一个错误(感谢 alan,详情请参见下方的论坛)。
- 在附件代码中,我将
PR_ATTACH_FILENAME
修改为附件的文件名而不是完整路径。 - 添加了对 Outlook 2000 的支持。要使用此类与 Outlook 2000,您必须使用值为零而不是默认的
MAPI_UNICODE
调用CMAPIEx::Init
。此外,据我所知,为 Unicode 构建项目与 Office 2000 不兼容(它以 ANSI 字符串响应)。 - 修复了“邮件发送后仍然留在发件箱”的问题。更多信息请参见下方的帖子。
- 在
CMAPIMessage::Create
中添加了优先级字段,您现在可以选择设置IMPORTANCE_LOW
或IMPORTANCE_HIGH
。 - 您现在可以反复调用
AddRecipient
来发送给多个用户。 - 将项目更新到 Visual Studio 2008。
- 添加了一个生成后步骤,将 MAPIEx.dll 复制到测试项目的输出文件夹。
- 为 DLL 添加了子文件夹支持,您现在可以迭代并直接打开子文件夹。
- 修改了
Login
命令以接受配置文件名(感谢 Chris,详情请参见论坛)。 - 添加了
GetProfileName
命令以查找当前配置文件的名称(感谢 Jason,CMAPIAdmin
的作者)。 - 在消息类中添加了
ReceivedTime
属性和GetReceivedTime
函数(再次感谢 Chris,请参阅论坛)。 - 添加了
CopyMessage
函数以在文件夹之间复制消息,以及DeleteMessage
和MoveMessage
函数。 - 添加了
CreateSubFolder
、DeleteSubFolder
函数。 - 添加了
OpenSentItems
函数。 - 添加了一个新的
RTF
属性,这允许我们设置 RTF 文本甚至 HTML(请参阅 TestMAPI 示例)。 - 将
GetBody
和GetRTF
更改为在打开消息时按需填充,而不是在打开消息时立即填充。 - MAPIEx 现在可以从 Exchange (EX) 原生地址提取 SMTP 电子邮件地址(详情请参阅
FillSenderEmail
)。 - MAPIEx 现在可以接收 MAPI 的通知(例如,新邮件到达、消息删除等,请参阅
NotificationTest
示例)。 - 添加了联系人的只读支持(请参阅
ContactsTest
示例)。 - 修复了 MAPI 属性中无效字符串的错误(更多信息请参阅
GetValidString
)。 - 添加了对
MAPI_NO_CACHE
标志的支持(适用于 Outlook 2003)。 - 添加了
OpenContacts
和OpenDrafts
函数。 - 添加了
GetRowCount
以在长时间的文件夹操作中提供进度反馈。 - 添加了对
AddressList
表单的支持,以通过常用 UI 获取收件人列表。(仅限 C++,如果有人要求,则提供 C# 版本。) - 添加了对联系人和消息中自定义命名属性的支持。
- 添加了
IMessageForm
支持,以显示用于编辑消息的默认表单。 - 向消息添加了
Save
、SubmitTime
、Sensitivity
、MessageFlags
和DeliveryReceipt
属性。 - 添加了枚举消息收件人的支持。
MAPIContact
现在支持三个电子邮件字段。- 向联系人添加了许多新字段,如
Company
、HomePage
、DisplayAs
等。 - 添加了创建联系人的支持。
- 添加了初步的日历支持(尚无创建约会的功能)。
- 添加了广泛的文件夹支持,请参阅
FolderTest
示例。 - 应用户要求,添加了
CreateProfile
和DeleteProfile
。 - 扩展了 RTF 和 HTML 支持,添加了
GetMessageEditorFormat
来检测格式。 - 对用户群请求的许多小的改进和添加(太多无法一一列举,详情请参见下方的论坛)。