1
votes

I'm creating a .NET wrapper around a C++ library (I don't have access to the source) using C++/CLI. The C++ library needs to call back the .NET delegate written in C++/CLI. I'm assigning a call back function to the C++ library classes using Marshal::GetFunctionPointerForDelegate. When this function is called back from the unmanaged side, however, I need to make sure that the .NET delegate function is still in the same memory location.

The easiest way, of course, is to require the .NET library user to pin the .NET object but that's not really a clean design and also allows the user to shoot themselves in the foot. Better way is to design the .NET class so that it pins itself either at the time of creation or triggered by a function/event.

How would I design this? According to this link, http://msdn.microsoft.com/en-us/library/18xa23yk%28v=VS.100%29.aspx, you can't have interior ~= pinned pointers as object members. This, then, implies that one could create a pinned pointer reference as either a static or a global variable.

So I want to do something like either of these two but can't get it to compile/work.

public ref class UserClass{
void createDotNetCPPWrapperClass()
{
    m_class = gcnew DotNetCPPWrapperClass;
}

DotNetCPPWrapperClass^ m_class

};


public ref class DotNetCPPWrapperClass{

static pin_ptr<DotNetCPPWrapperClass^> pinnedSelf;

DotNetCPPWrapperClass()
{
    pinnedSelf = this;
}
};

OR

public ref class UserClass{

void createDotNetCPPWrapperClass()
{
    m_class = gcnew DotNetCPPWrapperClass;
    m_class->setupImportantStuff();
}

DotNetCPPWrapperClass^ m_class

};


public ref class DotNetCPPWrapperClass{

static pin_ptr<DotNetCPPWrapperClass^> pinnedSelf;

DotNetCPPWrapperClass(){}

void setupImportantStuff()
{
    pinnedSelf = this;
}

};
2
Have you investigated using the GCHandle or HandleRef structures provided by the framework?Cody Gray
The point of using Marshal::GetFunctionPointerForDelegate() is that you don't have to do this. Don't help.Hans Passant
Please elaborate. msdn.microsoft.com/en-us/library/… "You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track reference to unmanaged code." Or are you saying that GetFunctionPointerForDelegate will prevent relocation, just not garbage collection?Computer Software Guy

2 Answers

0
votes

GCHandle will solve your problem.

public ref class Wrapper
{

GChandle thisHandle;
public:
  Wrapper() 
  {
    thisHandle = GCHandle.Alloc(this, GCHandleType::Normal);
  }

  ~Wrapper() // Dispose
  {
    if(thisHandle.IsAllocated)
      thisHandle.Free;
  }

  !Wrapper() // Finalize
  {
    //Native resource releasing
  }
}

Now there are some points you need to be careful with.

  1. You do not want to GCHandle.Alloc() something pinned for a long time. Once you pin an object GC cannot collect the objects that aligned before pinned object in the memory, because the address of pinned object cannot be changed. So garbace collection becomes pointless. Notice that I allocate the handle with GCHandleType::Normal to not only make it uncollectible but also movable.
  2. I do not know if GC attemps to collect an object that has an allocated handle, and call the finalizer of that object. IMO this doesn't make sense, because handle prevents the object from GC collection. So it is very very crucial for you to call delete operator in C++/Cli and call Dispose() in C# and free the handle. If you forget to call those, the whole object becomes a memory leak.
0
votes
public class MyClass {

  private GCHandle gch;

  public MyClass() {
    gch=GCHandle.Alloc(this, GCHandleType.Pinned);
  }
}