Jump to content
  • Advertisement
Sign in to follow this  
Daggerbot

C# pointers

This topic is 3347 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I recently started writing a C# binding for a 2D C game engine, and I came across something that I'm not sure what to do about. Is the address of an object consistent throughout its life? I've passed a pointer to a managed object as an IntPtr to unmanaged code (intptr_t), so that I can easily retrieve a managed object given the equivalent unmanaged handle. Sorry if this is confusing. Here is some code to clarify:
/* C code */
typedef struct
{
  intptr_t managed;
  ...

} Something;

intptr_t _CreateSomething( intptr_t managed )
{
  Something *something = malloc(sizeof(Something));
  something->managed = managed;
  return (intptr_t) something;
}

intptr_t _GetFromHandle( intptr_t handle )
{
  return ((Something*)handle)->managed;
}


// C# code
class Something
{
  private IntPtr mHandle;

  public unsafe Something()
  {
    mHandle = _CreateSomething(new IntPtr((void*)&this));
  }

  public static unsafe Something GetFromHandle( IntPtr handle )
  {
    return *((Something*)_GetFromHandle(handle).ToPointer());
  }
}


Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Daggerbot
Is the address of an object consistent throughout its life?
Most definitely not!

If you want to pass a pointer to an object on the manager heap to a native method, you must first "pin" it so that the garbage collector doesn't move it around behind your back. You do this in C++/CLI with pin_ptr "smart pointer".

Also, you should ensure that you only pin objects for a short a time as possible, because obviously pinning lots of things will stop the garbage collector from doing it's job as efficiently as possible.

In the code you've got, is the any reason in particular why you want your native object to hold a pointer to a pinned C# object? Can't it just hold a managed reference?

Edit: Actually, now I see what you're doing - CreateSomething is a P/Invoke, and there's no C++/CLI at all, right? (That's a good thing!). In that case, you need to use the fixed statement in C# to ensure the object doesn't move around while you need it. But don't keep objects pinned over a long time, it just doesn't work well.

Share this post


Link to post
Share on other sites
Quote:
Original post by Codeka
Quote:
Original post by Daggerbot
Is the address of an object consistent throughout its life?
Most definitely not!

If you want to pass a pointer to an object on the manager heap to a native method, you must first "pin" it so that the garbage collector doesn't move it around behind your back. You do this in C++/CLI with pin_ptr "smart pointer".

Also, you should ensure that you only pin objects for a short a time as possible, because obviously pinning lots of things will stop the garbage collector from doing it's job as efficiently as possible.

In the code you've got, is the any reason in particular why you want your native object to hold a pointer to a pinned C# object? Can't it just hold a managed reference?

Edit: Actually, now I see what you're doing - CreateSomething is a P/Invoke, and there's no C++/CLI at all, right? (That's a good thing!). In that case, you need to use the fixed statement in C# to ensure the object doesn't move around while you need it. But don't keep objects pinned over a long time, it just doesn't work well.


Thanks for the reply! What do you mean by managed reference? Would this stay consistent throughout the object's lifetime? How can I pass the managed reference to the C library? This is mostly for handling events. For example:


/* Some more C code */
typedef void( *CloseHandler )( intptr_t handle );

typedef struct
{
intptr_t managed;
CloseHandler close_callback;
HWND wnd;

} WindowWrapper;

intptr_t _CreateWindowWrapper( intptr_t managed, CloseHandler close_callback )
{
WindowWrapper *wrapper = malloc(sizeof(WindowWrapper));
wrapper->managed = managed;
wrapper->close_callback = close_callback;
...
return (intptr_t) wrapper;
}

void _DestroyWindowWrapper( intptr_t handle )
{
WindowWrapper *wrapper = (WindowWrapper*) handle;
DestroyWindow( wrapper->wnd );
free( wrapper );
}

LRESULT CALLBACK WindowWrapperProc( HWND wnd, UINT message, WPARAM wparam, LPARAM lparam )
{
switch( message )
{
/* User closes the WindowWrapper */
case WM_CLOSE:
{
WindowWrapper *wrapper = (WindowWrapper*) GetWindowLongPtr( wnd, GWLP_USERDATA );
wrapper->close_callback( (intptr_t) wrapper->managed );
return 0;
}
}

return DefWindowProc( wnd, message, wparam, lparam );
}





// Some more C# code
delegate void CloseHandler( WindowWrapper o );

class WindowWrapper
{
private IntPtr mHandle;
private CloseHandler mCloseCallback;

private static void OnClose( WindowWrapper o )
{
o.CloseEvent( o );
}

public event CloseHandler CloseEvent;

public WindowWrapper()
{
mHandle = _CreateWindowWrapper( this, mCloseCallback = new CloseHandler( OnClose ));
}

~WindowWrapper()
{
_DestroyWindowWrapper( mHandle );
}
}





// Some C# code using the class
class Asdf
{
private static bool mActive;

private static void OnClose( WindowWrapper o )
{
mActive = false;
}

private static void Main()
{
WindowWrapper wrapper = new WindowWrapper();
wrapper.CloseEvent += new CloseHandler( OnClose );

mActive = true;

while( mActive )
{
_PollMessages();
}
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!