如何使用 VSTO 加快获取 Outlook 文件夹中所有联系人的 EntryID 值

How to speed up getting EntryID values for all contacts in Outlook folder using VSTO

我需要获取 Outlook 文件夹中所有联系人的 EntryID。如果我在一个文件夹中有 6000 个联系人,则执行大约需要 100 秒(无论是后台线程还是主线程都没有关系,我都试过了)。代码是这样的:

List<Outlook.ContactItem> contactItemsList = null;
Outlook.Items folderItems = null;
Outlook.MAPIFolder folderSuggestedContacts = null;
Outlook.NameSpace ns = null;
Outlook.MAPIFolder folderContacts = null;
object itemObj = null;
try
{
    contactItemsList = new List<Outlook.ContactItem>();
    ns = Application.GetNamespace("MAPI");
    // getting items from the Contacts folder in Outlook
    folderContacts = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
    folderItems = folderContacts.Items;
    for (int i = 1; folderItems.Count >= i; i++)
    {
        itemObj = folderItems[i];
        if (itemObj is Outlook.ContactItem)
            contactItemsList.Add(itemObj as Outlook.ContactItem);
        else
            Marshal.ReleaseComObject(itemObj);
    }
    Marshal.ReleaseComObject(folderItems);
    folderItems = null;
}
catch (Exception ex)
{
    System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
    if (folderItems != null)
        Marshal.ReleaseComObject(folderItems);
    if (folderContacts != null)
        Marshal.ReleaseComObject(folderContacts);
    if (folderSuggestedContacts != null)
        Marshal.ReleaseComObject(folderSuggestedContacts);
    if (ns != null)
        Marshal.ReleaseComObject(ns);
}
var ids = contactItemsList.Select(c => c.EntryID).ToArray();

收集物品的部分大约需要 5-8 秒,而最后一行大约需要 80-90 秒。

有没有更快的方法?我最初想到了 Items.SetColumns,但事实证明它不适用于 EntryID(这似乎是 Outlook 开发人员的奇怪决定,因为 EntryID 只是一个字符串,而 SetColumns 用于快速检索字符串属性)。

此外,即使使用 BackgroundWorker,Outlook 也几乎一直冻结(约 100 秒)。你可能不小心点击了一些东西,但它的 FPS 速率是 1-2 FPS 左右。似乎后台执行没有多大帮助。我怀疑这是因为当正在执行的任务不是 CPU 密集型但获取 EntryIDs 是繁重的操作并因此严重干扰 UI.

时后台执行很好

我还有一些遗留的 C++/COM 代码,它们的功能相同,但速度也很慢。看起来 .NET 互操作不是问题的根本原因。也许我应该使用另一个 API 调用?

我目前正在使用 Outlook 2010 64 位进行测试。

好的,知道了。将 "for" 分成块并保持同时存在的 Outlook.ContactItem 个对象的数量(就像我上面的评论)是优化的一部分,但更重要的是另一件事。

文档说您不能使用 Items.SetColumns("EntryID") 但从未解释原因。实际上,您不能将它传递到那里,因为总是返回 EntryID!因此,传递任何其他轻量级 属性(我使用 "Initials")就可以了。它 returns 仅设置了 EntryID 和 Initials 字段的对象,这将性能提高了 4-5 倍。

结合块优化,我现在在后台线程中可以在 6 秒内完成所有操作(主线程更快)。

使用 MAPITable.GetTable 在一次调用中从多个项目中检索属性而根本不打开它们(这非常昂贵)。

其次,OOM 不能在辅助线程上使用 - 它从未受到支持,并且 Outlook 2016 会在检测到除主 UI 线程之外的任何线程正在使用它时立即引发异常。只有扩展 MAPI(C++ 或 Delphi)是线程安全的。您还可以使用 Redemption (any language - I am its author) and its RDO 系列对象 - 它 100% 基于扩展 MAPI,可以从辅助线程使用。特别是,您可以使用

RDOFolder.Items.MAPITable.ExecSQL("SELECT EntryID FROM FOLDER WHERE MessageClass = 'IPM.Contact' ")

检索所有联系人的条目 ID 作为记录集。