我正在尝试从具有多个文件夹和文件的共享邮箱中读取电子邮件。但是我得到了下面提到的错误,应用程序完成了任务而不返回任何数据。
根据日志,这是应用程序停止工作的方法:
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 不会滞后并且能够成功检查文件可用性。?
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);
}
错误消息表明您已达到代码中同时打开的项目的限制(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 对象? 线程。
不要使用
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)
}