none
Apartment Threaded COM DLL loaded Twice in assembly at runtime. RRS feed

  • Question

  • Hi all,

    I'm trying to understand this behavior within my application.  I was trying to find a way of forcing my Application Domain to unload my COM dll once it was finished with.  During this time, using ProcExplorer I discovered the library was loaded twice.  Although one of the libraries is five times the mapped size than the other.

    What is odd is no matter what I try to release the DLL on demand only forces on of these dll's to unload.  Sometime later the other DLL will unload but I'm not sure what is going on here?

    It doesn't matter if I am early or late binding the COM component the same results are being seen.  Setting the COM reference to null and calling GC.collect() will force one of the libraries to unload. I have also tryed platform invoking the following:

    CoFreeUnusedLibraries()
    CoFreeUnusedLibrariesEx()
    CoUninitialize()

    I have also ensured I call Marshal.ReleaseComObject() on my references and checking the refcount is 0.  No matter what I try here I can only free one DLL and the other will only free some minutes later.  Now reading the MSDN documents on this these releases do take time when calling CoFreeUnusedLibraries() to avoid MT or Free threaded DLL race conditions.  My DLL is Apartment threaded so shouldn't be affected??  I tried to circumvent this anyhow using the CoFreeUnusedLibrariesEx() with no success.

    The only way I can see forcing the unload would be the same way I force unload of .NET assemblies.  I would have to execute the COM component within another Application Domain and then free the Application Domain.  Although I know this would probably work it is such a work around for something I'm sure I can work around.

    Here is some rather messy C# source code examples:

    // early bind
            if(earlybind)
                comhost.comhostClass CHC = new comhost.comhostClass();
            else
            {
                // late bind (reference has been removed from project)
                Type myType = Type.GetTypeFromProgID("comhost.comhost");
                object myObject = Activator.CreateInstance(myType);
            }
     
            if(earlybind)
                Marshal.ReleaseComObject(CHC);
            else
                Marshal.ReleaseComObject(myObject);
            
            // Superflous since ReleaseCOMObject explicity called
            if(earlybind)
                CHC = null;
            else
            {
                myType = null;
                myObject = null;
            }
            
            // Explicity call COM exports
            CoUninitialize();
            // Force marking DLL's for freeing (should be ok on STA's)
            CoFreeUnusedLibrariesEx(0, 0);
            // Pause briefly
            System.Threading.Thread.Sleep(100);
            // Call again for force libraries down (Again probably superflous as way handled)
            CoFreeUnusedLibrariesEx(0, 0);
            // Force garbage collect anyhow.
            GC.Collect();
            // Give some time back to other processes
            System.Threading.Thread.Sleep(0);
            // Keep Thread Alive
            Console.ReadKey();


    Has anyone any ideas?

    Regards

    Richard
    Monday, February 1, 2010 10:10 AM

Answers

