Home > Back-end >  C# IntPtr as a member of a class, cannot delete pointer
C# IntPtr as a member of a class, cannot delete pointer

Time:02-23

Hello coders out there,

I have a question about C# pointer. I do not know what to do to get the code running.

So, in my WPF project there is this Class MainViewModel which contains one IntPtr.

private IntPtr m_context;

This context is filled when loading Data from an C-Interface (the interface does a "return new class_name()"). I call it like that:

this.m_context = interface_context_create();

When being used, I want to delete the context. This is also an interface method. I would expect it to call like this in the same method as the creation above (!!):

unsafe
{
    interface_context_free(&this.m_context);
}

The interface_context_free(..) gets a class_name** on the c-interface-side, so thats why I need to use &. The interface on my C# side is correct, since my test1() method (see below) does create and free correctly.

This gives at interface_context_free(&this.m_context); following error:

CS:0212 you can only take the address of an unfixed expression inside of a fixed statement initializer

If I declare it like this, then it works absolutely fine!!!:

public void test1()
{
  IntPtr iptr = interface_context_create();

  unsafe
  {
    interface_context_free(&iptr);
  }
}

How can I free m_context and where is the difference between the two scenarios??

CodePudding user response:

This is actually a quite simple fix.

The reason you cannot do &this.m_context is because that variable is not fixed in memory. As such, while you have an address to that variable floating around in unmanaged space, GC may kick in and move that variable somewhere else rendering that address invalid. This could happen if the object instance containing the field m_context survives a garbage collection cycle and thus gets promoted to a new generation, and thus moved into a new memory location.

The fix is quite simple: copy the IntPtr value into a local variable first. Variables on the stack are not moved around and taking their address is fine, as long as you don't let that address live past the scope of the variable.

So instead of this:

unsafe
{
    interface_context_free(&this.m_context);
}

just do this:

unsafe
{
    IntPtr temp = this.m_context;
    interface_context_free(&temp);
}

Note that if the reason the interface_context_free function takes a pointer to a variable is that it not only frees the object referenced from the variable but also modifies the variable content itself by for instance setting it to point to null, you would have to copy the modified temporary variable back into the field:

unsafe
{
    IntPtr temp = this.m_context;
    interface_context_free(&temp);
    this.m_context = temp;
}

CodePudding user response:

It is relate with garbadge collector. You should tag field as non-moving for GC.

fixed (void* myIntPtr = &intPtr)
{
    interface_context_free(&myIntPtr);
}

Details here: https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/fixed-statement

  • Related