0
votes

I am wrapping some functionality from a native C++ dll to .NET with C++/CLI. One of my native classes (class UA) has a method that returns a reference to another native class/object (class UB). I have wrapped both classes in my C++/CLI project as follows:

//Unmanaged classes
class UA 
{
public:
A(int x) : m_x(x) {}
int m_x;
};

class UB 
{
public:
UB() : a(1) {}
UA& get_A(){return a;}
protected:
UA a;
};

// managed classes /CLI
public ref class A
{
internal:
A(UA* a) : m_ptr(a) {}
public:
A() : m_ptr(new UA(1)) {}
~A()
{
  this->!A();
}
!A()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UA* m_ptr;
};

public ref class B 
{
public:
B() : m_ptr(new UB()) {}
A^ get_A(){return gcnew A(&m_ptr->get_A())}
~B()
{
  this->!B();
}
!B()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UB* m_ptr;
};

The above compiles with /clr; however, when tested in a C#.NET app, I get a Debug Assertion Error.

// C#.NET app
B b = new B();
A = b.get_A();

This error occurs in the finalizer (!A()) of the managed wrapping class A. In particular, it is raised when the GC is trying to delete the native pointer. I guess this happens simply why I have not created this instance with the new keyword. Do you think that it would be better to use a copy constructor in the native C++ and then create a managed wrapping class with the new keyword?

For example:

//Unmanaged classes
class UA 
{
public:
A(int x) : m_x(x) {}
A(const A& a)
{
  m_x = a.m_x;
}
int m_x;
};

And then the managed A and B wrappers will be:

// managed classes /CLI
public ref class A
{
internal:
A(UA* a) : m_ptr(a) {}
public:
A() : m_ptr(new UA(1)) {}
~A()
{
  this->!A();
}
!A()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UA* m_ptr;
};

public ref class B 
{
public:
B() : m_ptr(new B()) {}
A^ get_A(){return gcnew A(new UA(&m_ptr->get_A()))}
~B()
{
  this->!B();
}
!B()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UB* m_ptr;
};

Is there any different approach to return a reference to an unmanaged object in C#? Apologies if I do something fundamentally wrong, I have very small experience in C++/CLI. I also have seen this post; however, it seems that the accepted answer proposes the solution I firstly implemented. I appreciate any help/suggestion.

1
Interesting to see your C# code too. The code that produces the assertion.vahancho
Hi @vahancho, see the edit above.GioR
The code is nonsense and cannot compile, makes it hard to help you. All that I can really see is that the destructors are not safe and will trigger this diagnostic when the C# code disposes the object more than once. Easy to fix by setting the pointer to nullptr. Whether that's the real cause of the heap corruption is impossible to guess, C++ code rarely needs a lot of help to bomb like this.Hans Passant
@GiorgosR, the problem is probably in this line: A^ get_A(){return gcnew A(&m_ptr->get_A())}. Pointer in A object refers to the object in another object. When you destroy A it tries to delete an object which was already deleted.vahancho
Hi Hans, many thanks for the reply. The c++ is just an example. I tried to simplify/convert my real code to an example. The issue here is how I could return a native reference in a C# wrapper class. The only possibility I see is with a copy constructor either in c++/cli or in C++.GioR

1 Answers

0
votes

This is just due to the fact that when you call b.get_A(), this calls your internal constructor A(UA* a) : m_ptr(a) {} which just stores the pointer to field a of an object of type UB. The destructor will then try to delete this pointer which cannot work.

This has nothing to do with C++/CLI, just be symmetric with allocations and deletions. In this case just remove your public constructor A() which does an allocation and also those destructors and it will work.