All replies

  • I assume that your COM DLL is native (no managed code), right?
    I wouldn't expect a native DLL to be loaded twice. Unless it is comming from 2 different directories (check that out). Run your app under debugger to see who and when loads the DLL (catch DLL load events), that might help you find out why it is loaded twice.

    BTW: I am not COM expert, so it is possible that this is related to some specific COM apartment behavior I don't know about.

    -Karel

    Monday, February 1, 2010 4:45 PM
    Moderator
  • Hi Karel,

    Thanks for your reply.

    Well, technically speaking its native code. Unfortunately its developed in MS Visual Foxpro an interpreted language that does some magic will the binary output that I'm not 100% sure about.  What I do know, unlike .NET the DLL VFP produces appears to be the library that is directly called when instantiating the class (it appears).

    The VFP project is compiled as multi-threaded DLL; which turns out to be an apratment threaded DLL which supported multiple non-blocking requests unlike its single-threaded counterpart.  As far as I know its really only a single threaded class as VFP applications are not strictly multi-threaded.

    I'm no COM expert either, but I wasn't expected to see the library loaded twice.  As soon as I have released one instance of the handle the other will eventually disappear without me closing the application.  I have since used a seperate App Domain to host the object, but even with the App-Domain closed the Primary App Domain still seems to keep one instance of the handle alive??  This is weird as the Pimary Domain does not reference the class directly or even indirectly (via remoting).

    Maybe I should just accept this  behaviour, surely this has something to do with the COM subsystem, I'm not how critical it is to me at this point.  As long as the reference is released after a few minutes without stopping the application I am happy.

    JFMI, how do I attach to the DLL load events?

    Regards

    NozFX

    Monday, February 1, 2010 6:39 PM
  • In windbg it's "sxe ld yourDllName". Here's alternative in VS: http://blogs.msdn.com/greggm/archive/2004/07/08/177418.aspx

    -Karel

    • Marked as answer by NozFx Tuesday, February 9, 2010 9:08 AM
    Monday, February 1, 2010 7:29 PM
    Moderator
  • I'm wondering if the Dll had a base address conflict and was rebased. That would potentially give you a much larger memory footprint in your process because it would effectively be a private copy instead of a shared copy backed by the code file.


    Phil Wilson
    Tuesday, February 2, 2010 12:16 AM
  • Hi Phil,

    I think I understand what your saying but I've never had to deal with a problem of this nature before.  Which address are you refering to and how does rebasing of this address work? 

    This is probably a large topic, I suppose all I need to know is how to tell this is the case and what is possible to resolve it?

    Would you expect the behaviour I am seeing with the unloading of the DLL though?

    Thanks

    NozFX
    Monday, February 8, 2010 10:49 AM
  • Hi Phil,

    I did some reading from:

    http://www.drdobbs.com/windows/184416272;jsessionid=YJ0Y25USQLIBJQE1GHOSKH4ATMY32JVN?_requestid=44342

    Even if the DLL is getting rebased would this behviour cause a delay in the unload of the DLL?

    I understand the negative perf hit rebasing causes so its worth me looking at trying to overwrite the preffered base address at some point.  I'll look to see if the increased memory footprint makes any sense too.

    In short all I'm trying to achieve is an unload of the DLL on demand.  For some reason I cannot do this and when I looked at the handles I could see two copies of the DLL loaded.

    Regards

    NozFX
    Monday, February 8, 2010 11:00 AM
  • Rebasing DLL will not cause its double loading. It will just cause it to be loaded at different address.
    As I suggested: Find out why the DLL is loaded twice, then go on with the rest.

    -Karel
    Monday, February 8, 2010 4:59 PM
    Moderator
  • I think the same Dll can load twice if it is rebased between loads. For example, see this SysInternals thread:

    http://forum.sysinternals.com/forum_posts.asp?TID=9927&PN=2 

    search for "twice".



    Phil Wilson
    • Marked as answer by NozFx Tuesday, February 9, 2010 9:09 AM
    Monday, February 8, 2010 7:33 PM
  • Hmm, interesting. I didn't hear about anything like that before. Thanks for the link.

    This looks like a bug in Windows (or some smart feature I don't know about). I sure hope that this doesn't happen too often.

    BTW: Here's a similar post: http://gis.shandyba.com/archives/15-Same-DLL-loaded-twice.html (with 4 more links)

    -Karel
    Tuesday, February 9, 2010 12:15 AM
    Moderator
  • Hi Karel,

    Interesting, I haven't tested this in production only in debug throuh Visual Studio.  I'm not sure how likely this is but whats the chance the debug version is calling LoadLibrary() on the DLL to read information from the class and causing the dual load?

    I'm not sure how critical this is to me at the moment but its a really odd behaviour that I am going to have to spend some time with WinDbg on.

    Thanks for your posts.

    Regards

    NozFX
    Tuesday, February 9, 2010 9:08 AM
  • IMO there is minimal chance that debugger itself is causing the double-load. Debuggers tend to be as non-invasive as possible by design.

    -Karel
    Tuesday, February 9, 2010 4:58 PM
    Moderator
  • Hi Phil,

    I haven't had much more time to look into this issue but. . . using process explorer shows that the DLL is not getting rebased.  All of the assemblies are but I think they all share the VS default base address. The unmanaged COM library in question is not getting rebased, which is good news for me really. 

    Is possible that it is loaded twice due to the reading of the type library data?  I notice with COM servers with external tlb's, that the tlb file is opened along side the dll.  This COM library is not getting its tlb opened and as far as I know this particular class's tlb is embedded within the DLL as an internal resource.  Is this a plausable explaination?

    Regards

    NozFX
    Friday, March 12, 2010 3:41 PM
  • Hi Karel,

    The dll is not getting rebased.  Is it possible that the dll is opened twice for its type library info?  The COM DLL has its tlb file embedded as an internal resource.  Would this be a plausable explaination to this behaviour?

    Regards

    NozFX
    Friday, March 12, 2010 3:43 PM
  • Get full callstacks at time the DLL is loaded (see my earlier advice how to do that in windbg and link to VS alternative). Make sure you use Microsoft symbol server at that moment.
    Guessing will not give you the 'real' answer, only investigation will.

    -Karel
    Friday, March 12, 2010 8:29 PM
    Moderator