Search code examples
c#com-interopautodeskautodesk-inventor

releasing Inventor after event has run


The program takes control of Autodesk Inventor whilst it does what it's supposed to, which is ZIP and convert drawings to the required file type.

After the program has run, I can use Inventor but it doesn't allow me to save anything and I think the program has still locked Inventor.

How can I release it?

If you need anymore information just let me know.

Edit #1

FYI I didn't write this program and I'm completely new to the wonderful, yet confusing world of Autodesk Vault/Inventor.

This is where I assume Inventor is being locked by C#

m_inventorApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Inventor.Application") as Inventor.Application;

Then at the end of the function, m_inventorApp is set to null.

m_inventorApp = null;
m_inventorApp2 = null;

Solution

  • Short version

    Your code isn't releasing the COM reference at all. Use Marshal.FinalReleaseComObject to explicitly release it, preferably in a try/finally block. Don't store COM objects in fields either, create them as late as possible and release them as soon as you can, eg :

    Inventor.Appication inventorApp=null;
    try
    {
        inventorApp = (Inventor.Application)Marshal.GetActiveObject("Inventor.Application");
        .....
    }
    finally
    {
        if(inventorApp !=null)
        {
            Marshal.FinalReleaseComObject(inventorApp);
        }
    }
    

    Use FinalReleaseComObject instead of ReleaseComObject to immediately release the object.

    Setting the field or variable to null only means the COM reference object is available for garbage collection, it doesn't decrement COM's reference count or explicitly release the COM object.

    By using variables instead of fields, you ensure there aren't any dangling references anyway

    Explanation

    You're using COM automation, not something specific to Autodesk Inventor. This is a standard way on Windows for one application to control another. COM was built long before .NET and uses reference counting instead of Garbage Collection. .NET itself started as the "next" version of COM back in 2000 before becoming something completely different on release in 2002. COM is old.

    Each time you create a reference to a COM object, COM increments a reference counter for that object. Each time you release it, the reference counter is decremented. A COM object is released only when the counter reaches 0. This means that your code has to make sure that counter is decremented, otherwise the object will remain in use until the Garbage Collector runs.

    .NET works with COM components through Runtime-Callable Wrappers. The linked doc explains about reference counting, how reference counting works and what you need to do to release references.

    When you call Marshal.GetActiveObject you get a reference to an already running COM server object provider by Inventor (or any other application). The reference's counter starts at 1 and gets increased each time a new reference is created.

    Each client application has to explicitly decrement the reference counter. The COM server object and the application that created it can't close until the reference count reaches 0.

    Why FinalReleaseComObject

    When a client requests multiple references to the same object, it gets back the same reference with an incremented counter. To actually release an object, you'd have to release that reference as many times as it was requested. This caused quite a bit of object leaks in Visual Basic 6 and C++, especially when people were careless with the references. To forcefully kill an object, people often wrote loops that repeatedly called Release on a reference until the count reached 0.

    When .NET was released, the method ReleaseComObject would only decrement the reference count by 1. This resulted in the same leak problems VB6 and C++ had. When .NET Framework 2.0 came out in 2005, it introduced FinalReleaseComObject which actually does the looping itself. Things get even uglier when multiple threads get involved, which means one thread could release the COM object while another is still using it.

    The docs now explain that :

    Use the ReleaseComObject only if it is absolutely required. If you want to call this method to ensure that a COM component is released at a determined time, consider using the FinalReleaseComObject method instead.

    FinalReleaseComObject will release the underlying COM component regardless of how many times it has re-entered the CLR. The internal reference count of the RCW is incremented by one every time the COM component re-enters the CLR. Therefore, you could call the ReleaseComObject method in a loop until the value returned is zero. This achieves the same result as the FinalReleaseComObject method.