user12069329
user12069329

Reputation: 23

Performance issue with For loop in VSTO C# while getting emails from outlook

I am having issues with looping through the emails in outlook inbox folders (C# VSTO add in Outlook). I was using foreach loop but it consumes a lot of memory and then causes the exception: out of system/memory resources. So, I am using for loop now which does not cause that error but for one of the folders it is really slow, reads less than 5-8 emails per second. This folder contains a bit more than 100,000 emails. For all other folders the speed is between 30-35 emails.

The code is:

Static void IterateMessages(Outlook.Folder folder){

        int tempCount = folder.Items.Count;
        if (folder.Items != null)
        {
            Object item;
            for (int k = 1; k <= tempCount; k++)
            {
                item = folder.Items[k];
                if (item is Outlook.MailItem)
                {
                    emailCount++;
                    try
                    {
                        SaveAttachment(item);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("An error occurred Iterate Message: '{0}'", e);
                    }
                }
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(item);
                item = null;
            }
            tempCount = 0;
        }
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(folder);
    }

It would be great if someone can help with this issue. While using foreach loop the speed was approx. 40-45 emails per second. Also, this method is called from another for loop which iterates through the folders, Thanks in advance

Upvotes: 1

Views: 299

Answers (2)

Eugene Astafiev
Eugene Astafiev

Reputation: 49395

Properly releasing underlying COM objects is not enough for your goal. Instead of iterating over all items in the folder you need to use the Find/FindNext or Restrict methods of the Items class to find items with attachments and only then you can iterate over all items with attachments only. The query may use the SQL notation (VBA):

query ="@SQL=" & chr(34) & "urn:schemas:httpmail:hasattachment" & chr(34) & "=1"

Read more about these methods in the following articles:

Also you may find the AdvancedSearch method of the Application class helpful. The key benefits of using the AdvancedSearch method in Outlook are:

  • The search is performed in another thread. You don’t need to run another thread manually since the AdvancedSearch method runs it automatically in the background.
  • Possibility to search for any item types: mail, appointment, calendar, notes etc. in any location, i.e. beyond the scope of a certain folder. The Restrict and Find/FindNext methods can be applied to a particular Items collection (see the Items property of the Folder class in Outlook).
  • Full support for DASL queries (custom properties can be used for searching too). You can read more about this in the Filtering article in MSDN. To improve the search performance, Instant Search keywords can be used if Instant Search is enabled for the store (see the IsInstantSearchEnabled property of the Store class).
  • You can stop the search process at any moment using the Stop method of the Search class.

See Advanced search in Outlook programmatically: C#, VB.NET for more information.

Upvotes: 1

Dmitry Streblechenko
Dmitry Streblechenko

Reputation: 66215

Firstly, looping through all items in a folder is not good idea. Do you really need to do that?

You keep retrieving the Items collection in the loop (folder.Items[k]). Cache Items before entering the loop:

Items items = folder.Items;
for (int k = 1; k <= items.Count; k++)
{
  object item = items[k];
  ...
}
Marshal.ReleaseComObject(items);

You are leaking references (they will later will released by the GC of course), but since you are using FinalReleaseComObject...

object item = items[k];
MailItem mailItem = item as MailItem;
if (mailItem != null)
{
  ...
  Marshal.ReleaseComObject(mailItem);
}
Marshal.ReleaseComObject(item);

Upvotes: 0

Related Questions