Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get an IntPtr to a struct?

Tags:

c#

interop

I've got a method with the signature

public int Copy(Texture texture, Rect? srcrect, Rect? dstrect)

Rect is a struct, but I need to allow the caller to pass null (or IntPtr.Zero) to the method as well.

I want to then pass it off to a DLL with the signature

[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_RenderCopy")]
internal static extern int RenderCopy(IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect);

I was hoping I could do something like the following:

return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? (IntPtr)srcrect.Value : IntPtr.Zero, dstrect.HasValue ? (IntPtr)dstrect.Value : IntPtr.Zero);

But I can't cast the struct like that. Is there some other way I can get an IntPtr out of it?


There alternative is to create 4 overloads:

  • ref Rect, ref Rect
  • IntPtr, IntPtr
  • ref Rect, IntPtr
  • IntPtr, ref Rect

which could get even messier if I ever need to pass more than 2 struct pointers.


I came up with a solution, but I have some questions about it:

public int Copy(Texture texture, Rect? srcrect=null, Rect? dstrect=null)
{
    return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? StructToPtr(srcrect) : IntPtr.Zero, dstrect.HasValue ? StructToPtr(dstrect) : IntPtr.Zero);
}

private static IntPtr StructToPtr(object obj)
{
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
    Marshal.StructureToPtr(obj, ptr, false);
    return ptr;
}

Had I used ref Rect I wouldn't have had to allocate memory for the struct -- what does that do differently than this does?


I did some experimentation. The ref Rect solution runs at about the same speed as converting a Rect to an IntPtr generating an IntPtr for a Rect, which leads me to suspect that C# is doing something very similar under the hood when you use refs. As soon as I make it a Rect? and add the conditional logic to the method it runs up to 50% slower... so the 4-overload route would probably be the fastest. However, we're talking 100-150ms for 100K iterations, which means the method itself is super cheap, which is probably why the conditionals have such a notable impact. As such, I'm sticking with my custom StructToPtr solution as it's the simplest solution.

like image 541
mpen Avatar asked Jul 10 '13 02:07

mpen


People also ask

How does IntPtr work?

An IntPtr is a value type that is primarily used to hold memory addresses or handles. A pointer is a memory address. A pointer can be typed (e.g. int* ) or untyped (e.g. void* ).

How can struct be instantiated?

When you create a struct object using the New operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the New operator. If the New operator is not used, the fields remain unassigned and the object cannot be used until all the fields are initialized.

What is the use of IntPtr in C#?

The IntPtr type can be used by languages that support pointers and as a common means of referring to data between languages that do and do not support pointers. IntPtr objects can also be used to hold handles. For example, instances of IntPtr are used extensively in the System.

Can you have a struct in a class C#?

Yes, you can. The pointer to the class member variable is stored on the stack with the rest of the struct's values, and the class instance's data is stored on the heap.


1 Answers

You want to use Marshal.StructureToPtr.

You will also have to allocate and deallocate memory for the struct.

A good blog on the subject can be found at http://www.developerfusion.com/article/84519/mastering-structs-in-c/

like image 199
Richard Schneider Avatar answered Oct 18 '22 14:10

Richard Schneider