您好我有一个Outlook com插件,它正在为我做一些简单的搜索技巧。我是将它放在一起的一部分,但我遇到了内存不足的问题。这个过程非常简单,基本上循环遍历outlook文件夹,检查每个mailItem是否匹配。给定循环重新初始化变量每次我都希望垃圾收集器跟上,但是当我看到内存时,它会丢失~10m / sec,直到系统内存不足并且我得到未处理的异常。
这是代码的一部分
private void FindInFolder(Outlook.MAPIFolder FolderToSearch)
{
Outlook.MailItem mailItem;
Outlook.MAPIFolder ParentFolder;
int counter = 0;
StatusBar.Text = "Searching in Folder " + FolderToSearch.FolderPath + "/" + FolderToSearch.Name;
StatusBar.Update();
this.Update();
foreach (COMObject item in FolderToSearch.Items)
{
counter++;
if (counter % 100 == 0)
{
StatusBar.Text = FolderToSearch.FolderPath + "/" + FolderToSearch.Name + " item " + counter + " of " + FolderToSearch.Items.Count;
StatusBar.Update();
if (counter % 1000 == 0)
{
GC.Collect();
}
}
if (item is Outlook.MailItem)
{
mailItem = item as Outlook.MailItem;
if (IsMatch(mailItem))
{
if (mailItem.Parent is Outlook.MAPIFolder)
{
ParentFolder = mailItem.Parent as Outlook.MAPIFolder;
ResultGrd.Rows.Add(mailItem.EntryID, ParentFolder.FolderPath, mailItem.SenderName, mailItem.Subject, mailItem.SentOn);
}
}
}
mailItem = null;
}
}
哪个电话
private Boolean IsMatch(Outlook.MailItem inItem)
{
Boolean subBool = false;
Boolean NameBool = false;
try
{
if (null != inItem)
{
if (SubjectTxt.Text != "")
{
if (inItem.Subject.Contains(SubjectTxt.Text))
{
subBool = true;
}
}
else
{
subBool = true;
}
if (NameTxt.Text != "")
{
if (inItem.Sender != null)
{
if (inItem.Sender.Name.Contains(NameTxt.Text))
{
NameBool = true;
}
}
}
else
{
NameBool = true;
}
return subBool && NameBool;
}
}
catch (System.Runtime.InteropServices.COMException ce)
{
if (ce.ErrorCode == -2147467259)
{
//DO nothing just move to the next one
}
else
{
MessageBox.Show("Crash in IsMatch error code = " + ce.ErrorCode + " " + ce.InnerException);
}
}
return false;
}
请原谅底部的所有错误捕获部分和GC.collect,我们尝试找出错误并释放内存。
另请注意,FindInFolder也会被新线程调用,因此我可以在继续搜索时与结果进行交互。
到目前为止我尝试过:
使变量本地化为函数而不是类,因此G可以检索,但是'item'中最常用的变量因为它是foreach的一部分,所以必须以这种方式声明它。
每1000个mailItems执行一次手动GC,这根本没有区别。
出于某种原因,它需要很多内存才能循环遍历项目,而GC从不会释放它们。
请注意我使用的是netoffice而不是VSTO for Com addin。
答案 0 :(得分:4)
首先:这是NetOffice代码,你不需要NetOffice中的Marshal.ReleaseComObject。 (此外,在此处调用ReleaseComObject无用)代替实例使用Dispose()。
请记住:NetOffice为您处理COM代理(这就是为什么它允许在NetOffice中使用两个2点)。 在你的情况下,它的内部存储为: // FolderToSearch - 物品 --Enumerator - 项目 - 项目 - ....
在每个循环结束时使用item.Dispose()删除/释放项目实例或在foreach之后使用以下内容
FolderToSearch.Dipose() // dispose文件夹实例和所有代理来自
FolderToSearch.DisposeChildInstances() //配置所有代理来自但保持文件夹实例活着
下一步: 这里的Items枚举器是一个自定义枚举器(由NetOffice提供) 然而,它适用于少量的项目,但不要做更多 较重的场景(可以交换服务器和数千个项目)。本地工作站/程序无法在内存中处理此问题。因此,Microsoft仅提供了良好的GetFirst / GetNext模式。在NetOffice中,它看起来像:
Outlook._Items items = FolderToSearch.Items;
COMObject item = null;
do
{
if (null == item)
item = items.GetFirst() as COMObject;
if (null == item)
break;
// do what you want here
item.Dispose();
item = items.GetNext() as COMObject;
} while (null != item);
这也应该有效。
答案 1 :(得分:3)
使用C#中的COM对象时,我使用了2个技巧来防止构建内存和COM对象引用计数:
System.Runtime.InteropServices.Marshal.ReleaseComObject()
释放COM对象。这迫使COM"释放"在对象上。foreach
遍历COM集合。 foreach
保留一个枚举器对象,该对象阻止释放其他对象。所以,而不是:
foreach (COMObject item in FolderToSearch.Items)
{
// ....
}
这样做:
Items items = FolderToSearch.Items;
try
{
for (int i = 0; i < items.Count; ++i)
{
COMObject item = items[i];
try
{
// work
}
finally
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(item);
}
}
}
finally
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(items);
}
这些提示帮助我减少了记忆和对象消耗。
免责声明:我不能证明这是否是一种好的做法。
答案 2 :(得分:0)
首先,我建议您使用Items类的Find / FindNext或Restrict方法,而不是遍历文件夹中的所有项目。例如:
Sub DemoFindNext()
Dim myNameSpace As Outlook.NameSpace
Dim tdystart As Date
Dim tdyend As Date
Dim myAppointments As Outlook.Items
Dim currentAppointment As Outlook.AppointmentItem
Set myNameSpace = Application.GetNamespace("MAPI")
tdystart = VBA.Format(Now, "Short Date")
tdyend = VBA.Format(Now + 1, "Short Date")
Set myAppointments = myNameSpace.GetDefaultFolder(olFolderCalendar).Items
Set currentAppointment = myAppointments.Find("[Start] >= """ & tdystart & """ and [Start] <= """ & tdyend & """")
While TypeName(currentAppointment) <> "Nothing"
MsgBox currentAppointment.Subject
Set currentAppointment = myAppointments.FindNext
Wend
End Sub
有关更多信息和示例代码,请参阅以下文章:
另外,您可能会发现Application类的AdvancedSearch方法很有帮助。下面列出了使用AdvancedSearch方法的主要好处:
其次,我总是建议立即发布底层的COM对象。使用System.Runtime.InteropServices.Marshal.ReleaseComObject释放Outlook对象。然后在Visual Basic中将变量设置为Nothing(C#中为null)以释放对该对象的引用。您可以在Systematically Releasing Objects文章中详细了解相关内容。
如果要使用GC,则需要两次调用Collect和WaitForPendingFinalizers方法。
马特,你仍然没有释放代码中的所有对象。例如:
for (int i = 0; i < FolderToSearch.Items.Count; ++i)
{
COMObject item = FolderToSearch.Items[i];
Folder类的Items属性返回应该在之后释放的相应类的实例。我看到至少两行代码增加了参考计数器。