C ++ / CLI: предотвращение сбора мусора в управляемой оболочке неуправляемого ресурса - PullRequest
12 голосов
/ 06 декабря 2010

У меня есть неуправляемый класс C ++ NativeDog, который нужно использовать из C #, поэтому я создал класс-оболочку 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
    }
}

Все хорошо, и я использую класс следующим образом:

// 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);
}

Видишь, что не так? Сначала я тоже этого не делал, пока не начали происходить очень странные вещи. В основном, если после вызова MyFunc() программа приостановлена ​​GC, пока она находится где-то в собственной функции FeedDogNative (отмечена (***) выше), она будет думать, что управляемую оболочку можно собрать, поскольку она больше не будет использоваться, ни в C # MyFunc (это локальная переменная и не будет использоваться после вызова FeedDogManaged), ни в FeedDogManaged. И так это действительно происходило в некоторых случаях. GC вызывает Finalizer, который delete является нативным объектом собаки, хотя FeedDogNative еще не закончил его использовать! Поэтому мой неуправляемый код теперь использует удаленный указатель.

Как я могу предотвратить это? Я могу придумать несколько способов (например, фиктивный вызов, притворяющийся использовать dog в конце FeedDogManaged), но каков будет рекомендуемый способ?

Ответы [ 2 ]

7 голосов
/ 06 декабря 2010

Вам необходим вызов GC::KeepAlive() в вашей функции FeedDogManaged. Похоже, это точный вариант использования для этого.

4 голосов
/ 06 декабря 2010

В вашем управляемом коде добавьте GC.KeepAlive(dog) после вызова FeedDogManaged ():

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...