J'ai une classe non gérée C++ NativeDog
qui doit être utilisée à partir de C#, donc j'ai créé une classe de wrapper ManagedDog
.C++/CLI: empêcher la récupération de place sur l'enveloppe gérée d'une ressource non gérée
// 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
}
}
Tout est bien, et j'utiliser la classe comme ceci:
// 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);
}
Voyez ce qui ne va pas? Je ne l'ai pas fait non plus au début, jusqu'à ce que des choses très étranges se produisent de temps en temps. Fondamentalement, si après avoir appelé MyFunc()
le programme est mis en pause par le GC alors qu'il se trouve quelque part dans la fonction native FeedDogNative
(marquée (***)
ci-dessus), il pensera que le wrapper géré peut être collecté car il ne sera plus utilisé, ni dans C# MyFunc (c'est une variable locale et ne sera pas utilisée après l'appel FeedDogManaged
), ni dans FeedDogManaged
. Et c'est ce qui s'est passé en certaines occasions. Le GC appelle le Finalizer, qui delete
s l'objet chien natif, même si FeedDogNative
n'a pas fini de l'utiliser! Donc, mon code non géré utilise maintenant un pointeur supprimé.
Comment puis-je empêcher cela? Je peux penser à certains moyens (par exemple, un appel factice prétendant utiliser dog
à la fin de FeedDogManaged
) mais quelle serait la méthode recommandée?
Je recommande fortement d'utiliser CAutoNativePtr (http://www.codeproject.com/KB/mcpp/CAutoNativePtr.aspx) pour gérer la gestion de l'objet natif pendant toute sa durée de vie, puis de vous concentrer sur l'exposition de la fonctionnalité aux clients gérés. . (Ceci ne remplace pas 'GC :: KeepAlive', il remplace la gestion manuelle de la mémoire.) –