2010-12-06 10 views
12

Ich habe eine C++ nicht verwaltete Klasse NativeDog, die von C# verwendet werden muss, so habe ich eine Wrapper-Klasse ManagedDog erstellen.C++/CLI: verhindert Garbage Collection auf verwalteten Wrapper der nicht verwalteten Ressource

// unmanaged C++ class 
class NativeDog 
{ 
    NativeDog(...); // constructor 
    ~NativeDog(); // destructor 
    ... 
} 

// C++/CLI wrapper class 
ref class ManagedDog 
{ 
    NativeDog* innerObject; // unmanaged, but private, won't be seen from C# 
    ManagedDog(...) 
    { 
     innerObject = new NativeDog(...); 
     ... 
    } 

    ~ManagedDog() // destructor (like Dispose() in C#) 
    { 
     // free unmanaged resources 
     if (innerObject) 
      delete innerObject; 
    } 

    !ManagedDog() // finalizer (like Finalize() in C#, in case 
    {    // the user forgets to dispose) 
     ~ManagedDog(); // call destructor 
    } 
} 

Alles ist gut, und ich verwende die Klasse wie folgt aus:

// in C++/CLI 
// this function is called from C++ code 
void MyLibrary::FeedDogNative(NativeDog* nativedog) 
{ 
    ... // (***) 
} 
// this function is called from C#, passes on the dog to the native function 
void MyLibrary::FeedDogManaged(ManagedDog^ dog) 
{ 
    NativeDog* rawdog = dog->innerObject; 
    MyLibrary::FeedDogNative(rawdog); 
} 

// C# client code 
void MyFunc() 
{ 
    ManagedDog dog = new ManagedDog(...); 
    MyLibrary.FeedDogManaged(dog); 
} 

Sehen Sie, was falsch ist? Ich tat es zunächst auch nicht, bis von Zeit zu Zeit sehr seltsame Dinge passierten. Grundsätzlich, wenn nach dem Aufruf MyFunc() das Programm von der GC pausiert wird, während es irgendwo in der nativen Funktion FeedDogNative (markiert (***) oben) ist, wird es denken, dass der verwaltete Wrapper gesammelt werden kann, weil es nicht mehr verwendet wird, weder in der C# MyFunc (es ist eine lokale Variable und wird nicht nach dem FeedDogManaged Aufruf verwendet werden, weder in FeedDogManaged. Und so ist es tatsächlich gelegentlich passiert. Der GC ruft den Finalizer auf, der delete das native Hund Objekt ist, obwohl FeedDogNative es nicht beendet hat! Also verwendet mein nicht verwalteter Code jetzt einen gelöschten Zeiger.

Wie kann ich das verhindern? Ich kann mir einige Möglichkeiten vorstellen (z. B. ein Pseudoanruf, der vorgibt, dog am Ende von FeedDogManaged zu verwenden), aber was wäre der empfohlene Weg?

+0

Ich empfehle dringend, CAutoNativePtr (http://www.codeproject.com/KB/mcpp/CAutoNativePtr.aspx) zu verwenden, um die Lebensdauerverwaltung des nativen Objekts zu behandeln, dann sollte sich Ihre 'ref-Klasse' darauf konzentrieren, Funktionalität für verwaltete Clients verfügbar zu machen . (Dies ersetzt nicht GC :: KeepAlive; es ersetzt manuelle Speicherverwaltung.) –

Antwort

7

Sie benötigen einen GC::KeepAlive() Anruf in Ihrer FeedDogManaged Funktion. Es scheint, als wäre es ein genauer Anwendungsfall dafür.

+0

Perfekt, danke! – Laurent

4

In Ihrem verwalteten Code, fügen GC.KeepAlive(dog) zu FeedDogManaged nach dem Aufruf():

http://msdn.microsoft.com/en-us/library/system.gc.keepalive(VS.71).aspx

+0

Das ist, was ich brauche, obwohl wie Max sagte, ist es besser, den Aufruf _inside_ FeedDogManaged(), da es nicht in der Verantwortung des Aufrufers von ist FeedDogManaged(), um damit umzugehen. – Laurent

+0

Ja, stimme voll und ganz zu. Ich habe es beim ersten Mal nicht sorgfältig genug gelesen. – twon33

Verwandte Themen