Outlook 2010 中为开发人员提供的新功能

**摘要:**本文详细说明了 Microsoft Outlook 2010 中面向开发人员的增强功能和新增功能。此外,还深入分析了 Outlook 对象模型中的一些新增对象和增强的对象,包括其属性、方法和事件。对于迫切希望了解 Outlook 平台的开发人员来说,本文提供的详细信息足以帮助他们开始对 Outlook 2010 进行编码。

上次修改时间: 2013年1月30日

适用范围: Office 2010 | Outlook 2010

本文内容
Outlook 2010 平台概述
Outlook 对象模型更改
加载项复原能力
改进的 UI 扩展性
完善的对象模型
32 位和 64 位平台支持
多个 Exchange 帐户
结论
其他资源

目录

  • Outlook 2010 平台概述

  • Outlook 对象模型更改

  • 加载项复原能力

  • 改进的 UI 扩展性

  • 完善的对象模型

  • 32 位和 64 位平台支持

  • 多个 Exchange 帐户

  • 结论

  • 其他资源

Outlook 2010 平台概述

Outlook 2010 对象模型的更改满足了客户提出的能够扩展用户界面 (UI) 的要求,并且在对象模型中提供了对新增功能的支持,这些新增功能专为 Outlook 2010 最终用户设计。Outlook 2010 平台包含以下功能:

  • 加载项复原能力

    加载项不应该降低 Outlook 的性能或复原能力。Outlook 将连接的加载项列表写入 Windows 事件日志,并记录每个连接的加载项的启动时间。此外,如果在事件回调时加载项崩溃,Outlook 也会写入 Windows 事件日志。为了防止在用户操作关闭 Outlook 应用程序时加载项对 Outlook 的性能产生负面影响,Outlook 使用新增的快速关闭进程。有关新增关闭进程的详细信息,请参阅 Outlook 2010 中有关关闭的更改

  • 改进的 UI 扩展性

    您可以自定义 Outlook UI 的很多新增区域,包括导航窗格中称为"解决方案"模块的自定义导航模块。您可以在"解决方案"模块下设置或获取 Outlook 文件夹的自定义文件夹图标。通过"解决方案"模块可以更轻松地查找自定义解决方案文件夹的内容。通过使用 Microsoft Office Fluent 用户界面的扩展性,您可以自定义在早期版本的 Outlook 中不可扩展的 UI 区域。有关"解决方案"模块的详细信息,请参阅Outlook 2010 解决方案模块编程。有关如何使用功能区扩展性自定义 Outlook UI 的详细信息,请参阅在 Outlook 2010 中扩展用户界面

  • 32 位和 64 位平台支持

    您可以使用一个 API(即 Outlook 对象模型)为 32 位和 64 位平台开发解决方案。

  • 完善的对象模型

    对于绝大多数开发人员来说,对象模型非常完善,足以帮助他们编写专业的解决方案,而且无需在消息处理 API (MAPI) 级别编写代码。

  • 多个 Exchange 帐户

    您可以在一个配置文件下定义多个 Microsoft Exchange 帐户。最初在设计对象模型时假定在一个配置文件中只运行一个 Exchange 帐户。更新的对象模型得到了改进,可以处理多个 Exchange 帐户。

本文讨论对象模型中的重要新增功能,并且包含使用 C# 编写的代码示例,这些示例可以帮助您编写新解决方案或者调整现有代码以使其适合 Outlook 2010。

备注

本文中的对象、属性、方法和事件在 Outlook 2010 的最终发行版中可能会有所更改,并且可能会引入其他功能。请确保在发布解决方案之前在 Outlook 2010 的最终版本中对代码更改进行测试。

Outlook 对象模型更改

Outlook 对象模型包含的新增对象、方法、属性和事件以编程方式支持新增的 Outlook 2010 功能。对象模型的其他改进满足了常见的开发人员请求,即对 Outlook 平台进行特定更改。

Outlook 2010 的现有 Outlook 对象和集合的增强功能

下表列出了早期版本的 Outlook 中的对象和集合的增强功能。第二列仅列出新方法、属性和事件。

表 1. Outlook 对象模型增强功能

对象和集合

新成员

Account

方法

GetAddressEntryFromID(String)

GetRecipientFromID(String)

属性

AutoDiscoverConnectionMode

AutoDiscoverXml

CurrentUser

DeliveryStore

ExchangeConnectionMode

ExchangeMailboxServerName

ExchangeMailboxServerVersion

Accounts

事件

AutoDiscoverComplete

Application

方法

RefreshFormRegionDefinition(String)

属性

PickerDialog

AppointmentItem

方法

CopyTo(MAPIFolder, OlAppointmentCopyOptions)

GetConversation()

GetOrganizer()

属性

ConversationID

RTFBody

事件

AfterWrite

BeforeRead

Attachment

方法

GetTemporaryFilePath()

AttachmentSelection

方法

GetSelection(OlSelectionContents)

属性

Location

CalendarView

属性

SelectedEndTime

SelectedStartTime

ContactItem

方法

GetConversation()

属性

ConversationID

RTFBody

事件

AfterWrite

BeforeRead

DistListItem

方法

GetConversation()

属性

ConversationID

RTFBody

DocumentItem

事件

AfterWrite

BeforeRead

ExchangeUser

方法

GetPicture()

Explorer

方法

AddToSelection(Object)

ClearSelection()

IsItemSelectableInView(Object)

RemoveFromSelection(Object)

SelectAllItems()

属性

AccountSelector

AttachmentSelection

事件

AttachmentSelectionChange

Folder

方法

GetCustomIcon()

SetCustomIcon(StdPicture)

FormRegion

属性

Visible

Inspector

方法

SetSchedulingStartTime(DateTime)

属性

AttachmentSelection

事件

AttachmentSelectionChange

JournalItem

方法

GetConversation()

属性

ConversationID

事件

AfterWrite

BeforeRead

MailItem

方法

GetConversation()

属性

ConversationID

PermissionTemplateGuid

RetentionExpirationDate

RetentionPolicyName

RTFBody

Sender

事件

AfterWrite

BeforeRead

MeetingItem

方法

GetConversation()

属性

ConversationID

IsLatestVersion

RTFBody

SendUsingAccount

事件

AfterWrite

BeforeRead

NameSpace

方法

CreateContactCard(AddressEntry)

PostItem

方法

GetConversation()

属性

ConversationID

RTFBody

事件

AfterWrite

BeforeRead

Recipient

属性

Sendable

RemoteItem

方法

GetConversation()

属性

ConversationID

事件

AfterWrite

BeforeRead

ReportItem

方法

GetConversation()

属性

ConversationID

事件

AfterWrite

BeforeRead

Selection

方法

GetSelection(OlSelectionContents)

属性

Location

SharingItem

方法

GetConversation()

属性

ConversationID

PermissionTemplateGuid

RetentionExpirationDate

RetentionPolicyName

RTFBody

事件

AfterWrite

BeforeRead

Store

方法

