编辑是否需要使用Marshal.ReleaseComObject释放*每个* Excel互操作对象?
还请How do I properly clean up Excel interop objects?看到的。我最近遇到了这个问题,它提供了很多关于如何正确处理COM对象的问题。绝对检查超出第一个(标记的)答案,因为其他答案超出了简单的“不要使用两个点”和“为每个COM对象使用ReleaseComObject
”的建议。
我首先重新讨论了这个问题,因为我意识到尽管在注册和处理所有COM对象方面非常全面,但我的Excel实例仍然没有正确处理。事实证明,有很多方法可以创建完全不明显的COM对象(即,即使您从不使用两个点,也可能会错过COM对象)。此外,即使你是彻底的,如果你的项目增长超过一定的规模,错过一个COM对象的机会接近100%。当发生这种情况时,很难找到你错过的那个。上面链接的问题的答案提供了一些确保Excel实例完全关闭的其他技巧。同时,我对ComObjectManager
(下文)做了一个小的(但是很重要的)更新,以反映我从上面提到的问题中学到了什么。
原始的问题
我见过几个例子Marshal.ReleaseComObject()
使用与Excel互操作对象(即,从命名空间中的Microsoft.Office.Interop.Excel对象),但我已经看到了使用各种度。
我想知道如果我能逃脱这样的:
var application = new ApplicationClass();
try
{
// do work with application, workbooks, worksheets, cells, etc.
}
finally
{
Marashal.ReleaseComObject(application)
}
或者,如果我需要释放每一个对象创建的,在这个方法:
public void CreateExcelWorkbookWithSingleSheet()
{
var application = new ApplicationClass();
var workbook = application.Workbooks.Add(_missing);
var worksheets = workbook.Worksheets;
for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
{
var worksheet = (WorksheetClass)worksheets[worksheetIndex];
worksheet.Delete();
Marshal.ReleaseComObject(worksheet);
}
workbook.SaveAs(
WorkbookPath, _missing, _missing, _missing, _missing, _missing,
XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
workbook.Close(true, _missing, _missing);
application.Quit();
Marshal.ReleaseComObject(worksheets);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(application);
}
是什么促使我问这个问题是,作为LINQ的奉献者,我真的很想做这样的事情:
var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name);
...但我担心如果我不释放每个工作表(ws
)对象,我将最终发生内存泄漏或幽灵进程。
任何有关这方面的意见将不胜感激。
更新
基于答案为止,这听起来像我确实需要释放每一个COM对象创建。我借此机会创建了一个ComObjectManager
课程,以便处理这个令人头疼的问题。每次你实例化一个新的com对象时,你都必须记得使用Get()
方法,但如果你这样做,它会为你处理所有其他事情。如果您发现任何问题,请告诉我(或编辑并留下评论,如果可以的话)。下面的代码:
public class ComObjectManager : IDisposable
{
private Stack<object> _comObjects = new Stack<object>();
public TComObject Get<TComObject>(Func<TComObject> getter)
{
var comObject = getter();
_comObjects.Push(comObject);
return comObject;
}
public void Dispose()
{
// these two lines of code will dispose of any unreferenced COM objects
GC.Collect();
GC.WaitForPendingFinalizers();
while (_comObjects.Count > 0)
Marshal.ReleaseComObject(_comObjects.Pop());
}
}
下面是一个使用示例:
public void CreateExcelWorkbookWithSingleSheet()
{
using (var com = new ComObjectManager())
{
var application = com.Get<ApplicationClass>(() => new ApplicationClass());
var workbook = com.Get<Workbook>(() => application.Workbooks.Add(_missing));
var worksheets = com.Get<Sheets>(() => workbook.Worksheets);
for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
{
var worksheet = com.Get<WorksheetClass>(() => (WorksheetClass)worksheets[worksheetIndex]);
worksheet.Delete();
}
workbook.SaveAs(
WorkbookPath, _missing, _missing, _missing, _missing, _missing,
XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
workbook.Close(true, _missing, _missing);
application.Quit();
}
}
对于负责释放对象的ComObjectManager类+1。 – 2011-03-25 06:25:10
但是,由于编译器在后台创建大量匿名方法,因此ComObjectManager的解决方案可能会导致程序集的大小增加。 – 2011-03-25 06:50:53
与经理的好主意。 – 2011-04-01 13:47:51