none
How to unload COM object RRS feed

  • Question

  • Ok, I've been struggling with this for some time now and I need help.

    Basically I am trying to integrate our product with a third party SDK. We need to be able to upgrade this SDK without shutting down our application. The SDK is exposed

    as a COM object, from which I have generated an interop file that I am referencing in my project.

    The problem is that once I have loaded the COM object, I can no longer release the lock on the file. I need to release it in order to be able to uninstall the SDK and

    install the new version.

    I have followed the instructions found on many posts on how to load and unload assemblies in different AppDomains. This all seems to work, well I'm not getting

    exceptions, etc...

    My setup is as follows:

    I have 2 projects, the first one is just a test console application, the second one I call "COM", which takes care of doing all the work related to the COM object. Only

    in this second project do I reference the interop file.

    Basically the test application creates a second AppDomain and creates an instance of a class which will take care of loading up the COM (project) assembly. You've

    probably seen the code before, is use the following methods to do the core loading...
    AppDomain newDomain = AppDomain.CreateDomain(appDomainName, evidence, setup);
    newDomain.CreateInstanceFromAndUnwrap(assemblyPath, className); //This is the full path to the dll and a string representing the namespace and class of the object

    In this second class, I create an instance of a class in the COM project and invoke a method on it (GetVersion), which in turns does things with the 3rd party COM

    component

    String path = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "COM.dll");
    Assembly assembly = Assembly.LoadFile(path);
    Type type = assembly.GetType("COM.TestClass");
    Object handle = Activator.CreateInstance(type);
    MethodInfo methodInfo = type.GetMethod("GetVersion");
    String test = (String)methodInfo.Invoke(handle, null);

    Like I said, this is all fine, it returns the expected values. The problem is once I'm done with executing this method and try to unload the AppDomain created earlier. I

    can see the file lock being released on my COM.dll (I am using a tool called Unlocker to do this) but the 3rd party COM dll is still locked by my test EXE. How is that

    possible? I've unloaded the COM.dll, something must be transferred from my COM project to my test app.

    Second question, why can I rename the 3rd party COM dll but not delete it, this is weird...

    Thanks
    Nic

     

    Thursday, August 27, 2009 7:05 PM

Answers

  • You are referencing the interop wrapper, there's no other option.  I can only recommend you debug this.  Set a breakpoint on the DllCanUnloadNow() entry point of the COM server and find out why it objects.

    Hans Passant.
    Thursday, August 27, 2009 9:15 PM
    Moderator

All replies

  • COM doesn't know anything about AppDomains, that's strictly a .NET concept.  As such, using an AppDomain is pointless and it is just more likely to cause problems.  COM loads the DLL when the app first call CoCreateInstance() for any to the coclasses that it contains.  Right now, indirectly done through Activator.CreateInstance() in your code.  The DLL then by itself decides when it can be unloaded, COM periodically calls its DllCanUnloadNow() entry point.  It will return S_FALSE when there's an object of any of its coclasses still in use.

    You'll first have to make sure that you've no longer got any objects in use and that the garbage collector has released the wrappers for them.  Then P/Invoking CoFreeUnusedLibraries() would trigger the unloading of the DLL.  Ensuring that the garbage collector did its job requires GC.Collect + WaitForPendingFinalizers.  Marshal.FinalReleaseComObject() is another heavy hammer at your disposal.

    Hans Passant.
    Thursday, August 27, 2009 7:26 PM
    Moderator
  • Thanks for the input nobugz,

    Unfortunately I still can't seem to get things in the right order for the dll to be freed.

    Here's what I've got now:

    CInfo info = new CInfo();
    Int32 ret = Marshal.FinalReleaseComObject(info);
    info = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
    CoFreeUnusedLibraries();

    FinalReleaseComObject returns 0, which I assume is successful. I've also tried a bunch of other combinations of different GC and Marshal methods, but no luck.

    In this test I have skipped all the Activator.CreateInstance and simplified the whole process. How about referencing the Interop file rather than the COM object in my project; is the outcome the same?

    Thanks
    Thursday, August 27, 2009 9:06 PM
  • You are referencing the interop wrapper, there's no other option.  I can only recommend you debug this.  Set a breakpoint on the DllCanUnloadNow() entry point of the COM server and find out why it objects.

    Hans Passant.
    Thursday, August 27, 2009 9:15 PM
    Moderator
  • Ok, I will try this. I will need some time as I've never done this before.

    I really thought that loading all of this in a different AppDomain would allow me to remove all the references. I can't help to think that those interop files do something extra when they are loaded up.

    Thanks for the input.
    Nic
    Friday, August 28, 2009 11:29 AM