GetDefaultFolder(OlDefaultFolders)

RefreshQuotaDisplay()

属性

Categories

IsConversationEnabled

TableView

方法

GetTable()

属性

AlwaysExpandConversation

ShowConversationByDate

ShowConversationSendersAboveSubject

ShowFullConversations

TaskItem

TaskRequestAcceptItem

TaskRequestDeclineItem

TaskRequestItem

TaskRequestUpdateItem

方法

GetConversation

属性

ConversationID

RTFBody

ViewFont

属性

ExtendedColor

Outlook 2010 的新增 Outlook 对象和集合

下表列出了 Outlook 2010 中引入的新对象。所有对象成员都在第二列中列出。

表 2. Outlook 对象模型新增内容

对象

方法、属性和事件

AccountSelector

属性

Application

Class

Parent

SelectedAccount

Session

事件

SelectedAccountChange

Conversation

方法

ClearAlwaysAssignCategories(Store)

GetAlwaysAssignCategories(Store)

GetAlwaysDelete(Store)

GetAlwaysMoveToFolder(Store)

GetChildren(Object)

GetParent(Object)

GetRootItems()

GetTable()

MarkAsRead()

MarkAsUnread()

SetAlwaysAssignCategories(String, Store)

SetAlwaysDelete(OlAlwaysDeleteConversation, Store)

SetAlwaysMoveToFolder(MAPIFolder, Store)

StopAlwaysDelete(Store)

StopAlwaysMoveToFolder(Store)

属性

Application

Class

ConversationID

Parent

Session

ConversationHeader

方法

GetConversation()

GetItems()

属性

Application

Class

ConversationID

ConversationTopic

Parent

Session

MobileItem

方法

Close(OlInspectorClose)

Copy()

Delete()

Display(Object)

Forward()

Move(MAPIFolder)

PrintOut()

Reply()

ReplyAll()

Save()

SaveAs(String, Object)

Send(Boolean)

属性

Actions

Application

Attachments

BillingInformation

Body

Categories

Class

Companies

ConversationIndex

ConversationTopic

Count

CreationTime

EntryID

FormDescription

GetInspector

HTMLBody

Importance

ItemProperties

LastModificationTime

MessageClass

Mileage

MobileFormat

NoAging

OutlookInternalVersion

OutlookVersion

Parent

PropertyAccessor

ReceivedByEntryID

ReceivedByName

ReceivedTime

Recipients

ReplyRecipientNames

ReplyRecipients

Saved

SenderEmailAddress

SenderEmailType

SenderName

SendUsingAccount

Sensitivity

Sent

SentOn

Session

Size

SMILBody

Subject

Submitted

To

UnRead

UserProperties

事件

AfterWrite

AttachmentAdd

AttachmentRead

AttachmentRemove

BeforeAttachmentAdd

BeforeAttachmentPreview

BeforeAttachmentRead

BeforeAttachmentSave

BeforeAttachmentWriteToTempFile

BeforeAutoSave

BeforeCheckNames

BeforeDelete

BeforeRead

Close

CustomAction

CustomPropertyChange

Forward

Open

PropertyChange

Read

Reply

ReplyAll

Send

Unload

Write

SimpleItems

属性

Application

Class

Count

Item[Object]

Parent

Session

SolutionsModule

方法

AddSolution(MAPIFolder, OlSolutionScope)

属性

Application

Class

Name

NavigationModuleType

Parent

Position

Session

Visible

加载项复原能力

加载项复原能力 是 Outlook 2010 平台的重要功能。加载项复原能力 是指对加载项进行加载前后,Outlook 继续正常运行和响应。加载项在 Outlook 的进程内运行,因此可能会降低 Outlook 用户体验。此外,IT 管理员担心在非托管环境中他们并非始终知道用户将运行哪些加载项,如果加载项编写欠佳,可能会导致支持中心呼叫和拥有成本增加。因为所有 Outlook 对象模型调用在 Outlook 中的主要前台线程上执行,所以在编写代码时一定要将性能作为重要目标。如果代码使 Outlook 运行缓慢或者使其无限期地挂起,用户将感到失望并弃用您的解决方案。

Windows 事件日志中的加载项目录和启动时间

在 Outlook 2010 中,用户和 IT 管理员可以确定 Outlook 启动时将加载哪些加载项以及这些加载项对启动时间的影响。Outlook 启动时,每个连接的加载项标识的相关详细信息会写入 Windows 事件日志。此外,每个连接的加载项的启动时间也会写入事件日志(以毫秒为单位)。

图 1. Windows 事件日志中的 Outlook 2010 加载项以及每个加载项的启动时间

Windows 事件日志中的加载项

加载项快速关闭

Outlook 2010 对加载项强制执行新增的快速关闭进程。新增关闭进程通过在用户退出 Outlook 后不释放资源来防止加载项造成较长时间的延迟。尽管这一更改可能会对少量现有的加载项产生负面影响,但是加载项提供商和 IT 管理员可以通过强制 Outlook 继续使用标准加载项关闭进程来缓解这些影响。

下面的列表说明了有关加载项的快速关闭进程的一些要点:

  • 用户操作关闭 Outlook 时,如果 RemoveMode 参数设置为 ext_DisconnectMode.ext_dm_HostShutdown,将触发 IDTExtensibility2 接口的 OnDisconnection 事件。

  • 对于托管 Microsoft Visual Studio Tools for Office 加载项,用户操作关闭 Outlook 时,不会调用 ThisAddin 类的 Shutdown 方法。

  • "COM 加载项"对话框中的用户操作断开加载项时,如果 RemoveMode 设置为 ext_DisconnectMode.ext_dm_UserClosed,将触发 IDTExtensiblity2 接口的 OnDisconnection 事件。

  • 用户操作关闭 Outlook 时,将触发 Outlook Application 对象的 Quit 事件。

有关加载项关闭和最佳实践的详细信息,请参阅 Outlook 2010 中有关关闭的更改

改进的 UI 扩展性

Outlook 2010 提供了以下扩展 UI 的方式,这些扩展方式都是开发人员经常提出的要求:

  • 在导航窗格的新"解决方案"模块中添加了自定义文件夹层次结构。在 Outlook 对象模型中,"解决方案"模块由 SolutionsModule 对象表示。SolutionsModule 对象公开了 AddSolution 方法,该方法可用于添加解决方案根文件夹。Outlook 2010 自动将解决方案根文件夹的所有子文件夹添加到在"解决方案"模块中显示的解决方案文件夹层次结构中。有关"解决方案"模块的示例,请参阅图 2。

  • 自定义解决方案文件夹的文件夹图标。可以使用 Folder 对象的 SetCustomIcon 和 GetCustomIcon 方法设置和获取文件夹图标。

  • 使用 Office Fluent UI 扩展性来自定义 Outlook UI,包括以下几项:

    • 浏览器功能区

    • 检查器功能区

    • 上下文菜单

    • 新项目菜单

    • 联系人卡片上下文菜单

    • 上下文选项卡

    • Microsoft Office Backstage 视图

