开发者

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

开发者 https://www.devze.com 2023-03-04 19:29 出处:网络
I am working on a .NET program that starts a new instance of Excel, does some work, then ends, but must leave Excel running.Later, when the program runs again, it will attempt to hook into the previou

I am working on a .NET program that starts a new instance of Excel, does some work, then ends, but must leave Excel running. 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? 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?

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. In your case, these would be xlsApp and xlsWb. 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:

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

Either way I would change your code so that xlsWb is scoped locally and just do the following:

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

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

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

You may also find reading Marshal.ReleaseComObject Considered Dangerous useful.

0

精彩评论

暂无评论...
验证码 换一张
取 消