I have a C++ unmanaged class NativeDog
that needs to be used from C#, so I've create a wrapper class ManagedDog
.
// 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
}
}
All is well, and I use the class like this:
// 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);
}
See what's wrong? I didn't either at first, until very strange things started happening from time to time. Basically if after calling MyFunc()
the program is paused by the GC while it is somewhere in the native function FeedDogNative
(marked (***)
above), it will think the managed wrapper can be collected because it will no longer be used, neither in the C# MyFunc (it's a local variable and will not be used after the FeedDogManaged
call), neither in FeedDogManaged
. And so this has actually happened on occasions. The GC calls the Finalizer, which delete
s the native dog object, even though FeedDogNative
has not finished using it! So my unmanaged code is now using a deleted pointer.
How can I prevent this? I can think of some ways (e.g. a dummy call pretending to use dog
at the end of FeedDogManaged
) but what would the recommended way be?
ref class
focus on exposing functionality to managed clients. (This doesn't replaceGC::KeepAlive
; it replaces manual memory management.) – Putupon