4

こんにちは、いくつかの簡単な検索トリックを実行する Outlook com アドインがあります。組み立ての途中ですが、メモリ不足の問題が発生しています。プロセスは非常に単純で、基本的に Outlook フォルダーをループして、各 mailItem が一致するかどうかをチェックします。ループが変数を再初期化すると、ガベージコレクターが追いつくと予想されるたびに変数が再初期化されますが、メモリを監視すると、システムがメモリ不足になり、未処理の例外が発生するまで、メモリは約 10m/秒失われます。

これはコードの一部です

private void FindInFolder(Outlook.MAPIFolder FolderToSearch)
    {
        Outlook.MailItem mailItem;
        Outlook.MAPIFolder ParentFolder;

        int counter = 0;

        StatusBar.Text = "Searching in Folder " + FolderToSearch.FolderPath + "/" + FolderToSearch.Name;
        StatusBar.Update();
        this.Update();

        foreach (COMObject item in FolderToSearch.Items)
        {
            counter++;
            if (counter % 100 == 0)
            {
                StatusBar.Text = FolderToSearch.FolderPath + "/" + FolderToSearch.Name + " item " + counter + " of " + FolderToSearch.Items.Count;
                StatusBar.Update();
                if (counter % 1000 == 0)
                {
                    GC.Collect();
                }
            }
            if (item is Outlook.MailItem)
            {
                mailItem = item as Outlook.MailItem;
                if (IsMatch(mailItem))
                {
                    if (mailItem.Parent is Outlook.MAPIFolder)
                    {
                            ParentFolder = mailItem.Parent as Outlook.MAPIFolder;
                            ResultGrd.Rows.Add(mailItem.EntryID, ParentFolder.FolderPath, mailItem.SenderName, mailItem.Subject, mailItem.SentOn);
                    }
                }
            }
            mailItem = null;
        }
    }

どの呼び出し

        private Boolean IsMatch(Outlook.MailItem inItem)
    {
        Boolean subBool = false;
        Boolean NameBool = false;

        try
        {
            if (null != inItem)
            {
                if (SubjectTxt.Text != "")
                {
                    if (inItem.Subject.Contains(SubjectTxt.Text))
                    {
                        subBool = true;
                    }
                }
                else
                {
                    subBool = true;                    
                }

                if (NameTxt.Text != "")
                {
                    if (inItem.Sender != null)
                    {
                        if (inItem.Sender.Name.Contains(NameTxt.Text))
                        {
                            NameBool = true;
                        }
                    }
                }
                else 
                {
                    NameBool = true;
                }

                return subBool && NameBool;

            }
        }
        catch (System.Runtime.InteropServices.COMException ce)
        {
            if (ce.ErrorCode == -2147467259)
            {
                //DO nothing just move to the next one
            }
            else
            {
                MessageBox.Show("Crash in IsMatch error code = " + ce.ErrorCode + " " + ce.InnerException);
            }
        }
        return false;
    }

下部にあるすべてのエラーキャッチ部分と GC.collect は、何が問題なのかを突き止めてメモリを解放するための私の試みの一部であることをお許しください。

また、FindInFolder は新しいスレッドによって呼び出されるので、検索を続けながら結果を操作できることに注意してください。

私がこれまでに試したこと:

変数をクラスではなく関数に対してローカルにして、G で取得できるようにしますが、「item」で最も使用される変数は foreach の一部であるため、そのように宣言する必要があります。

1000 個の mailItem ごとに手動 GC が実行されますが、これはまったく違いがありませんでした。

何らかの理由で、アイテムをループするだけで大​​量のメモリが必要になり、GC はアイテムを解放しません。

Comアドイン用のVSTOではなく、netofficeを使用していることにも注意してください。

4

3 に答える 3

4

まず第一に、これは NetOffice コードであり、NetOffice に Marshal.ReleaseComObject は必要ありません。(さらに、ここで ReleaseComObject を呼び出しても意味がありません) インスタンスには Dispose() を使用してください。

心に留めておいてください: NetOffice は COM プロキシを処理します (NetOffice で 2 つの 2 ドットを使用できるのはそのためです)。あなたの場合、その内部は次のように保存されます: // FolderToSearch -- Items --Enumerator -- Item -- Item -- ....

