views:

769

answers:

4

I have a managed object in a c# dll that maintains an anonymous integer handle to an unmanaged object in a c++ dll. Inside the c++ dll, the anonymous integer is used in an std::map to retrieve an unmanaged c++ object. Through this mechanism, I can maintain a loose association between a managed and unmanaged object using an anonymous integer handle.

In the finalize method (destructor) of the managed object I have a call into the unmanaged dll to delete the unmanaged object.

All is well as the c# program runs, but I have a problem when the program exits. Becuase I have no control on the order of delete operations on the managed side, the unmanaged dll is deleted from memory BEFORE any managed object. Thus when the managed object's destructor is called (which in turn calls the unmanaged destructor [at least indirectly]), the unmanaged object has already been deleted and the program crashes.

So how can I safely delete a unmanaged object in an external c++ dll that is associated with a managed object in a c# program.

Thanks

Andrew

A: 

You should delete your unmanaged object from the Dipose method of your managed object. You should also call Dispose out of the Finalize method in case your code hasn't called Dispose before the garbage collector got to it. Adam Robinson's answer illustrates that much better.

So if you are dilligent with you Dispose calls (and use using blocks) you shouldn't have shutdown crashes.

Edit: I think the problem is actually the unmanaged DLL getting unloaded before the finalizer runs. Ye old "Once the app is shutting down there are no guarantees as to what order the are unloaded".

Perhaps you can experiment having your unmanaged resources in a managed C++ assembly? That way you know the DLL doesn't go bang before you are finished with it and you don't have to do ugly P/Invoke stuff.

Here is an example from MSDN:

ref struct A {
   // destructor cleans up all resources
   ~A() {
      // clean up code to release managed resource
      // ...
      // to avoid code duplication 
      // call finalizer to release unmanaged resources
      this->!A();
   }

   // finalizer cleans up unmanaged resources
   // destructor or garbage collector will
   // clean up managed resources
   !A() {
      // clean up code to release unmanaged resource
      // ...
   }
};

More here http://msdn.microsoft.com/en-us/library/ms177197.aspx

The above is the same pattern as the C# one except you might get away with having the unamanaged resources in the managed C++ assembly. If you really MUST have those in an unmanaged DLL (not a static unmanaged library) then you are stuck, you will have the same shutdown issues.

Igor Zevaka
+2  A: 

You may be able to solve this quickly by checking Environment.HasShutdownStarted in the finaliser of your C# object (and not calling into the C++ DLL / deleting the C++ object if HasShutdownStarted is true). If you are not in the main AppDomain then you might need to check AppDomain.Current.IsFinalizingForUnload instead (in fact this may be safer in general).

Note this merely avoids calling the freed library (i.e. avoids running the unmanaged destructor): if the unmanaged library was holding a resource that won't automatically be freed on process shutdown, then that resource could be leaked. (Most OS resources are freed on process shutdown, so this will often not be a concern.) And as Adam notes the CLR finaliser is intended as a failsafe: you really want to free resources more deterministically. Therefore, if structurally possible, Igor's suggestion to implement IDisposable on the C# class and deterministically Dispose the object would be preferable.

itowlson
+7  A: 

The finalizer of any managed object should almost always be used only as a failsafe. As a general rule, if you have finalizer logic, then your object likely needs to implement IDisposable. The basic pattern for implementing IDisposable is (let's say the class name is MyClass):

public class MyClass : IDisposable
{
    private int extHandle;

    public MyClass()
    {
        extHandle = // get the handle
    }

    public void Dispose()
    {
        Dispose(true);

        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
        {
            // call dispose() on any managed objects you might have
        }

        // release the handle
    }

    ~MyClass()
    {
        Dispose(false);
    }
}

This also means that whatever code is creating and using this object needs to be able to manage the lifetime of the object. The easiest way is to enclose the instance in a using block, like this:

using(MyClass c = new MyClass())
{
    // do things with c
}

The using block automatically calls Dispose on the object as it falls out of scope at the end of the block. Things, of course, get more complicated when the object needs to exist outside of a single function. In any case, whenever the object is finished with Dispose needs to be called.

Adam Robinson
The real issue is the very loose coupling. WIth just a handle, the application does not think there is dependencies and as such it can unload in any order. One thing which could help it to establish at least one strong reference which should ensure that the managed DLL will be unloaded before the unmanaged. This does not need to be the reference you are currently used, just one strong reference should be fine. I have been developing a system with MANY thousands of references to unmaged DLL using managed C++ and have not run into this issue. Managed C++ could also be a possible solution.
Jim Kramer
@Jim: Following the recommended `IDisposable` pattern should alleviate that issue, as objects will (or should) be disposed before any unloading ever starts.
Adam Robinson
A: 

The usual way to do this is to derive your managed object from IDisposable

I always try to call object.Dispose explicitly when I'm done with the object, but I'm not sure that would be necessary in your case. The documentation that I've read is unclear as to whether it gurantees that Dispose() will be called before your dll unloads or not.

In my own code, the managed code domain is torn down explicitly before the unmanaged app exits so I don't have to worry about that particular problem.

John Knoeller
No, there is *no* guarantee that `Dispose` will be called (as the GC itself doesn't do anything special for objects that implement that interface). That's why the finalizer logic that I demonstrated is a strongly recommended pattern.
Adam Robinson
Dispose will *not* be called before your DLL unloads, unless something calls it; that is, the CLR will *not* call Dispose for you. (Some .NET components, such as Form, will call it for their children.) The CLR *will* call Finalize() when an object is garbage collected, including during shutdown. But Dispose() is purely a user-code thing.
itowlson
+1 to itowlson; Exactly, the intended pattern is that anything that implements `IDisposable` should call `Dispose` on any member objects that it manages that also implement `IDisposable`.
Adam Robinson
Thanks for the clarification, the MS docs could be clearer on this point IMO.
John Knoeller