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

问题描述 投票:0回答:3

我正在尝试从具有多个文件夹和文件的共享邮箱中读取电子邮件。但是我得到了下面提到的错误,应用程序完成了任务而不返回任何数据。

错误信息: error

根据日志,这是应用程序停止工作的方法:

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;
        }

此方法执行 200 次以检查不同文件名的可用性。 这是日志最后的样子:

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

我如何优化或修复它,以便我的 outlook 不会滞后并且能够成功检查文件可用性。?

c# outlook office-interop com-interop office-automation
3个回答
0
投票

private bool IsAvailable(ChartData data, out string availableFileName)
{
Log.Information("3. Checking if folders and files are Available");
MAPIFolder myFolder = null;
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;
}
}


if (myFolder == null)
{
    availableFileName = string.Empty;
    return false;
}

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

    var filter = $"[ReceivedTime] >= '{data.LastRun.ToString("MM/dd/yyyy HH:mm:ss")}'";
    var searchFolder = myFolder.Items.Restrict(filter);
    searchFolder.Sort("ReceivedTime", true);

    foreach (var item in searchFolder)
    {
        if (item is MailItem mailItem)
        {
            if (mailItem.CreationTime > data.LastRun)
            {
                Log.Information("5. CreationTime > LastRun...");
                foreach (Attachment attachment in mailItem.Attachments)
                {
                    if (attachment.FileName.ToLower().Contains(data.Attachmentname.ToLower())
                        && attachment.FileName.ToLower().EndsWith(data.AttachmentExtension.ToLower()))
                    {
                        Log.Information($"6. Attachment Name: {attachment.FileName}");
                        availableFileName = attachment.FileName;
                        return true;
                    }
                }
            }
        }
    }

    availableFileName = string.Empty;
    return false;
}
catch (Exception ex)
{
    Log.Error(ex, "Error while checking for availability");
    availableFileName = string.Empty;
    return false;
}
finally
{
    if (myFolder != null) Marshal.ReleaseComObject(myFolder);
}


0
投票

错误消息表明您已达到代码中同时打开的项目的限制(Outlook 会话)。为避免此类错误,您需要在使用完它们后立即在代码中释放底层 COM 对象。 Marshal.ReleaseComObject 方法可以帮助解决这个问题。然后只需将对象引用设置为

null
(VB.NET中的
Nothing
)。

您需要避免使用如下所示的多点符号:

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

这意味着编译器创建一个隐式变量来保存

.Folders
调用的结果,因此您不能显式释放该变量。该对象包含对文件夹集合对象的引用。然后你通过使用索引器符号得到另一个底层 COM 对象,一个
Folder
实例未发布。您需要将每个 COM 对象保存在代码中的显式对象引用中,并使用
Marshal.ReleaseComObject
方法显式释放它。

永远不要对 Outlook 集合使用

foreach
循环!该循环持有对所有项目的引用,直到循环退出。使用 for 循环并在完成该项目后立即在循环的每个步骤中显式释放项目。

如果相应的 .NET 对象未被引用(如果它们被设置为 null 或 Nothing),垃圾收集器 (GC) 会释放 COM 对象。所以你可以用它来释放未使用的 COM 对象。但是,也有一些缺点。首先,它是一种隐式方式。也就是说,您无法控制结果。其次,你必须花一些时间来运行 GC。不仅如此,.NET 对象在内存中的存储方式需要您运行两次 GC。

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

有关如何使用该方法的更多信息,您可以查看如何正确释放 Outlook 对象? 线程。


0
投票

不要使用

foreach
循环 - 它会保留所有引用的集合元素。使用
for
循环,避免多点符号,并在完成后立即使用
Marshal.ReleaseComObject
释放对象。

最重要的是,never 遍历文件夹中的所有项目。使用 Items.Find/

FindNext
Items.Restrict.

最起码,使用应该对

CreationTime
使用限制。 OOM 不允许对附件名称进行限制,为此你需要使用 Redemption(我是它的作者)。

      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)
        }
© www.soinside.com 2019 - 2024. All rights reserved.