各ループの最後で item.Dispose() を使用してアイテム インスタンスを削除/解放するか、foreach の後に次を使用します。

FolderToSearch.Dipose() // フォルダ インスタンスとそこにあるすべてのプロキシを破棄します

FolderToSearch.DisposeChildInstances() // そこにあるすべてのプロキシを破棄しますが、フォルダ インスタンスは有効にします

次へ: ここのアイテム列挙子はカスタム列挙子です (NetOffice によって提供されます)。ただし、少量のアイテムでは問題なく動作しますが、より重いシナリオではこれを行わないでください (サーバーと数千のアイテムを交換する可能性があります)。ローカル ワークステーション/プログラムはメモリ内でこれを処理できません。このため、Microsoft はよく知られている GetFirst/GetNext パターンのみを提供しています。NetOffice では次のようになります。

Outlook._Items items = FolderToSearch.Items;
COMObject item = null;
do
{
    if (null == item)
       item = items.GetFirst() as COMObject;
    if (null == item)
       break;

    // do what you want here

    item.Dispose();
    item = items.GetNext() as COMObject;
} while (null != item);

これもうまくいくはずです。

于 2015-04-03T14:27:32.097 に答える
0

まず、フォルダー内のすべてのアイテムを反復処理するのではなく、Items クラスのFind / FindNextまたはRestrictメソッドを使用することをお勧めします。例えば:

Sub DemoFindNext() 
 Dim myNameSpace As Outlook.NameSpace 
 Dim tdystart As Date 
 Dim tdyend As Date 
 Dim myAppointments As Outlook.Items 
 Dim currentAppointment As Outlook.AppointmentItem 

 Set myNameSpace = Application.GetNamespace("MAPI") 
 tdystart = VBA.Format(Now, "Short Date") 
 tdyend = VBA.Format(Now + 1, "Short Date") 
 Set myAppointments = myNameSpace.GetDefaultFolder(olFolderCalendar).Items 
 Set currentAppointment = myAppointments.Find("[Start] >= """ & tdystart & """ and [Start] <= """ & tdyend & """") 
 While TypeName(currentAppointment) <> "Nothing" 
   MsgBox currentAppointment.Subject 
   Set currentAppointment = myAppointments.FindNext 
 Wend 
End Sub

詳細とサンプル コードについては、次の記事を参照してください。

また、Application クラスのAdvancedSearchメソッドが役立つ場合もあります。AdvancedSearch メソッドを使用する主な利点は次のとおりです。

  • 検索は別のスレッドで実行されます。AdvancedSearch メソッドはバックグラウンドでスレッドを自動的に実行するため、別のスレッドを手動で実行する必要はありません。
  • メール、予定、カレンダー、メモなど、特定のフォルダの範囲を超えて任意の場所にある任意のタイプのアイテムを検索する可能性。Restrict および Find/FindNext メソッドは、特定の Items コレクションに適用できます (Outlook の Folder クラスの Items プロパティを参照してください)。
  • DASL クエリの完全なサポート (カスタム プロパティも検索に使用できます)。詳細については、MSDN のフィルタリングの記事を参照してください。検索パフォーマンスを向上させるために、ストアでインスタント検索が有効になっている場合は、インスタント検索キーワードを使用できます (Store クラスの IsInstantSearchEnabled プロパティを参照してください)。
  • Search クラスの Stop メソッドを使用して、いつでも検索プロセスを停止できます。

2 つ目は、基になる COM オブジェクトをすぐに解放することを常に提案しています。System.Runtime.InteropServices.Marshal.ReleaseComObject を使用して、使い終わった Outlook オブジェクトを解放します。次に、変数を Visual Basic では Nothing (C# では null) に設定して、オブジェクトへの参照を解放します。詳細については、Systematically Release Objectsの記事を参照してください。

GC を使用する場合は、Collect メソッドと WaitForPendingFinalizers メソッドを 2 回呼び出す必要があります。

マット、あなたはまだコード内のすべてのオブジェクトを解放していません。例えば:

for (int i = 0; i < FolderToSearch.Items.Count; ++i)
{
   COMObject item = FolderToSearch.Items[i];

Folder クラスの Items プロパティは、後で解放する必要がある対応するクラスのインスタンスを返します。参照カウンターが増加する少なくとも 2 行のコードが表示されます。

于 2015-03-18T13:54:02.113 に答える