Подтвердить что ты не робот

С++/CLI: предотвращение сбора мусора в управляемой оболочке неуправляемого ресурса

У меня есть неуправляемый класс С++ NativeDog, который должен использоваться с С#, поэтому я создаю класс-оболочку 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 (помечена (***) выше), она будет думать, что управляемая обертка может быть собрана, потому что она больше не будет использоваться, ни в С# MyFunc (это локальная переменная и не будет использоваться после вызова FeedDogManaged), ни в FeedDogManaged. И это на самом деле произошло иногда. GC вызывает Finalizer, который delete является родным объектом собаки, хотя FeedDogNative не закончил его использовать! Поэтому мой неуправляемый код теперь использует удаленный указатель.

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

4b9b3361

Ответ 1

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