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

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

问题描述

编辑



请参阅 ReleaseComObject 为每个com对象建议。



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



原始问题



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



我想知道我能不能用这样的东西:

  var application = new ApplicationClass 
try
{
//与应用程序,工作簿,工作表,单元格等工作。
}
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& > ws.Name); 

...但是我担心我会遇到内存泄漏或者ghost进程不要释放每个工作表( 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< worksheets.Count; worksheetIndex ++)
{
var worksheet = com.Get< WorksheetClass>(WorksheetClass)工作表[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相信你必须调用每个COM对象上的ReleaseComObject。因为它们不是垃圾收集的,所以父子层次结构不会真正进入等式:即使你释放父对象,它也不会减少任何子对象的引用计数。


Edit

Please see also How to 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.

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

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