[英]Does *every* Excel interop object need to be released using Marshal.ReleaseComObject?
编辑
另请参阅如何正确清理Excel互操作对象? 。 我最近遇到了这个问题,它提供了很多关于如何正确处理COM对象的问题的见解。 绝对检查超出第一个(标记的)答案,因为其他答案超出了简单的“不使用两个点”和“为每个com对象使用ReleaseComObject
”的建议。
我首先重新审视了这个问题,因为我意识到尽管注册和处理所有COM对象非常彻底,但我的Excel实例仍然没有得到妥善处理。 事实证明,有些方法可以创建完全不明显的COM对象(即,即使您从不使用两个点,也可以错过COM对象)。 此外,即使您是彻底的,如果您的项目增长超过一定的大小,错过COM对象的机会接近100%。 当发生这种情况时,很难找到你错过的那个。 上面链接的问题的答案提供了一些其他技术,以确保Excel实例肯定关闭。 同时,我对我的ComObjectManager
(下面)做了一个小的(但很重要的)更新,以反映我从上面链接的问题中学到的东西。
原始问题
我看过几个例子,其中Marshal.ReleaseComObject()
与Excel Interop对象(即名称空间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();
}
}
我相信你必须在每个COM对象上调用ReleaseComObject。 由于它们不是垃圾收集的,因此父子层次结构并不真正进入等式:即使释放父对象,它也不会减少任何子对象的引用计数。
您应该在代码中使用的每个COM对象上调用Marshal.ReleaseComObject,而不仅仅是主应用程序对象。
不需要。您不必释放单个COM对象。 请参阅以下答案: 使用IDisposable清理Excel Interop对象
总结答案:除非程序崩溃,否则垃圾收集器会在感觉到它时处理它们。 你需要注意的是:
现在,如果要在方法调用结束后立即释放所有COM对象,则可以简单地调用GC.Collect(); GC.WaitForPendingFinalizers();
GC.Collect(); GC.WaitForPendingFinalizers();
但是你需要在这个OUTSIDE中调用创建COM对象的方法。 同样,如果您正在调试应用程序,它可能无法按预期工作,但它将在发布模式下工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.