2017-11-27 11 views
0

Outlookフォルダのすべての連絡先にEntryIDを取得する必要があります。フォルダに6000件の連絡先がある場合、実行には約100秒かかります(バックグラウンドスレッドかメインスレッドかどうかは関係ありません)。コードは次のようになります。VSTOを使用してOutlookフォルダ内のすべての連絡先のEntryID値を取得する方法

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(); 

最後の行が約80-90秒かかる間、アイテムを収集する部分は約5-8秒かかります。

速い方法がありますか?私はItems.SetColumnsを最初に考えましたが、EntryID(EntryIDは単なる文字列であり、SetColumnsは文字列プロパティの迅速な検索を目的としているため、Outlook開発者の奇妙な判断のようです)では機能しません。

また、OutlookはBackgroundWorkerを使用していても、この時間(〜100秒)の間、ほとんどフリーズします。誤って何かをクリックすることはできますが、FPSレートは1〜2 FPS程度です。バックグラウンドの実行があまり役に立たないようです。私はそれが実行されているタスクがCPUを必要としないが、EntryIDを取得することは重い操作であり、したがってUIをひどく妨害する場合、バックグラウンドでの実行が素晴らしいからだと思う。

私も同じことをするいくつかのレガシーC++/COMコードを持っていて、それも遅いです。 .NETの相互運用性は問題の根本的な原因ではないようです。たぶん、別のAPI呼び出しがあります。

私は現在Outlook 2010 64ビット版でテスト中です。

+0

同時に多くのOutlook.ContactItemオブジェクトがあると、Outlookが遅くなるようです。 "for"ループを分割してReleaseComObjectでオブジェクトをできるだけ早く解放すると(同時に200個以上のオブジェクトが存在しない場合)、パフォーマンスは3〜4倍に増加し、Outlookは応答性が向上します。より良いが、まだかなり遅い(そしてチャンクを小さな値に減らすと、パフォーマンスが向上しなくなる)。 – Alex

答えて

1

MAPITable.GetTableを使用すると、1つの呼び出しで複数の項目からプロパティを取得することなく(本当に高価です)、プロパティを取得できます。

第2に、OOMをセカンダリスレッドで使用することはできません。これはサポートされていないため、Outlook 2016はメインUIスレッド以外から使用されたことを検出するとすぐに例外を発生させます。拡張MAPI(C++またはDelphi)のみがスレッドセーフです。 Redemption(任意の言語)とそのRDOオブジェクトファミリを使用することもできます。これは100%拡張MAPIベースであり、セカンダリスレッドから使​​用できます。特に、すべての連絡先のエントリIDをレコードセットとして取得するには、

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

を使用できます。

+0

MAPITable.GetTableをありがとう。バックグラウンドスレッドからOOMアクセスをオフロードし、そこにOOM以外のものだけを残そうとします。 – Alex

+0

MAPITable.GetTableは短命のEntryIDを返します。だから私は代わりにPR_LONG_TERM_ENTRYID_FROM_TABLE値を抽出するコードを変更する必要がありました。 – Alex

0

よろしくお願いします。 "for"を分割して同時に存在するOutlook.ContactItemオブジェクト(上記のコメントのような)の数を維持することは、最適化の一部でしたが、もっと重要なことは別のものです。

Items.SetColumns("EntryID")を使用することはできませんが、その理由を説明することはできません。実際には、EntryIDが常に返されるので、そこを渡すことはできません!したがって、他の軽量プロパティ( "Initials"を使用した)を渡すと、そのトリックが実行されます。 EntryIDとInitialsフィールドだけを設定してオブジェクトを返します。これにより、パフォーマンスが4〜5倍向上します。

チャンクの最適化と組み合わせると、バックグラウンドスレッドで6秒で完了しました(メインスレッドではさらに高速です)。

関連する問題