使用Marshal.ReleaseComObject需要释放每个* Excel互操作对象吗? [英] Does *every* Excel interop object need to be released using Marshal.ReleaseComObject?

查看:607
本文介绍了使用Marshal.ReleaseComObject需要释放每个* Excel互操作对象吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

修改



另请参阅如何正确清理Excel互操作对象?我最近遇到了这个问题,它提供了很多洞察如何正确处理COM对象的问题。绝对检查超出第一(标记)答案,因为其他答案超出了简单的不要使用两个点和使用 ReleaseComObject 为每个com对象建议。



我首先回顾了这个问题,因为我意识到尽管注册和处理所有COM对象非常全面,但我的Excel实例仍然没有正确处置。事实证明,COM对象可以被创建完全不显而易见(即使您从不使用两个点也可能会错过COM对象)。另外,即使你是彻底的,如果你的项目增长超过一定的大小,那么错过COM对象的几率接近100%。当发生这种情况时,可能很难找到你错过的那个。上面提到的问题的答案提供了一些确保Excel实例绝对关闭的其他技巧。同时,我对我的 ComObjectManager (下面)做了一个小的(但重要的)更新,以反映我从上面链接的问题中学到的东西。



原始问题



我看过几个例子,其中 Marshal.ReleaseComObject() 与Excel Interop对象(即来自命名空间Microsoft.Office.Interop.Excel的对象)一起使用,但我已经看到它在不同程度上被使用。


$ b $我想知道我是否可以得到这样的东西:

  var application = new ApplicationClass(); 
尝试
{
//处理应用程序,工作簿,工作表,单元格等。
}
finally
{
Marashal。 ReleaseComObject(应用程序)
}

或者如果我需要释放创建的每个对象,在此方法中:

  public void CreateExcelWorkbookWithSingleSheet()
{
var application = new ApplicationClass();
var workbook = application.Workbooks.Add(_missing);
var worksheets = workbook.Worksheets;
for(var worksheetIndex = 1; worksheetIndex< workheets.Count; worksheetIndex ++)
{
var worksheet =(WorksheetClass)workheets [worksheetIndex];
工作表.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 = workheets.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()
{
//这两行代码将处理任何未引用的COM对象
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< workheets.Count; worksheetIndex ++)
{
var worksheet = com.Get< WorksheetClass>(()=>(WorksheetClass)worksheets [worksheetIndex ]);
工作表.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。由于它们不是垃圾回收的,所以父子层次结构并没有真正进入公式:即使释放父对象也不会减少任何子对象的引用计数。


Edit

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.

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.

Original Question

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);
}

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);

...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.

Update

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());
    }
}

Here's a usage example:

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();
    }
}

解决方案

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.

这篇关于使用Marshal.ReleaseComObject需要释放每个* Excel互操作对象吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