问题描述
编辑
另请参阅 我该如何正确清理 Excel 互操作对象?.我最近遇到了这个问题,它为如何正确处理 COM 对象的问题提供了很多见解.一定要检查第一个(标记的)答案之外的答案,因为其他答案超出了简单的不要使用两个点"和对每个 com 对象使用 ReleaseComObject
"的建议.
Please see also How do I properly clean up Excel interop objects?. I recently came across this question, and it provided a lot of insight into the problem of how to properly dispose of COM objects. Definitely check beyond the first (marked) answer, because the other answers go beyond the simple "don't use two dots" and "use ReleaseComObject
for every com object" advice.
我首先重新审视了这个问题,因为我意识到,尽管我非常彻底地注册和处置我的所有 COM 对象,但我的 Excel 实例仍然没有被正确处置.事实证明,有一些方法可以创建完全不明显的 COM 对象(即,即使您从不使用两个点,您也可能会错过 COM 对象).此外,即使您是彻底的,如果您的项目超过一定规模,丢失 COM 对象的可能性也接近 100%.当这种情况发生时,很难找到你错过的那个.上面链接的问题的答案提供了一些其他技术来确保 Excel 实例肯定被关闭.同时,我对我的 ComObjectManager
(如下)做了一个小的(但重要的)更新,以反映我从上面链接的问题中学到的东西.
I revisited this question in the first place because I realized that, despite being very thorough about registering and disposing all my COM objects, my Excel instances still weren't being properly disposed. It turns out, there are ways COM objects can be created that are completely non-obvious (i.e., you can miss COM objects even if you never use two dots). In addition, even if you are thorough, if your project grows beyond a certain size, the chance of missing a COM object approaches 100%. And it can be very hard to find the one you missed when that happens. The answers to the question linked above provide some other techniques for making sure the Excel instance definitely gets closed. Meanwhile, I've made a small (but significant) update to my ComObjectManager
(below) to reflect what I learned from the question linked above.
原始问题
我见过几个例子,其中 Marshal.ReleaseComObject()
与 Excel 互操作对象(即来自命名空间 Microsoft.Office.Interop.Excel 的对象)一起使用,但我见过它使用程度不同.
I've seen several examples where Marshal.ReleaseComObject()
is used with Excel Interop objects (i.e., objects from namespace Microsoft.Office.Interop.Excel), but I've seen it used to various degrees.
我想知道我是否可以摆脱这样的事情:
I'm wondering if I can get away with something like this:
var application = new ApplicationClass();
try
{
// do work with application, workbooks, worksheets, cells, etc.
}
finally
{
Marashal.ReleaseComObject(application)
}
或者如果我需要释放每个创建的对象,就像在这个方法中一样:
Or if I need to release every single object created, as in this method:
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 爱好者,我真的很想做这样的事情:
What prompted me to ask this question is that, being the LINQ devotee I am, I really want to do something like this:
var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name);
...但我担心如果我不释放每个工作表 (ws
) 对象,最终会导致内存泄漏或幽灵进程.
...but I'm concerned I'll end up with memory leaks or ghost processes if I don't release each worksheet (ws
) object.
对此的任何见解将不胜感激.
Any insight on this would be appreciated.
更新
根据目前的答案,听起来我确实需要释放我创建的每一个 com 对象.我借此机会构建了一个 ComObjectManager
类,以便更轻松地处理这个令人头疼的问题.您必须记住每次实例化一个新的 com 对象时都使用 Get()
方法,但如果您这样做了,它会为您处理其他所有事情.如果您发现它有任何问题,请告诉我(或者如果可以,请编辑并发表评论).代码如下:
Based on the answers so far, it sounds like I really do need to release every single com object I create. I took the opportunity to build a ComObjectManager
class to make it a little easier to deal with this headache. You have to remember to use the Get()
method each time you instantiate a new com object, but if you do, it will take care of everything else for you. Please let me know if you see any problems with it (or edit and leave a comment if you are able). Here's the code:
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.由于它们没有被垃圾回收,因此父子层次结构并没有真正进入等式:即使您释放父对象,它也不会减少任何子对象的引用计数.
I believe you would have to call ReleaseComObject on each COM object. Since they're not garbage-collected, the parent-child hierarchy doesn't really come into the equation: even if you release the parent object it does not decrement the reference count on any child objects.
这篇关于*每个* Excel 互操作对象都需要使用 Marshal.ReleaseComObject 释放吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!