none
unmanaged COM unloading itself... RRS feed

  • Question

  •  Hi,

    I have some c++ code designed to run as a service that maintains state. I have wrapped this code in a dll so I can call it from c# using a static instance of the dll (I've just added a reference to the dll in the c# project). On start up I load the dll and call a startup routine and then the c# applciation acts as a service with a client calling another routine (called initialise) within the dll. On the third time (always the third time i.e. the dll maintains state for the startup and two initialise calls) I call the dll I get a "callee disappeared" exception (see below for code snippets and full exception). I assume the dll is being unloaded automatically? How can I keep calling the same instance of the dll running always (i.e. for years)?

    Thanks in advance,
    Patrick

    {System.Runtime.InteropServices.COMException (0x80010012): The callee (server [not server application]) is not available and disappeared; all connections are invalid. The call did not execute. (Exception from HRESULT: 0x80010012 (RPC_E_SERVER_DIED_DNE))
       at smDllLib.operationsdllClass.initialise(Int64& initstring)
       at smService.SOService.initialise(str& initstring) in D:\projects\sm\smService\SOInterface.cs:line 72}

    static void Main(string[] args)
    {
        try
        {
     service.startSM();//load an instance of the dll

     service.runSOService();//run a WCF service whose client can call SOService.initialise below
        }
        catch (Exception e)
        {
     Console.WriteLine("An exception occurred: {0}", e.Message);
        }
    }

    class service
    {
        public static smDllLib.operationsdllClass SMdll;//static instance of the dll
        public static void startSM()
        {
            SMdll = new smDllLib.operationsdllClass();
            SMdll.startUp();
        }

        ...
    }
    public class SOService : serviceUtils, ISOInterface
    {
        public short initialise(ref str initstring)//call this three times from the client and the exception occurs when the dll is called...
        {
            try
            {
                long cotaskptr = stringToCoTaskPtr(ref initstring);
                short resp = service.SMdll.initialise(ref cotaskptr);//exception thrown here
                freeCoTaskMemPtr(cotaskptr);
                return resp;
            }
            catch (Exception e)
            {
            }
            return -1;
        }
    }

    Wednesday, June 4, 2008 11:14 AM

Answers

  • OK got it. The SQL utility class I'm using called CoUninitialise twice when destructing which was somehow interferring with the c# COM the second time the class was destructed.

    Thanks for your time and advice NoBugz, I'd like to say I won't ask impossible questions again but sometimes I just get the wrong end of the stick when bug hunting,

    Patrick
    Wednesday, June 4, 2008 4:36 PM

All replies

  • I don't see anything in your code that would prevent the SMdll instance from getting garbage collected on the next sweep.  You'll have to keep a reference to it in a class instance member.  Using a singleton is the typical pattern.
    Hans Passant.
    Wednesday, June 4, 2008 12:24 PM
    Moderator
  • Hi Nobugz,

    thanks for taking a look. I thought the static on the SMdll object declaration would prevent garbage collection. MSDNs example of the singleton pattern seems to rely on static in the same way (see below) as would a c++ implementation. Also the SMdll object isn't being set to NULL, the c# still has a seemingly valid instance of the object which attempts to connect to the COM dll. The COM code itself finds the dll missing and throws the exception. Surely an unmanaged dll wouldn't be subject to the garbage collecter either?

    I'm a c++ programmer so the garbage collection is new to me but I don't think that is the issue. Could it be related to the way I reference the dll (i.e. am I actually instantiating a COM dll or is c# providing some other form of access to the functionality)? Should I be calling CoInitialiseEx() et al in the c#? Any thoughts please, even if they're not solutions I just need some new ways of thinking about this...

    Thanks,
    Patrick

    public class Singleton
    {
       private static Singleton instance;

       private Singleton() {}

       public static Singleton Instance
       {
          get
          {
             if (instance == null)
             {
                instance = new Singleton();
             }
             return instance;
          }
       }
    }
    • Edited by IamPatrick Wednesday, June 4, 2008 1:35 PM added more info
    Wednesday, June 4, 2008 1:32 PM
  • Your COM object is kept alive by a reference count created by the RCW when you used the new operator.  When the garbage collector collects the last instance of a smDllLib.operationsdllClass, it calls the RCW's finalizer which calls Release() on your COM object's interface.  So, yes, the garbage collector will indeed cause your COM server to be unloaded.  Using the singleton pattern is fine, but the class that uses the reference must store that reference in a field for a long as it needs to be able to use the COM object.  Think of it as one AddRef() call.  The Release() call is automatic.

    Hans Passant.
    Wednesday, June 4, 2008 2:22 PM
    Moderator
  • Hi Nobugz,

    OK that's in terms I can understand a bit :). So I've changed my main to:

            service s = new service();
            s.startSM();
            s.runSOService();

    and made the two methods of service non static (while keeping the dll object static as in the original code). The runSOService method starts a selfhosted WCF service and then waits on Console.readline(). The client then calls the interface implemented by class SOService which uses the dll object within service. As far as my knowledge streches that means the reference within class service should always be viable and not get collected but I'm still gettting the same issue... What have I missed?

    Thanks,
    Patrick

    Wednesday, June 4, 2008 2:55 PM
  • Drop the "static" keyword from your SMdll declaration.  Now the active reference to the service class will keep the SMdll reference alive.
    Hans Passant.
    Wednesday, June 4, 2008 3:02 PM
    Moderator
  • No it's definetly not that (though I think the implmentation is better now than before, thank you).

    I've changed my main to remove all the chaff to just test the dll connection and still get the error, the second call to initialise is somehow invalidating the SMdll object. The difficulty with this is that the first two calls to initialise follow exactly the same route (the data passed in is invalid and quickly rejected). The other method provided by SMdll takes no parameters and can be called as often as desired so I'm guessing it is something to do with the data pointed to by cotaskptr but that is not being changed in these calls! I'm lost and without the whole app I expect no one will know the answer but any thoughts would be appreciated; just got to find the thread to let me unravel the whole problem.

    static void Main(string[] args)
    {
        try
        {
            SecModuleDllLib.operationsdllClass SMdll = new SecModuleDllLib.operationsdllClass();
                ...//init the cotaskptr
            resp = SMdll.initialise(ref cotaskptr);
            resp = SMdll.initialise(
    ref cotaskptr);
            resp = SMdll.initialise(
    ref cotaskptr);
        }
        catch (Exception e)
        {
            Console.WriteLine("An exception occurred: {0}", e.Message);
        }
    }

    • Edited by IamPatrick Wednesday, June 4, 2008 4:07 PM clarification
    Wednesday, June 4, 2008 4:06 PM
  • Oh bugger. Following the theme of stripping things down I started removing calls with in the dll. The dll uses an ADO SQL connection. Turns out that if I don't initalise that connection in the unmanaged c++ then the reference to the dll object in c# dosen't get lost... The ADO connection is closed in a destructor which I haven't looked at but I can't see the connection. Not happy with this so far...
    Wednesday, June 4, 2008 4:20 PM
  • OK got it. The SQL utility class I'm using called CoUninitialise twice when destructing which was somehow interferring with the c# COM the second time the class was destructed.

    Thanks for your time and advice NoBugz, I'd like to say I won't ask impossible questions again but sometimes I just get the wrong end of the stick when bug hunting,

    Patrick
    Wednesday, June 4, 2008 4:36 PM