65.9K
CodeProject 正在变化。 阅读更多。
Home

MAPIEx:扩展 MAPI 包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (134投票s)

2005年7月4日

CDDL

7分钟阅读

viewsIcon

4953596

downloadIcon

18349

一个(希望)完整的扩展 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()(可以选择只读取未读邮件)。
  • 使用 HasAttachmentSaveAttachments() 来访问邮件的附件。
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 中的 ContactsTestFoldersTest 函数以获取更多示例。

使用 LogoutNetMAPI.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_LOWIMPORTANCE_HIGH
  • 您现在可以反复调用 AddRecipient 来发送给多个用户。
  • 将项目更新到 Visual Studio 2008。
  • 添加了一个生成后步骤,将 MAPIEx.dll 复制到测试项目的输出文件夹。
  • 为 DLL 添加了子文件夹支持,您现在可以迭代并直接打开子文件夹。
  • 修改了 Login 命令以接受配置文件名(感谢 Chris,详情请参见论坛)。
  • 添加了 GetProfileName 命令以查找当前配置文件的名称(感谢 Jason,CMAPIAdmin 的作者)。
  • 在消息类中添加了 ReceivedTime 属性和 GetReceivedTime 函数(再次感谢 Chris,请参阅论坛)。
  • 添加了 CopyMessage 函数以在文件夹之间复制消息,以及 DeleteMessageMoveMessage 函数。
  • 添加了 CreateSubFolderDeleteSubFolder 函数。
  • 添加了 OpenSentItems 函数。
  • 添加了一个新的 RTF 属性,这允许我们设置 RTF 文本甚至 HTML(请参阅 TestMAPI 示例)。
  • GetBodyGetRTF 更改为在打开消息时按需填充,而不是在打开消息时立即填充。
  • MAPIEx 现在可以从 Exchange (EX) 原生地址提取 SMTP 电子邮件地址(详情请参阅 FillSenderEmail)。
  • MAPIEx 现在可以接收 MAPI 的通知(例如,新邮件到达、消息删除等,请参阅 NotificationTest 示例)。
  • 添加了联系人的只读支持(请参阅 ContactsTest 示例)。
  • 修复了 MAPI 属性中无效字符串的错误(更多信息请参阅 GetValidString)。
  • 添加了对 MAPI_NO_CACHE 标志的支持(适用于 Outlook 2003)。
  • 添加了 OpenContactsOpenDrafts 函数。
  • 添加了 GetRowCount 以在长时间的文件夹操作中提供进度反馈。
  • 添加了对 AddressList 表单的支持,以通过常用 UI 获取收件人列表。(仅限 C++,如果有人要求,则提供 C# 版本。)
  • 添加了对联系人和消息中自定义命名属性的支持。
  • 添加了 IMessageForm 支持,以显示用于编辑消息的默认表单。
  • 向消息添加了 SaveSubmitTimeSensitivityMessageFlagsDeliveryReceipt 属性。
  • 添加了枚举消息收件人的支持。
  • MAPIContact 现在支持三个电子邮件字段。
  • 向联系人添加了许多新字段,如 CompanyHomePageDisplayAs 等。
  • 添加了创建联系人的支持。
  • 添加了初步的日历支持(尚无创建约会的功能)。
  • 添加了广泛的文件夹支持,请参阅 FolderTest 示例。
  • 应用户要求,添加了 CreateProfileDeleteProfile
  • 扩展了 RTF 和 HTML 支持,添加了 GetMessageEditorFormat 来检测格式。
  • 对用户群请求的许多小的改进和添加(太多无法一一列举,详情请参见下方的论坛)。
© . All rights reserved.