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?
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.) –