I try to hold a singleton C++/CLI object from a native object which is held by another C++/CLI object which is held by the C# App.
TestGcroot::App (C#) -> Class1 (C++/CLI) -> Class2 (native) -> Something (C++/CLI) ClassLibrary1::SharedSomething (C++/CLI) -> Something (C++/CLI)
Since both Class2 and SharedSomething hold the same Something I would expect that Something will be alive for the lifetime of Class2. However the observation is that when the application shuts down Something is finalized (thus destroyed) before Class2 is destroyed. The Class2::gcroot still holds Something but it is like referencing a deleted pointer - it crashes the application.
In C++ terms the situation looks as if I have passed the same raw pointer to two shared_ptr. One will destroy the object while the other still "holds" the object.
Here is the code that demonstrates the issue:
namespace ClassLibrary1 {
ref class Something {
public:
Something() {
}
~Something() { // Dispose
this->!Something();
}
!Something() { // Finalize() - this happens before ~Class2() even though Class2 holds a gcroot handle to the same Something object!!!
}
};
ref class SharedSomething {
public:
SharedSomething() {
}
~SharedSomething() { // Dispose()
this->!SharedSomething();
}
!SharedSomething() { // Finalize()
}
static Something^ Get() {
return _something;
}
private:
static Something^ _something = gcnew Something();
};
class Class2 {
public:
Class2() {
_something = SharedSomething::Get();
}
~Class2() {
// _something is dead even though it has not been reset!!!
}
private:
gcroot<Something^> _something;
};
public ref class Class1 {
public:
Class1() {
_class2 = new Class2();
}
~Class1() { // Dispose()
this->!Class1();
}
!Class1() { // Finalize()
delete _class2;
}
private:
Class2* _class2;
};
}
namespace TestGcroot
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
_class1 = new ClassLibrary1.Class1();
}
ClassLibrary1.Class1 _class1;
}
}
I have the complete code example at https://github.com/georgevs/TestGcroot.git
EDIT: the code is failing even if no singletons or statics are involved:
App (C#) -> Class1 (C++/CLI) -> Class2 (native) -> Something (C++/CLI)
Something finalizer is called before both the Class2 destructor and Class1 finalizer. Even if Class1 creates Something and passes it to Class2, still Something finalizer is called first, and Class1 finalizer is called next while still referencing Something!?!