图 2. Outlook 2010 中的解决方案模块示例

Outlook 2010 中的解决方案模块示例

如果您的现有代码自定义 Office 命令栏,则这些 UI 自定义项显示在"加载项"选项卡中,这样可能不便于用户查找。请遵循 Office Fluent UI 扩展性指南并重新编写代码,以利用浏览器功能区、Office Fluent UI 上下文菜单和 Backstage 视图。有关 Outlook 2010 中的 UI 扩展性以及示例代码的综合信息,请参阅下列文章:

完善的对象模型

完善的对象模型的目标是确保开发人员无需直接使用 MAPI 代码即可编写专业的解决方案。在托管代码中,不能开发使用 MAPI 的 Outlook 解决方案。

适用于对话的对象模型

Outlook 2010 引入了一项功能强大的新功能,称为对话视图,此外还引入了一个称为 Conversation 对象的新对象。可以使用 Conversation 对象执行对话操作并遍历对话树。对话中的项目可以位于不同的存储区和文件夹中。

若要访问对话线索中的项目,请调用项目的 GetConversation 方法以返回 Conversation 对象。Conversation 对象表示父项目所属的对话。

如果项目的对话不存在,则 GetConversation 返回 Null(在 Visual Basic 中返回 Nothing)。在以下情况下项目的对话不存在:

  • 项目尚未保存。可以通过用户操作或自动保存以编程方式保存项目。

  • 可以发送项目,但是尚未发送它(例如邮件项目、约会项目或联系人项目)。

  • 已通过 Windows 注册表禁用所有对话。

  • 存储区不支持对话(例如,Outlook 在经典联机模式下运行在早于 Microsoft Exchange Server 2010 的 Microsoft Exchange 版本上)。使用 Store 对象的 IsConversationEnabled 属性可以确定存储区是否支持对话。

以下 DemoConversation 函数获取 Outlook 资源管理器窗口中所选项目的 Conversation 对象。若要列出对话中的项目,请使用 Conversation 对象的 GetTable 方法来返回 Table 对象。然后可以根据需要向 Table 对象添加列,或者调用 GetNextRow 以返回 Table 中的每一行。

void DemoConversation()
    {
        object selectedItem = Application.ActiveExplorer().Selection[1];
        // For this example, you work only with 
        // MailItem. Other item types such as
        // MeetingItem and PostItem can participate 
        // in a conversation.
        if (selectedItem is Outlook.MailItem)
        {
            // Cast selectedItem to MailItem.
            Outlook.MailItem mailItem =
                selectedItem as Outlook.MailItem; ;
            // Determine store of MailItem.
            Outlook.Folder folder = mailItem.Parent
                as Outlook.Folder;
            Outlook.Store store = folder.Store;
            if (store.IsConversationEnabled == true)
            {
                // Obtain a Conversation object.
                Outlook.Conversation conv =
                    mailItem.GetConversation();
                // Check for null conversation.
                if (conv != null)
                {
                    // Obtain Table that contains rows 
                    // for each item in the conversation.
                    Outlook.Table table = conv.GetTable();
                    Debug.WriteLine("Conversation Items Count: " +
                        table.GetRowCount().ToString());
                    Debug.WriteLine("Conversation Items from Table:");
                    while (!table.EndOfTable)
                    {
                        Outlook.Row nextRow = table.GetNextRow();
                        Debug.WriteLine(nextRow["Subject"]
                            + " Modified: "
                            + nextRow["LastModificationTime"]);
                    }
                    Debug.WriteLine("Conversation Items from Root:");
                    // Obtain root items and enumerate Conversation.
                    Outlook.SimpleItems simpleItems 
                        = conv.GetRootItems();
                    foreach (object item in simpleItems)
                    {
                        // In this example, enumerate only the MailItem type.
                        // Other types such as PostItem or MeetingItem
                        // can appear in a conversation.
                        if (item is Outlook.MailItem)
                        {
                            Outlook.MailItem mail = item
                                as Outlook.MailItem;
                            Outlook.Folder inFolder =
                                mail.Parent as Outlook.Folder;
                            string msg = mail.Subject
                                + " in folder " + inFolder.Name;
                            Debug.WriteLine(msg);
                        }
                        // Call EnumerateConversation 
                        // to access child nodes of root items.
                        EnumerateConversation(item, conv);
                    }
                }
            }
        }
    }


    void EnumerateConversation(object item,
        Outlook.Conversation conversation)
    {
        Outlook.SimpleItems items =
            conversation.GetChildren(item);
        if (items.Count > 0)
        {
            foreach (object myItem in items)
            {
                // In this example, enumerate only MailItem type.
                // Other types such as PostItem or MeetingItem
                // can appear in a conversation.
                if (myItem is Outlook.MailItem)
                {
                    Outlook.MailItem mailItem =
                        myItem as Outlook.MailItem;
                    Outlook.Folder inFolder =
                        mailItem.Parent as Outlook.Folder;
                    string msg = mailItem.Subject
                        + " in folder " + inFolder.Name;
                    Debug.WriteLine(msg);
                }
                // Continue recursion.
                EnumerateConversation(myItem, conversation);
            }
        }
    }

如果要导航对话的每个节点,一种替代方法是调用 GetRootItems 方法以返回对话的一个或多个根项目。如果删除了原始的单一根项目,子项目提升为根项目,则对话可以包含多根项目。得到根项目后,可以获取每个根项目的所有子节点的 SimpleItems 集合。SimpleItems 是集合对象,您可以使用该对象枚举对话中每个节点的子项目。如果使用 foreach 循环枚举集合中的每个项目,请以递归方式调用 EnumerateConversation 函数以访问对话中的每个子项目。EnumerateConversation 函数接受两个参数,一个表示项目(如 MailItem),另一个表示 Conversation 对象。Conversation 对象的 GetChildren 方法返回 SimpleItems 集合,该集合表示对话中给定项目的子节点。如果 SimpleItems.Count 大于零,则继续枚举子节点并对每个子节点调用 EnumerateConversation。

Conversation 对象还公开允许将对话操作应用于对话的方法。具体来说,您可以使用 Outlook 2010 中 Conversation 对象的下列方法。

表 3. Conversation 对象方法

方法

说明

ClearAlwaysAssignCategories

从对话的所有项目中删除所有类别,并禁止 Outlook 总是为对话中的项目分配类别。

GetAlwaysAssignCategories

返回一个 String,它指示分配给进入对话中的所有新项目的一个或多个类别。

GetAlwaysDelete

返回 OlAlwaysDeleteConversation 枚举中的一个常量,该常量指示是否总是将进入对话中的所有新项目移动到指定传送存储区中的"已删除邮件"文件夹中。

GetAlwaysMoveToFolder

获取指定传送存储区中的一个 Folder 对象,进入对话中的所有新项目总是移动到该对象中。

MarkAsRead

将对话中的所有项目标记为已读。

MarkAsUnread

将对话中的所有项目标记为未读。

SetAlwaysAssignCategories

