如何优化C# Interop.Outlook读取共享邮箱邮件代码以避免卡顿崩溃?
问题描述
使用C#的Interop.Outlook读取带有多文件夹的共享邮箱邮件时,出现程序卡顿、无数据返回的问题。日志显示程序执行到5. CreationTime > LastRun...阶段后停滞,该方法需重复执行200次以检查不同文件名的可用性。
相关IsAvailable方法代码:
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; }
停滞阶段的日志:
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...
优化与修复方案
1. 简化文件夹定位逻辑,避免冗余遍历
原代码先遍历所有文件夹,随后又通过层级路径重新获取目标文件夹,操作冗余且易出错。直接通过共享邮箱名称定位目标文件夹,减少IO开销:
// 替换原文件夹遍历代码 var sharedMailbox = outlookNameSpace.Folders["email"]; // 共享邮箱名称 myfolder = sharedMailbox.Folders["Inbox"].Folders["reports"].Folders[data.MailFolder]; // 可选:添加异常捕获,避免文件夹不存在导致崩溃 try { myfolder = sharedMailbox.Folders["Inbox"].Folders["reports"].Folders[data.MailFolder]; } catch (System.Runtime.InteropServices.COMException ex) { Log.Error($"找不到目标文件夹 {data.MailFolder}: {ex.Message}"); avilableFileName = string.Empty; return false; }
2. 使用Restrict预过滤邮件,减少遍历量
原代码遍历所有邮件后再判断时间条件,效率极低。利用Outlook的Restrict方法提前筛选出符合CreationTime > data.LastRun的邮件:
// 替换原Items获取与排序逻辑 string timeFilter = $"[CreationTime] > '{data.LastRun.ToString("g")}'"; // 适配Outlook日期格式 Items filteredItems = myfolder.Items.Restrict(timeFilter); filteredItems.Sort("ReceivedTime", true); // 遍历过滤后的邮件 foreach (Object item in filteredItems) { if (item is MailItem mailItem) // 模式匹配简化类型转换 { Log.Information("5. CreationTime > LastRun..."); // 附件处理逻辑... } }
3. 优化附件匹配逻辑,减少重复计算
提前将目标附件名称和扩展名转为小写,避免每次匹配时重复调用ToLower();同时直接遍历Attachment类型,减少类型判断开销:
// 预处理目标匹配字符串 var targetName = data.Attachmentname.ToLower(); var targetExt = data.AttachmentExtension.ToLower(); // 替换原附件遍历代码 foreach (Attachment attach in mailItem.Attachments) { string attachNameLower = attach.FileName.ToLower(); if (attachNameLower.Contains(targetName) && attachNameLower.EndsWith(targetExt)) { Log.Information($"6. Attachment Name: {attach.FileName}"); avilableFileName = attach.FileName; return true; } }
4. 手动释放COM对象,解决内存泄漏卡顿
Interop.Outlook的COM对象未正确释放会导致Outlook内存溢出、卡顿。添加释放方法并在使用后清理:
// 新增COM对象释放方法 private void ReleaseComObject(object obj) { if (obj != null) { System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); obj = null; } } // 在方法内使用后释放相关对象 // ... ReleaseComObject(filteredItems); ReleaseComObject(mailItem); ReleaseComObject(attach); ReleaseComObject(myfolder);
5. 批量处理替代200次重复调用
原方法需执行200次,改为一次性收集所有待检查的附件规则,批量遍历邮件匹配,减少重复的Outlook交互:
// 示例:提前收集所有需要检查的附件规则 List<(string Name, string Ext)> targetAttachments = new List<(string, string)>(); // 填充规则:targetAttachments.Add((data1.Attachmentname, data1.AttachmentExtension)); // 批量遍历匹配 foreach (MailItem mailItem in filteredItems) { foreach (Attachment attach in mailItem.Attachments) { string attachNameLower = attach.FileName.ToLower(); foreach (var target in targetAttachments) { string targetName = target.Name.ToLower(); string targetExt = target.Ext.ToLower(); if (attachNameLower.Contains(targetName) && attachNameLower.EndsWith(targetExt)) { // 记录找到的文件,可根据需求存储对应的数据 // ... } } } ReleaseComObject(mailItem); }
6. 添加异常捕获,定位隐藏错误
原代码未处理COM异常,卡顿可能是因为未捕获的异常导致程序停滞。在方法内添加异常处理:
try { // 原方法内的所有逻辑 } catch (System.Runtime.InteropServices.COMException ex) { Log.Error($"Outlook操作异常: {ex.Message}, 错误码: {ex.ErrorCode}"); // 释放所有已创建的COM对象 // ... avilableFileName = string.Empty; return false; } catch (Exception ex) { Log.Error($"未知异常: {ex.Message}"); avilableFileName = string.Empty; return false; }
内容的提问来源于stack exchange,提问作者Shishank Jain
相关产品推荐
相关产品推荐

