Search code examples
c#outlookoffice-interopcom-interopoffice-automation

How to read outlook emails using Interop.Outlook in C# without crashing?


I am trying to read the emails from a shared mailbox which has multiple folders and files. But I get the below mentioned error and the application completes the task without returning any data.

Error message: error

Here is the method where the application stops working according to the logs:

private bool IsAvailable(ChartData data, out string avilableFileName)
        {
            Log.Information("3. Checking if folders and files are Available");
            foreach (MAPIFolder folder in mAPIFolder.Folders)
            {
                if (folder.FullFolderPath.Contains(data.MailFolder))
                {
                    var mailfolder = data.MailFolder;
                    myfolder=outlookNameSpace.Folders["email"].
                    Folders["Inbox"].Folders["reports"].Folders[mailfolder];
                    break;
                }
            }

            Log.Information("4. Checking if Creation Time > Received Time");

            avilableFileName = string.Empty;
            Items mailItems = myfolder.Items;
            mailItems.Sort("ReceivedTime", true);
            foreach (Object item in mailItems)
            {
                if (item is MailItem)
                {
                    MailItem mailItem = (MailItem)item;
                    if (mailItem.CreationTime > data.LastRun)
                    {
                        Log.Information("5. CreationTime > LastRun...");
                        foreach (var attachment in mailItem.Attachments)
                        {
                            if (attachment is Attachment)
                            {
                                var attach = (Attachment)attachment;
                                string attachmentName = attach.FileName;
                                if (attachmentName.ToLower().Contains(data.Attachmentname.ToLower())                                       
                                && attachmentName.ToLower().EndsWith
                                  (data.AttachmentExtension.ToLower()))
                                {
                                    Log.Information($"6. Attachment Name: {attachmentName}");
                                    avilableFileName = attachmentName;
                                    return true;
                                }
                            }
                            
                        }
                    }
                }
            }
            return false;
        }

This method is executed 200 times to check the availability of different filenames. This is how logs look at the end:

2023-04-25 17:52:30.829 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:30.911 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:31.013 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:31.086 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:31.157 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:31.232 +08:00 [INF] 5. CreationTime > LastRun...
2023-04-25 17:52:31.343 +08:00 [INF] 5. CreationTime > LastRun...

How can I optimize it or fix it so that my outlook does not lag and it is able to check the files availability successfully.?


Solution

  • Do not use foreach loop - it keeps all collection elements referenced. Use a for loop, avoid multiple dot notation, and release the objects using Marshal.ReleaseComObject as soon as you are done.

    Most importantly, never loop through all items in a folder. use Items.Find/FindNext or Items.Restrict.

    At the very least, use should use a restriction on CreationTime. OOM does not allow to create restriction on attachment names, you'd need to use Redemption for that (I am its author).

          for (int i = 1; i <= mailItems.Count; i++) 
          {
               object item = mailItems[i]; 
                if (item is MailItem mailItem)
                {
                    if (mailItem.CreationTime > data.LastRun)
                    {
                        Log.Information("5. CreationTime > LastRun...");
                        var attachments = mailItem.Attachments;
                        for(int j = 1; j <= attachments.Count; j++)
                        {
                            var attach = attachments[j];
                            string attachmentName = attach.FileName;
                            if (attachmentName.ToLower().Contains(data.Attachmentname.ToLower())                                       
                            && attachmentName.ToLower().EndsWith
                              (data.AttachmentExtension.ToLower()))
                            {
                                 Log.Information($"6. Attachment Name: {attachmentName}");
                                avilableFileName = attachmentName;
                                return true;
                            } 
                            Marshal.ReleaseComObject(attach);   
                        }
                        Marshal.ReleaseComObject(attachments);
                    }
                   Marshal.ReleaseComObject(mailItem)
                }
                Marshal.ReleaseComObject(item)
            }