1

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.?

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45

2 Answers2

0

The error message indicates that you have reached the limit of items being opened simultaneously in your code (Outlook session). To avoid such errors you need to release underlying COM objects instantly in the code when you are done with them. The Marshal.ReleaseComObject method can help with that. Then just set an object reference to null (Nothing in VB.NET).

You need to avoid of using multiple dot notation like shown below:

myFolder = outlookNameSpace.Folders["email"]
.Folders["Inbox"].Folders["reports"].Folders[mailFolder];

which means the compiler creates an implicit variable to hold the result of the .Folders call, so you cannot explicitly release that variable. That object holds a reference to the folders collection object. Then you get another underlying COM object by using the indexer notation, a Folder instance is left unreleased. You need to keep each COM object in an explicit object references in the code and release it explicitly using the Marshal.ReleaseComObject method.

Do not ever use the foreach loop with Outlook collections! That loop holds a reference to all items until the loop exits. Use a for loop and release the items explicitly on each step of the loop immediately after you are done with that item.

The Garbage Collector (GC) does release COM objects if the corresponding .NET objects are not referenced (if they are set to null or Nothing). So you can use it to release unused COM objects. There are some drawbacks, though. First, it is an implicit way. That is, you can’t control the result. Secondly, you have to run the GC spending some time on this. More than that, the way in which .NET objects are stored in memory requires you to run the GC twice.

GC.Collect
GC.WaitForPendingFinalizers
GC.Collect
GC.WaitForPendingFinalizers

For more information how to use the that method you may take a look at the How to release Outlook objects correctly? thread.

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
0

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)
        }
Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78