将一个或多个类别应用于对话的所有现有项目和未来项目。

SetAlwaysDelete

设置 OlAlwaysDeleteConversation 枚举中的一个常量,该常量指示是否总是将所有现有项目以及进入对话中的所有新项目移动到指定传送存储区中的"已删除邮件"文件夹中。

SetAlwaysMoveToFolder

设置指定传送存储区中的一个 Folder 对象,所有现有项目以及进入对话中的所有新项目总是移动到该对象中。

StopAlwaysDelete

禁止总是将指定存储区上的对话项目移动到该存储区上的"已删除邮件"文件夹中的操作。

StopAlwaysMoveToFolder

禁止总是将指定存储区上的对话项目移动到特定文件夹中的操作。

例如,忽略操作将导致对话中的所有当前项目和未来项目移动到"已删除邮件"文件夹中。若要通过编写代码来实现与忽略操作相同的效果,请使用 SetAlwaysDelete 方法,以总是删除对话中的所有项目并将所有新对话项目移动到"已删除邮件"文件夹中。若要以编程方式还原忽略操作,请调用 StopAlwaysDelete 方法。下面的代码示例演示如何使用 SetAlwaysDelete 方法。

void DemoIgnoreConversation()
    {
        // Obtain the selection.
        object selectedItem =
            Application.ActiveExplorer().Selection[1];
        if (selectedItem is Outlook.MailItem)
        {
            // Cast the object to MailItem.
            Outlook.MailItem mail = selectedItem
                as Outlook.MailItem; 
            // Determine the store of mail.
            Outlook.Folder folder = mail.Parent
                as Outlook.Folder;
            Outlook.Store store = folder.Store;
            if (store.IsConversationEnabled == true)
            {
                Outlook.Folder delItems =
                    store.GetDefaultFolder(
                    Outlook.OlDefaultFolders.olFolderDeletedItems)
                    as Outlook.Folder;
                Outlook.Conversation conv = mail.GetConversation();
                if (conv != null)
                {
                    conv.SetAlwaysDelete(
                        Outlook.OlAlwaysDeleteConversation.olAlwaysDelete,
                        delItems.Store);
                }
            }
        }
    }

获取发件人的 SMTP 地址

在 Microsoft Office Outlook 2007 中,开发人员使用 PropertyAccessor 对象获取发件人的 SMTP 地址,实现此操作的代码与以下代码类似:

