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

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

问题描述

修改

请参见<一href="http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c-sharp">How妥善清理在C#的Excel互操作的对象。最近,我遇到了这个问题,它提供了很多有识之士到如何正确处置COM对象的问题。肯定检查超越第一(标记)的答案,因为其他的答案超越了简单的不使用两个点和使用 ReleaseComObject的为每个COM对象的建议。

我在第一时间重新审视这个问题,因为我意识到,尽管是非常彻底的有关注册和处理我所有的COM对象,我的Excel的情况下,仍然没有被妥善处理。事实证明,有很多方法可以创建COM对象是完全无显着性(也就是说,你可以错过COM对象,即使你从来没有使用两个点)。此外,即使你是彻底的,如果你的项目增长超过一定的规模,缺少一个COM对象的几率接近100%。它可以是很难找到你错过了当这种情况发生的人。在回答上面链接的问题提供了一些其他的技术来确保对Excel实例肯定被封闭。同时,我做了一个小的(但显著)更新到我的 ComObjectManager (下图),以反映我从上面链接的问题的经验教训。

原始的问题

我见过的几个实例,其中对Marshal.ReleaseComObject()用于与Excel互操作对象(即,从命名空间中的Microsoft.Office.Interop.Excel对象),但我已经看到了用于不同程度。

我想知道如果我可以逃脱这样的:

  VAR应用=新ApplicationClass();
尝试
{
    //做应用,工作簿,工作表,电池等方面的工作
}
最后
{
    Marashal.ReleaseComObject(应用程序)
}
 

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

 公共无效CreateExcelWorkbookWithSingleSheet()
{
    VAR应用=新ApplicationClass();
    VAR工作簿= application.Workbooks.Add(_missing);
    VAR工作表= workbook.Worksheets;
    对于(VAR worksheetIndex = 1; worksheetIndex&LT; worksheets.Count; worksheetIndex ++)
    {
        VAR工作表=(WorksheetClass)工作表[worksheetIndex]
        worksheet.Delete();
        对Marshal.ReleaseComObject(表);
    }
    workbook.SaveAs(
        WorkbookPath,_missing,_missing,_missing,_missing,_missing,
        XlSaveAsAccessMode.xlExclusive,_missing,_missing,_missing,_missing,_missing);
    workbook.Close(真,_missing,_missing);
    application.Quit();
    对Marshal.ReleaseComObject(工作表);
    对Marshal.ReleaseComObject(练习册);
    对Marshal.ReleaseComObject(应用);
}
 

什么促使我问这个问题是这样的,作为LINQ奉献我,我真的很想做这样的事情:

  VAR worksheetNames = worksheets.Cast&LT;表&GT;()选择(WS =&GT; ws.Name)。
 

...但我担心,我会结束了内存泄漏,或鬼的过程,如果我不释放每个工作表( WS )的对象。

对此有何见解将AP preciated。

更新

基于答案

到目前为止,这听起来像我确实需要释放我创造每一个COM对象。我借此机会建立一个 ComObjectManager 类,使之更容易一些处理这个头痛的问题。你要记住每次实例化一个新的COM对象时使用的get()的方法,但如果这样做,很会照顾一切别人为你。请让我知道,如果你看到任何问题,它(或编辑和发表评论,如果你能)。这里的code:

 公共类ComObjectManager:IDisposable的
{
    私人堆叠式和LT;对象&gt; _comObjects =新的堆栈&LT;对象&gt;();

    公共TComObject获取&LT; TComObject&GT;(Func键&LT; TComObject&GT;吸气)
    {
        变种comObject =吸气剂();
        _comObjects.Push(comObject);
        返回comObject;
    }

    公共无效的Dispose()
    {
        //这两行code将处置任何未引用的COM对象
        所以GC.Collect();
        GC.WaitForPendingFinalizers();

        而(_comObjects.Count大于0)
            Marshal.ReleaseComObject的(_comObjects.Pop());
    }
}
 

下面是一个使用示例:

 公共无效CreateExcelWorkbookWithSingleSheet()
{
    使用(VAR COM =新ComObjectManager())
    {
        VAR应用= com.Get&LT; ApplicationClass&GT;(()=&gt;新建ApplicationClass());
        VAR工作簿= com.Get&LT;工作簿&GT;(()=&GT; application.Workbooks.Add(_missing));
        VAR工作表= com.Get&LT;表&GT;(()=&GT; workbook.Worksheets);
        对于(VAR worksheetIndex = 1; worksheetIndex&LT; worksheets.Count; worksheetIndex ++)
        {
            变种工作表= com.Get&其中; WorksheetClass&GT;(()=&GT;(WorksheetClass)工作表[worksheetIndex]);
            worksheet.Delete();
        }
        workbook.SaveAs(
            WorkbookPath,_missing,_missing,_missing,_missing,_missing,
            XlSaveAsAccessMode.xlExclusive,_missing,_missing,_missing,_missing,_missing);
        workbook.Close(真,_missing,_missing);
        application.Quit();
    }
}
 

解决方案

我相信你将不得不调用ReleaseComObject的每个COM对象。因为他们不是垃圾收集,父子层次结构并没有真正来到这个等式:即使你释放父对象也不会减少引用计数的任何子对象

Edit

Please see also How to properly clean up Excel interop objects in C#. 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.

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

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