11

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.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Your title is poorly worded. You can't convert a struct to an IntPtr. You can, however, get a pointer to a struct. – Jim Mischel Jul 10 '13 at 03:38
  • @JimMischel: True. I wasn't thinking about actually "converting" it, I was thinking of something along the lines of a cast; in C you can slap an `&` before a variable to get its address. – mpen Jul 10 '13 at 03:44
  • 1
    Using `Marshal.AllocHGlobal` as in the `StructToPtr` shown above, places a significant risk to memory leakage. `AllocHGlobal` allocates memory that must be manually freed using `Marshal.FreeHGlobal`. The shown `Copy` method doesn't do that. – C. Rahn Jan 24 '20 at 20:23

2 Answers2

7

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/

E-rich
  • 9,243
  • 11
  • 48
  • 79
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
  • It requires 3 parameters. It seems I have to preallocate memory for it. – mpen Jul 10 '13 at 03:20
  • Yeah, sorry about that. I editted the answer and pointed you to a blog. – Richard Schneider Jul 10 '13 at 03:21
  • Did some more searching and I think a better solution can be found at http://stackoverflow.com/questions/1049623/how-to-pass-a-nullable-type-to-a-p-invoked-function – Richard Schneider Jul 10 '13 at 03:24
  • [The solution](http://stackoverflow.com/a/1049660/65387) posted there is the same as the middle section of my question -- to create multiple overloads; one taking an `IntPtr`, the other a `ref`. It gets hairy when you need more than one of these in the same function; requiring 2^n overloads. – mpen Jul 10 '13 at 03:28
-3

Here's what I have, I'm using an API from a M$ DLL and the prototype is like this:

HRESULT C_API(LPCWSTR name, PSTRUCT_WHATEVER *ppStruct);

The structure is defined as such:

typedef struct whatever {
    LPCWSTR x;
    LPCWSTR y;
}

In C# I am defining the following:

[StructLayout(LayoutKind.Sequential)]
public class WHATEVER {
    public IntPtr x;
    public IntPtr y;
}

[DllImport("msdll.dll", SetLastError=false, CharSet=CharSet.Unicode)]
    public static extern long C_API(String name, out IntPtr ppStruct);

To use it:

IntPtr s;           
long HR = C_API("myname", out s);

WHATEVER pInfo = (WHATEVER) Marshal.PtrToStructure(s, typeof(WHATEVER));

String mystring = Marshal.PtrToStringAuto(pInfo.x);

At this point mystring = "this is a string";

E.T
  • 1,095
  • 1
  • 10
  • 19