简体   繁体   English

当app继续运行时,如何在.NET中清理COM引用?

[英]How to clean up COM references in .NET when app will be left running?

I am working on a .NET program that starts a new instance of Excel, does some work, then ends, but must leave Excel running. 我正在开发一个.NET程序,它启动一个新的Excel实例,做一些工作,然后结束,但必须让Excel运行。 Later, when the program runs again, it will attempt to hook into the previous instance. 稍后,当程序再次运行时,它将尝试挂钩到前一个实例。

What is the best way to handle the releasing of COM objects in this situation? 在这种情况下处理COM对象释放的最佳方法是什么? If I do not do a "ReleaseComObject" on the app object the first time, then on the second run get the active object, then finally release the com object, do I have a memory leak? 如果我第一次没有在app对象上执行“ReleaseComObject”,那么在第二次运行时获取活动对象,然后最终释放com对象,我是否有内存泄漏?

The following simplified code illustrates what I am trying to do: 以下简化代码说明了我要做的事情:

private Microsoft.Office.Interop.Excel.Application xlsApp;
private Microsoft.Office.Interop.Excel.Workbook xlsWb;

public void RunMeFirst()
{
    //Start a new instance
    System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
    xlsApp = Activator.CreateInstance(oSEType);
    xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");

    //Do some stuff

    xlsWb.Close(false);
    Cleanup(ref xlsWb);

    //Do not quit Excel here
    //No Cleanup of xlsApp here?  Is this OK?

    System.Environment.Exit(0);
}

public void RunMeSecond()
{
    //Hook into existing instance
    xlsApp = Marshal.GetActiveObject("Excel.Application");
    xlsWb = xlsApp.Workbooks.Open("C:\\test2.xls");

    //Do some stuff

    xlsWb.Close(false);
    Cleanup(ref xlsWb);

    xlsApp.Quit();
    Cleanup(ref xlsApp);

    System.Environment.Exit(0);
}

public void Cleanup(ref object theObj)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Marshal.FinalReleaseComObject(theObj);
    theObj = null;
}

Thanks 谢谢

In general, when working with the Office PIAs, i've found that these sorts of problems with objects not being released arise when you have instance variables that hold COM objects. 通常,在使用Office PIA时,我发现当您拥有包含COM对象的实例变量时,会出现这些对象未被释放的问题。 In your case, these would be xlsApp and xlsWb . 在您的情况下,这些将是xlsAppxlsWb You don't have to quit the Excel application in order to release the objects, but you do have to perform the following as a cleanup procedure: 你不必为了释放对象退出Excel应用程序,但必须执行以下的清理过程:

Marshal.FinalReleaseComObject(xlsWb);
xlsWb = null;

Marshal.FinalReleaseComObject(xlsApp);
xlsApp = null;

GC.Collect();

Local-scoped variables containing COM objects don't seem to cause this problem, only instance variables. 包含COM对象的本地范围变量似乎不会导致此问题,只会导致实例变量。 I hope this helps! 我希望这有帮助!

I'm a tad confused - at the end of RunMeFirst you exit the process, so is RunMeSecond run in a different process from the first method or the same process? 我有点困惑 - 在RunMeFirst结束时你退出进程, RunMeSecond在第一个方法或同一个进程的不同进程中运行吗?

Either way I would change your code so that xlsWb is scoped locally and just do the following: 无论哪种方式,我都会更改您的代码,以便xlsWb在本地作用域,并执行以下操作:

public void RunMeFirst()
{
    System.Type oSEType = Type.GetTypeFromProgID("Excel.Application");
    xlsApp = Activator.CreateInstance(oSEType);
    Workbook xlsWb = xlsApp.Workbooks.Open("C:\\test1.xls");

    // Do stuff
    xlsWb.Close(false);

    System.Environment.Exit(0);
}

You shouldn't really call any of the ReleaseComObject methods except in certain circumstances (for example in server applications where many COM objects will all be in use and so it is critical to release objects as soon as possible). 除非在某些情况下(例如,在许多COM对象都将被使用的服务器应用程序中,因此尽快释放对象至关重要),您不应该真正调用任何ReleaseComObject方法。 The usual mechanism for cleaning up COM object should simply be to let them go out of scope, in which case the COM object will be released using the same magic the GC uses (I believe that it is cleaned up as the finalizer is run, but I'm not 100% sure on this). 清理COM对象的常用机制应该只是让它们超出范围,在这种情况下,COM对象将使用GC使用的相同魔法释放(我相信它会在终结器运行时被清理,但是我不是百分百肯定的。)

The reason why its a good idea to scope xlsWb locally (rather than as a class member or a static member) is that class members will only be cleaned up when the class is cleaned up, and static members are never cleaned up until the appdomain is unloaded. 在本地(而不是作为类成员或静态成员)范围内xlsWb的好主意的原因是,只有在清理类时才会清除类成员,并且在appdomain为止之前永远不会清除静态成员卸载。 If you do need to clean up a COM object referenced by a static field then the way to do this is to set the static field to null so the underlying COM object can be cleaned up by the GC scoping rules: 如果确实需要清理静态字段引用的COM对象,那么执行此操作的方法是将静态字段设置为null以便GC范围规则可以清除基础COM对象:

xlsWb = null;

Also there should be no real need to call GC.Collect for similar reasons - Mike Rosenblum gives a fair explanation as to why he's calling GC.Collect and ReleaseComObject , but no real justification as to why he isn't just satisfied with letting the garbage collector do its job. 此外,由于类似的原因,不应该真正需要调用GC.Collect Rosenblum给出了他为什么调用GC.CollectReleaseComObject的公平解释,但没有真正的理由为什么他不仅仅满足于放弃垃圾收藏家做它的工作。 Like I said, there are situations where you might want more control over the release of COM objects, however these are the exception not the rule. 就像我说的那样,有些情况下你可能想要更多地控制COM对象的释放,但是这些是例外而不是规则。

You may also find reading Marshal.ReleaseComObject Considered Dangerous useful. 您可能还会发现阅读Marshal.ReleaseComObject Considered Dangerous非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM