2010-12-06 37 views
12

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?

+0

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

Répondre

7

Vous avez besoin d'un appel GC::KeepAlive() dans votre fonction FeedDogManaged. On dirait que c'est un cas d'utilisation exact pour ça.

+0

Parfait, merci! – Laurent

4

Dans votre code managé, ajoutez GC.KeepAlive(dog) suite à l'appel à FeedDogManaged():

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

+0

C'est ce dont j'ai besoin, même si, comme le dit Max, il vaut mieux mettre l'appel _inside_ FeedDogManaged() parce que ce n'est pas la responsabilité de l'appelant. FeedDogManaged() pour gérer cela. – Laurent

+0

Yup, d'accord complètement. Je ne l'ai pas lu assez attentivement la première fois. – twon33