private string GetSenderSMTPAddress(Outlook.MailItem mail)
    {
        if (mail == null)
        {
            throw new ArgumentNullException();
        }
        string PR_SENT_REPRESENTING_ENTRYID =
            @"https://schemas.microsoft.com/mapi/proptag/0x00410102";
        string PR_SMTP_ADDRESS =
            @"https://schemas.microsoft.com/mapi/proptag/0x39FE001E";
        if (mail.SenderEmailType == "EX")
        {
            string senderEntryID =
                mail.PropertyAccessor.BinaryToString(
                mail.PropertyAccessor.GetProperty(
                PR_SENT_REPRESENTING_ENTRYID));
            Outlook.AddressEntry sender =
                Application.Session.
                GetAddressEntryFromID(senderEntryID);
            if (sender != null)
            {
                // Now there is an AddressEntry that represents the sender.
                if (sender.AddressEntryUserType ==
                    Outlook.OlAddressEntryUserType.
                    olExchangeUserAddressEntry
                    || sender.AddressEntryUserType ==
                    Outlook.OlAddressEntryUserType.
                    olExchangeRemoteUserAddressEntry)
                {
                    // Use the PrimarySMTPAddress property of the
                    // ExchangeUser object.
                    Outlook.ExchangeUser exchUser =
                        sender.GetExchangeUser();
                    if (exchUser != null)
                    {
                        return exchUser.PrimarySmtpAddress;
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return sender.PropertyAccessor.GetProperty(
                        PR_SMTP_ADDRESS) as string;
                }
            }
            else
            {
                return null;
            }
        }
        else
        {
            return mail.SenderEmailAddress;
        }
    }

在 Outlook 2010 中,MailItem 对象的 Sender 属性返回或设置一个 AddressEntry 对象,该对象表示项目的发件人。无须再使用 PropertyAccessor 对象确定 PR_SENT_REPRESENTING_ENTRYID (PidTagSentRepresentingEntryId) 属性。但是,可以编写与以下示例类似的代码。

private string GetSenderSMTPAddress2010(Outlook.MailItem mail)
    {
        string PR_SMTP_ADDRESS =
            @"https://schemas.microsoft.com/mapi/proptag/0x39FE001E";
        if (mail == null)
        {
            throw new ArgumentNullException();
        }
        if (mail.SenderEmailType == "EX")
        {
            Outlook.AddressEntry sender =
                mail.Sender;
            if (sender != null)
            {
                // Now there is an AddressEntry that represents the sender.
                if (sender.AddressEntryUserType ==
                    Outlook.OlAddressEntryUserType.
                    olExchangeUserAddressEntry
                    || sender.AddressEntryUserType ==
                    Outlook.OlAddressEntryUserType.
                    olExchangeRemoteUserAddressEntry)
                {
                    // Use the PrimarySMTPAddress property of the
                    // ExchangeUser object.
                    Outlook.ExchangeUser exchUser =
                        sender.GetExchangeUser();
                    if (exchUser != null)
                    {
                        return exchUser.PrimarySmtpAddress;
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return sender.PropertyAccessor.GetProperty(
                        PR_SMTP_ADDRESS) as string;
                }
            }
            else
            {
                return null;
            }
        }
        else
        {
            return mail.SenderEmailAddress;
        }
    }

PropertyAccessor 的改进

使用 PropertyAccessor 对象可以创建、设置、获取和删除对象的属性。在 Outlook 2007 中,PropertyAccessor 对象对可以通过 GetProperty 或 GetProperties 调用检索的属性大小进行了限制。在 Outlook 2010 中,删除了这些限制。

还可以使用 Outlook 2010 中的 PropertyAccessor 对象操作附件,而无需通过使用 Attachment 对象的 SaveAsFile 方法来将附件写入磁盘。下面的代码示例使用 PropertyAccessor 对象从 Attachment 对象检索字节数组、在内存中将字节数组从 A 更改为 B,然后将 Attachment 对象设置为更改后的字节数组。若要保留更改,请对项目调用 Save 方法。

    private void DemoAttachmentStream()
    {
        const string PR_ATTACH_DATA_BIN = 
            "https://schemas.microsoft.com/mapi/proptag/0x37010102";
        // Create a mail item.
        Outlook.MailItem mail = 
            Application.CreateItem(Outlook.OlItemType.olMailItem) 
            as Outlook.MailItem;
        mail.Subject = "Demo Attachment Stream";
        // Create the c:\demo folder if it does not exist.
        if(!Directory.Exists(@"c:\demo"))
        {
            Directory.CreateDirectory(@"c:\demo");
        }
        // Write to the attach.txt file.
        StreamWriter sw = new StreamWriter(@"c:\demo\attach.txt");
        char charA = 'A';
        string myString = new string(charA ,4096);
        sw.WriteLine(myString);
        sw.Close();
        // Add attach.txt as an attachment.
        Outlook.Attachment attach = 
            mail.Attachments.Add(@"c:\demo\attach.txt",
            Outlook.OlAttachmentType.olByValue, 
            Type.Missing, 
            Type.Missing);
        // Save the item.
        mail.Save();
        // Use PropertyAccessor to retrieve attachment byte stream.
        byte[] attachStream = 
            attach.PropertyAccessor.GetProperty(
            PR_ATTACH_DATA_BIN) as byte[];
        // Iterate the stream and change "A" to "B".
        for (int i = 0; i < attachStream.Length; i++)
        {
            attachStream[i] = 0x42; //Hex for "B"
        }
        // Set PR_ATTACH_DATA_BIN to attachStream.
        attach.PropertyAccessor.SetProperty(PR_ATTACH_DATA_BIN, 
            attachStream);
        // Save the item again.
        mail.Save();
    }

访问 RTF

Outlook 支持通过 Inspector 对象的 WordEditor 属性访问项目正文的多种格式。WordEditor 表示 Microsoft Word 对象模型中的 Word.Document 对象。Inspector.WordEditor 适用于在检查器中显示 Outlook 项目的情况。对于不显示检查器窗口的情况,Outlook 2010 对项目对象(如 MailItem 和 AppointmentItem 等)引入了 RTFBody 属性。对除 NoteItem 和 JournalItem 之外的所有项目类型公开了 RTFBody。RTFBody 是一个可读/写属性,您可以设置或获取表示项目的 RTF 流的字节数组。根据您的开发环境,您必须将 RTFBody 返回的字节数组转换为字符串、根据需要修改包含 RTF 的字符串、将字符串转换为字节数组,然后将 RTFBody 设置为修改后的字节数组。

下面的代码示例将活动资源管理器窗口的 Selection 对象中第一个项目的 RTF 写入 Visual Studio 中的"调试跟踪侦听器"窗口。代码从邮件对象获取字节数组,然后使用 System.Text.AsciiEncoding 将字节数组转换为字符串。

private void GetRTFBodyForMail()
{
    Outlook.Selection selection = 
        Application.ActiveExplorer().Selection;
    if(selection.Count >= 1)
    {
        if (selection[1] is Outlook.MailItem)
        {
            Outlook.MailItem mail =
                selection[1] as Outlook.MailItem;
            byte[] byteArray = mail.RTFBody as byte[];
            System.Text.Encoding encoding = 
                new System.Text.ASCIIEncoding();
            string RTF = encoding.GetString(byteArray);
            Debug.WriteLine(RTF);
        }
    }
}

获取在视图中显示的项目

使用 Outlook 2010,您可以通过对 TableView 对象调用 GetTable 方法获取在视图中显示的项目。GetTable 返回 Table 对象,但是不能按照为 Folder.GetTable 指定参数的方式为该方法指定参数。GetTable 适用于以下情况:视图包含的限制导致视图中的项目不同于文件夹中的项目,或者即时搜索查询从多个文件夹或存储区返回项目。下面的代码示例从"收件箱"的当前视图中检索 Table 对象。请注意,"收件箱"必须是当前文件夹,TableView.GetTable 调用才能成功。对文件夹调用 TableView.GetTable 时,如果该文件夹不是可见资源管理窗口中的当前文件夹,Outlook 将引发错误。

private void DemoViewGetTable()
    {
        // Obtain the Inbox folder.
        Outlook.Folder inbox =
            Application.Session.GetDefaultFolder(
            Outlook.OlDefaultFolders.olFolderInbox)
            as Outlook.Folder;
        // Set ActiveExplorer.CurrentFolder to Inbox.
        // Inbox must be the current folder
        // for View.GetTable to work correctly.
        Application.ActiveExplorer().CurrentFolder = inbox;
        // Ensure that the current view is TableView.
        if (inbox.CurrentView.ViewType == 
            Outlook.OlViewType.olTableView)
        {
            Outlook.TableView view = 
                inbox.CurrentView as Outlook.TableView;
            // No arguments are needed for View.GetTable.
            Outlook.Table table = view.GetTable();
            Debug.WriteLine("View Count=" 
                + table.GetRowCount().ToString());
            while (!table.EndOfTable)
            {
                // First row in Table.
                Outlook.Row nextRow = table.GetNextRow();
                Debug.WriteLine(nextRow["Subject"]
                    + " Modified: "
                    + nextRow["LastModificationTime"]);
            }
        }
    }

Selection 对象增强功能

Selection 对象通过 Location 属性得到了增强,该属性通知开发人员有关所选内容的 UI 区域的信息。Location 属性是只读属性,并返回 OlSelectionLocation 枚举中的值。在早期版本的 Outlook 中,Selection 对象仅包含在视图列表中所选的项目。在 Outlook 2010 中,Selection.Location 可以返回以下任一 OlSelectionLocation 值。

表 4. OlSelectionLocation 返回值

说明

olViewList

视图列表中的所选项目。

olToDoBarTaskList

"待办事项栏"任务列表中的所选项目。

olToDoBarAppointmentList

"待办事项栏"约会列表中的所选项目。

olDailyTaskList

日历视图的日常任务列表中的所选项目。

olAttachmentWell

Outlook 资源管理器阅读窗格的附件条带区或者 Outlook 检查器的附件条带区中的所选附件。

如果所选内容更改,则会对先前列出的所有所选内容位置(olAttachmentWell 除外)触发 SelectionChange 事件。如果在附件条带区选择了附件,则会触发 AttachmentSelectionChange 事件。Explorer 对象同时存在 SelectionChange 和 AttachmentSelectionChange 事件。

如果在触发 SelectionChange 事件时枚举 Selection 对象,请确保您的代码可以正确处理不同的项目类型。不要认为 Selection 对象中只包含 MailItem 对象。

Outlook 2010 中增强的 Selection 对象适用于任何类型的视图,包括 Outlook 2010 中的新增对话视图。以下属性帮助您确定当前视图是否为对话视图。

表 5. TableView 属性

TableView 属性

说明

AlwaysExpandConversation

返回或设置一个值,该值指示对话是否总是在表视图中完全展开。可读/写。

ShowConversationByDate

返回或设置一个 Boolean 值,该值指示表视图中的项目是否按对话日期和对话进行组织。可读/写。

ShowFullConversations

返回或设置一个 Boolean 值,该值指示其他文件夹(如"已发送邮件"文件夹)中的对话项目是否应该显示在表视图的对话中。可读/写。

如果 TableView.ShowConversationByDate 的值为 true,则视图使用 Outlook 2010 中新增的对话排列。如果当前视图是对话视图,则对话可以是单项目对话、分割对话或者已展开对话。所选内容根据所选项目是对话标题还是已展开对话(在已展开对话中可以选择多个项目)会有所变化。此外,Outlook 2010 新增了一个 ConversationHeader 对象,可以在视图中选择一个或多个对话标题。若要确定是否在视图中选择了对话标题,请使用 Selection 对象新增的 GetSelection 方法,并将 OlSelectionContents.olConversationHeaders 传递给该方法。调用 GetSelection 方法将返回 Selection 对象,但是在这种情况下,Selection 对象包含 ConversationHeader 对象而不是项目对象(如 MailItem 或 MeetingItem 对象)。DemoConversationHeadersFromSelection 函数枚举对话视图中所选对话标题的所有项目。

private void DemoConversationHeadersFromSelection()
{
    // Obtain the Inbox folder.
    Outlook.Folder inbox =
        Application.Session.GetDefaultFolder(
        Outlook.OlDefaultFolders.olFolderInbox)
        as Outlook.Folder;
    // Set ActiveExplorer.CurrentFolder to the Inbox.
    // Inbox must be the current folder.
    Application.ActiveExplorer().CurrentFolder = inbox;
    //Ensure that current view is a TableView object.
    if (inbox.CurrentView.ViewType ==
        Outlook.OlViewType.olTableView)
    {
        Outlook.TableView view =
            inbox.CurrentView as Outlook.TableView;
        if (view.ShowConversationByDate == true)
        {
            Outlook.Selection selection =
                Application.ActiveExplorer().Selection;
            Debug.WriteLine("Selection.Count = " + selection.Count);
            // Call GetSelection to create
            // a Selection object that contains ConversationHeader objects.
            Outlook.Selection convHeaders =
                selection.GetSelection(
                Outlook.OlSelectionContents.olConversationHeaders)
                as Outlook.Selection;
            Debug.WriteLine("Selection.Count (ConversationHeaders) = " 
                + convHeaders.Count);
            if (convHeaders.Count >= 1)
            {
                foreach (Outlook.ConversationHeader convHeader in
                    convHeaders)
                {
                    // Enumerate the items in the ConversationHeader.
                    Outlook.SimpleItems items = convHeader.GetItems();
                    for (int i = 1; i <= items.Count; i++)
                    {
                        // Enumerate only MailItem objects in this example.
                        if (items[i] is Outlook.MailItem)
                        {
                            Outlook.MailItem mail = 
                                items[i] as Outlook.MailItem;
                            Debug.WriteLine(mail.Subject 
                                + " Received:" + mail.ReceivedTime);
                        }
                    }
                }
            }
        }
    }
}

32 位和 64 位平台支持

本节讨论使用安装了 32 位或 64 位 Outlook 2010 的 32 位或 64 位 Windows 时,影响 Outlook 扩展性的一些问题。

对 64 位 Outlook 的操作系统和 Office 支持

从 Microsoft Office 2010 开始,Outlook 作为 32 位或 64 位应用程序提供。在同一计算机上,Outlook 的位数取决于 Windows 操作系统的位数(x86 或 x64);如果已经在该计算机上安装 Office,则取决于 Office 的位。下面列出了确定安装 32 位或 64 位版本的 Outlook 是否可行的一些因素:

  • 32 位 Office(和 32 位 Outlook)可以安装在 32 位或 64 位版本的 Windows 操作系统上。64 位 Office(和 64 位 Outlook)只能安装在 64 位操作系统上。

  • 在 64 位版本的 Windows 操作系统上,默认安装的 Office 是 32 位 Office。

  • 如果在同一计算机上安装 Office 和 Outlook,则 Outlook 版本的位数始终与 Office 的位数相同。换句话说,32 位版本的 Outlook 不能与 64 位版本的其他 Office 应用程序(如 64 位 Word 或 64 位 Microsoft Excel)安装在同一计算机上。同样,64 位版本的 Outlook 不能与 32 位版本的其他 Office 应用程序安装在同一计算机上。

在 64 位 Outlook 上运行的现有应用程序的注意事项

如果 32 位应用程序在安装了 64 位 Office 的 64 位版本的 Windows 操作系统上运行,请考虑以下问题:

  • 对于 32 位和 64 位版本的 Outlook,对象模型调用应无需修改即可使用。

  • 针对 32 位版本的 Outlook(这包括 Outlook 2010 之前的所有版本的 Outlook)编译的本机加载项必须针对 64 位 Outlook 重新编译。

  • 使用 Microsoft Visual Studio Tools for the Microsoft Office system 3.0 和 Visual Studio 2010 中的 Microsoft Office 开发工具创建的 Outlook 加载项适用于 32 位和 64 位版本的 Office,只要这些加载项使用"项目属性"对话框的"生成"选项卡上目标平台的"任何 CPU"选项进行了编译。

  • 使用 Outlook 对象模型的 32 位独立应用程序 (.exe) 必须针对 64 位 Outlook 重新编译。

64 位 Outlook 中消息处理 API (MAPI) 的更改

尽管 32 位和 64 位版本的 Outlook 的 Outlook 2010 对象模型没有任何区别,但是因为随 64 位 Outlook 引入了 64 位 MAPI,所以 MAPI 函数有一些较小的更改。通常,这些更改与必须传递到 MAPI 函数的 64 位类型(如 ULONG_PTR)相关。

MAPI 应用程序包含独立应用程序(如 Microsoft Communicator 和 MFCMAPI(该链接可能指向英文页面))和服务提供程序(如通讯簿、存储和传输提供程序)。要使 MAPI 方法和函数调用在 MAPI 应用程序中发挥作用(一个简单 MAPI 函数 MAPISendMail 除外),MAPI 应用程序的位数必须与将运行应用程序的目标计算机上 MAPI 子系统的位数相同。而 MAPI 子系统的位数由安装的 Outlook 版本的位数确定,并且二者始终相同。

如果您的解决方案是本机解决方案并且直接调用 MAPI,请参阅 Welcome to the Outlook 2010 MAPI Reference,以获取有关如何移植您的解决方案以使其适用于 64 位 Outlook 的指南。MAPI 开发人员应该特别关注下列主题:

弃用的 Exchange 客户端扩展

Exchange 客户端扩展 (ECE) 在 Outlook 2010 中已被弃用。ECE 在 1995 年随 Microsoft Exchange 客户端引入,但该客户端是针对早期版本的 Exchange Server 运行的 16 位邮件应用程序。ECE 必须以本机代码编写,通常使用 C++ 并且高度依赖 MAPI。Outlook 取代 Exchange 客户端后,ECE 用于扩展 Outlook 97-98,直到在 Outlook 2000 中 COM 加载项(该链接可能指向英文页面)取代了 ECE。

在 Outlook 2007 和更早的版本中,ECE 将继续按照预期方式运行。然而,在 32 位和 64 位版本的 Outlook 2010 中将不加载 ECE。若要重新设计现有 ECE 解决方案,请考虑以下选择:

Outlook 2010 不支持 CDO 1.21

协作数据对象 (CDO) 1.2.1 是一个客户端库,为扩展的 MAPI 功能提供瘦包装。CDO 不随 Outlook 2010 安装,只能通过下载获得。有关详细信息,请参阅 Microsoft 下载中心的协作数据对象,版本 1.2.1(该链接可能指向英文页面)。无法保证 CDO 在包含多个 Exchange 帐户的环境中正常运行。CDO 1.2.1 是 32 位客户端库,不能基于 64 位 Outlook 2010 运行。因此,不支持将 CDO 1.2.1 用于 Outlook 2010。大多数 CDO 1.2.1 功能已经包含在 Outlook 2010 对象模型中。作为 CDO 1.2.1 的替代方案,请将现有依赖于 CDO 的解决方案更新为使用 Outlook 对象模型或直接使用 MAPI。

多个 Exchange 帐户

Outlook 2010 中一项激动人心的新增功能是可以运行多个 Exchange 帐户。Outlook 对象模型最初围绕给定配置文件中的一个(并且只有一个)Exchange 帐户进行设计。在 Outlook 2007 中,与 Exchange 帐户相关联的属性和方法位于 Namespace 上。为了适应多个 Exchange 帐户,Account 对象现在公开了一些新属性和方法。ExchangeMailboxServerName、ExchangeMailboxServerVersion 和 ExchangeConnectionMode 等属性仍位于 Namespace 对象上,但是返回的值仅应用于主 Exchange 帐户。对于与配置文件中的所有 Exchange 帐户相关的 Exchange 属性,请使用 Account 对象。下表显示为了支持多个 Exchange 帐户,在 Outlook 2010 中向 Account 对象添加的属性和方法。

表 6. Account 对象新增成员

名称

类型

说明

AutoDiscoverConnectionMode

属性

返回一个 OlAutoDiscoverConnectionMode 常量,该常量指定承载帐户邮箱的 Exchange 服务器的自动发现服务所使用的连接类型。只读。

AutoDiscoverXml

属性

返回一个字符串,该字符串表示从与帐户相关联的 Exchange 服务器的自动发现服务检索的 XML 中的信息。只读。

CurrentUser

属性

返回一个 Recipient 对象,该对象表示帐户的当前用户标识。只读。

DeliveryStore

属性

返回一个 Store 对象,该对象表示帐户的默认传送存储区。如果帐户不具有默认传送存储区,则返回 Null(在 Visual Basic 中返回 Nothing)。

ExchangeConnectionMode

属性

返回一个 OlExchangeConnectionMode 常量,该常量表示承载帐户邮箱的 Exchange Server 的当前连接模式。只读。

ExchangeMailboxServerName

属性

返回一个字符串,该字符串表示承载帐户邮箱的 Exchange Server 的名称。

ExchangeMailboxServerVersion

属性

返回承载帐户邮箱的 Exchange Server 的完整版本号。只读。

GetAddressEntryFromID

方法

返回一个 AddressEntry 对象,该对象表示由 ID 指定的地址条目。

GetRecipientFromID

方法

返回由指定的条目 ID 标识的 Recipient 对象。

枚举配置文件中的帐户

以下 EnumerateAccounts 函数提供了一个简单的示例来说明如何使用 Account 对象的新属性。

private void EnumerateAccounts()
    {
        Outlook.Accounts accounts =
            Application.Session.Accounts;
        foreach (Outlook.Account account in accounts)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("Account: " + account.DisplayName);
                if (string.IsNullOrEmpty(account.SmtpAddress)
                    || string.IsNullOrEmpty(account.UserName))
                {
                    Outlook.AddressEntry oAE =
                        account.CurrentUser.AddressEntry
                        as Outlook.AddressEntry;
                    if (oAE.Type == "EX")
                    {
                        Outlook.ExchangeUser oEU =
                            oAE.GetExchangeUser()
                            as Outlook.ExchangeUser;
                        sb.AppendLine("UserName: " +
                            oEU.Name);
                        sb.AppendLine("SMTP: " +
                            oEU.PrimarySmtpAddress);
                        sb.AppendLine("Exchange Server: " +
                            account.ExchangeMailboxServerName);
                        sb.AppendLine("Exchange Server Version: " +
                            account.ExchangeMailboxServerVersion); 
                    }
                    else
                    {
                        sb.AppendLine("UserName: " +
                            oAE.Name);
                        sb.AppendLine("SMTP: " +
                            oAE.Address);
                    }
                }
                else
                {
                    sb.AppendLine("UserName: " +
                        account.UserName);
                    sb.AppendLine("SMTP: " +
                        account.SmtpAddress);
                    if (account.AccountType == 
                        Outlook.OlAccountType.olExchange)
                    {
                        sb.AppendLine("Exchange Server: " +
                            account.ExchangeMailboxServerName);
                        sb.AppendLine("Exchange Server Version: " +
                            account.ExchangeMailboxServerVersion); 
                    }
                }
                If (account.DeliveryStore !=null)
                {
                    sb.AppendLine("Delivery Store: " +
                        account.DeliveryStore.DisplayName);
                }
                sb.AppendLine("---------------------------------");
                Debug.Write(sb.ToString());
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
    }

创建可发送项目并设置"发件人"字段

在 Outlook 对象模型中,一个最常用的方法是 Application 对象上的 CreateItem 方法。该方法不能识别多个帐户,并且始终为配置文件中的主帐户创建项目。如果要创建"可以识别"与给定文件夹相关联的帐户的可发送项目,必须确定当前文件夹的 Store,然后枚举 Accounts 集合以确定给定帐户的 DeliveryStore 是否与当前文件夹的 Store 属性相匹配。确定当前文件夹的 Store 属性的正确 Account 后,可以设置 MailItem 的 Sender 属性,或者 AppointmentItem 的 SendUsingAccount 属性。CreateMailItemUsingAccount 和 CreateMeetingRequestUsingAccount 函数演示在具有多个帐户的情况下如何处理该功能。

private void CreateMailItemFromAccount()
    {
        Outlook.AddressEntry addrEntry = null;
        Outlook.Folder folder =
            Application.ActiveExplorer().CurrentFolder 
            as Outlook.Folder;
        Outlook.Store store = folder.Store;
        Outlook.Accounts accounts =
            Application.Session.Accounts;
        foreach (Outlook.Account account in accounts)
        {
            if (account.DeliveryStore == store)
            {
                addrEntry =
                    account.CurrentUser.AddressEntry;
                break;
            }
        }
        Outlook.MailItem mail =
            Application.CreateItem(
            Outlook.OlItemType.olMailItem)
            as Outlook.MailItem;
        if (addrEntry != null)
        {
            mail.Sender = addrEntry;
        }
        mail.Display(false);
    }

    private void CreateMeetingRequestFromAccount()
    {
        Outlook.Account acct = null;
        Outlook.Folder folder =
            Application.ActiveExplorer().CurrentFolder
            as Outlook.Folder;
        Outlook.Store store = folder.Store;
        Outlook.Accounts accounts =
            Application.Session.Accounts;
        foreach (Outlook.Account account in accounts)
        {
            if (account.DeliveryStore == store)
            {
                acct = account;
                break;
            }
        }
        Outlook.AppointmentItem appt =
            Application.CreateItem(
            Outlook.OlItemType.olAppointmentItem)
            as Outlook.AppointmentItem;
        appt.MeetingStatus = 
            Outlook.OlMeetingStatus.olMeeting;
        if (acct != null)
        {
            appt.SendUsingAccount=acct;
        }
        appt.Display(false);
    }

确定 Exchange 存储的全局地址列表

如果配置文件中存在多个 Exchange 帐户,可以枚举给定 Store 对象可用的 AddressLists 或者确定给定 Exchange 存储的全局地址列表 (GAL)。下面的代码示例返回一个 AddressList 对象,该对象表示传递给 GetGlobalAddressList 函数的 Store 对象的 GAL。可以使用代码示例中的 GetGlobalAddressList 函数返回一个 AddressList 对象,该对象表示 ActiveExplorer().CurrentFolder 的 Store 的 GAL。对于包含多个 Exchange 帐户的环境中 Outlook 的本机行为,您可以通过示例代码实现相同的效果。在用户单击"新邮件"时,在 Outlook 通讯簿对话框中显示的第一个地址列表是当前文件夹的 Store 的全局地址列表(如果该存储区是 Exchange 帐户的传送存储区)。使用 DisplayGlobalAddressListForStore 和 GetGlobalAddressList 函数可以通过编程方式实现相同的行为。

        void DisplayGlobalAddressListForStore()
        {
            Outlook.Folder currentFolder =
                Application.ActiveExplorer().CurrentFolder
                as Outlook.Folder;
            Outlook.Store currentStore = currentFolder.Store;
            if (currentStore.ExchangeStoreType !=
                Outlook.OlExchangeStoreType.olNotExchange)
            {
                Outlook.SelectNamesDialog snd = 
                    Application.Session.GetSelectNamesDialog();
                Outlook.AddressList addrList = 
                    GetGlobalAddressList(currentStore);
                if (addrList != null)
                {
                    snd.InitialAddressList = addrList;
                    snd.Display();
                }
            }
        }
        public Outlook.AddressList GetGlobalAddressList(Outlook.Store store)
        {
            string  PR_EMSMDB_SECTION_UID = 
                @"https://schemas.microsoft.com/mapi/proptag/0x3D150102";
            if (store == null)
            {
                throw new ArgumentNullException();
            }
            Outlook.PropertyAccessor oPAStore = store.PropertyAccessor;
            string storeUID = oPAStore.BinaryToString(
                oPAStore.GetProperty(PR_EMSMDB_SECTION_UID));
            foreach (Outlook.AddressList addrList 
                in Application.Session.AddressLists)
            {
                Outlook.PropertyAccessor oPAAddrList = 
                    addrList.PropertyAccessor;
                string addrListUID = oPAAddrList.BinaryToString(
                    oPAAddrList.GetProperty(PR_EMSMDB_SECTION_UID));
                // Return addrList if match on storeUID
                // and type is olExchangeGlobalAddressList.
                if (addrListUID == storeUID && addrList.AddressListType ==
                    Outlook.OlAddressListType.olExchangeGlobalAddressList)
                {
                    return addrList;
                }
            }
            return null;
        }

枚举传送存储区的 AddressList 对象

下面的代码示例使用 Outlook 2010 中的一个属性充当 Store 和 AddressList 对象的唯一标识符。该属性在下面的代码示例中名为 PR_EMSMDB_SECTION_UID,用于评估给定 AddressList 是否属于特定 Store 对象。Store 对象必须表示传送存储区。如果存在多个 Exchange 帐户,则一个 Store 对象充当给定 Exchange 帐户的传送存储区。EnumerateAddressListsForStore 函数枚举 ActiveExplorer().CurrentFolder.Store 的地址列表。

private void EnumerateAddressListsForStore()
        {
            Outlook.Folder currentFolder =
                Application.ActiveExplorer().CurrentFolder
                as Outlook.Folder;
            Outlook.Store currentStore = currentFolder.Store;
            List<Outlook.AddressList> addrListsForStore = 
                GetAddressLists(currentStore);
            foreach (Outlook.AddressList addrList in addrListsForStore)
            {
                Debug.WriteLine(addrList.Name 
                    + " " + addrList.AddressListType.ToString()
                    + " Resolution Order: " +
                    addrList.ResolutionOrder);
            }
        }

        public List<Outlook.AddressList> GetAddressLists(Outlook.Store store)
        {
            List<Outlook.AddressList> addrLists = 
                new List<Microsoft.Office.Interop.Outlook.AddressList>();
            string PR_EMSMDB_SECTION_UID =
                @"https://schemas.microsoft.com/mapi/proptag/0x3D150102";
            if (store == null)
            {
                throw new ArgumentNullException();
            }
            Outlook.PropertyAccessor oPAStore = store.PropertyAccessor;
            string storeUID = oPAStore.BinaryToString(
                oPAStore.GetProperty(PR_EMSMDB_SECTION_UID));
            foreach (Outlook.AddressList addrList
                in Application.Session.AddressLists)
            {
                Outlook.PropertyAccessor oPAAddrList =
                    addrList.PropertyAccessor;
                string addrListUID = oPAAddrList.BinaryToString(
                    oPAAddrList.GetProperty(PR_EMSMDB_SECTION_UID));
                // Return addrList if match on storeUID
                // and type is olExchangeGlobalAddressList.
                if (addrListUID == storeUID)
                {
                    addrLists.Add(addrList);
                }
            }
            return addrLists;
        }

结论

对于开发人员来说,Outlook 2010 在存储方面带来了激动人心的挑战。您可以改进您的解决方案的 UI,以与 Outlook 2010 的新 UI 无缝集成,如果您的解决方案使用自定义文件夹存储信息,则可以使用新增的"解决方案"模块和自定义文件夹图标来提高可发现性。您的代码应该能够适应多个 Exchange 帐户,并利用对象模型的更改,这些更改源于 Outlook 2010 中功能强大的新增功能,如对话视图和项目选择。最后,对平台的更改(如对 32 位和 64 位版本的 Outlook 的支持)产生了更多更改并且增加了复杂性,对于在 MAPI 级别编写的高级应用程序尤其如此。

其他资源

MAPI

Welcome to the Outlook 2010 MAPI Reference

Outlook 2010:MAPI 头文件(该链接可能指向英文页面)

Outlook 2010 消息处理 API (MAPI) 示例(该链接可能指向英文页面)

SGriffin 的 MAPI 内部原理(该链接可能指向英文页面)

Office Fluent UI 扩展性

自定义 Office 2010 中的上下文菜单

针对开发人员的 Office 2010 Backstage 视图介绍

Office 2010 中的功能区扩展性:选项卡激活和自动缩放

Microsoft Office 2010 Fluent UI 架构(该链接可能指向英文页面)

Microsoft Office 2010 Fluent 用户界面控件标识符(该链接可能指向英文页面)