*每个* Excel互操作对象是否需要使用Marshal.ReleaseComObject发布?
编辑
请参阅如何正确清理Excel互操作对象? 。 最近我遇到了这个问题,它提供了很多关于如何正确处理COM对象的问题的见解。 肯定检查超出第一个(标记)的答案,因为其他答案超越了简单的“不要使用两个点”和“使用ReleaseComObject
为每个COM对象”的build议。
我首先回顾了这个问题,因为我意识到尽pipe在注册和部署所有COM对象方面非常全面,但是我的Excel实例仍然没有得到妥善处理。 事实certificate,有一些方法可以创build完全不明显的COM对象(即,即使您从不使用两个点,也可能会错过COM对象)。 此外,即使你是彻底的,如果你的项目增长超过一定的规模,错过一个COM对象的机会接近100%。 当发生这种情况时,很难find你错过的那个。 上面提到的问题的答案提供了一些确保Excel实例被closures的其他技巧。 同时,我对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) }
或者,如果我需要释放每个创build的对象,如下所示:
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
)的对象,我会最终与内存泄漏或幽灵进程。
任何有识之士将不胜感激。
更新
根据目前的答案,这听起来像我真的需要释放我创build的每一个COM对象。 我借此机会构build了一个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。 由于它们没有被垃圾收集,所以父子层次并没有真正进入方程:即使你释放父对象,也不会减less任何子对象的引用计数。
您应该在您的代码中使用的每个COM对象上调用Marshal.ReleaseComObject,而不仅仅是主应用程序对象。