Search code examples
c#.netmarshallingintptrunmanaged-memory

How to convert an IntPtr to array of structs


I have an IntPtr which is a pointer to an array of structs, and I'm trying to convert it to the array.

I tried

Marshal.Copy(srcIntPtr, destIntPtrArray, 0, destIntPtrArray.Length);

but after doing the copy, I'm not able to convert the IntPtr's inside of the destination array to the desired structure, while I can convert the srcIntPtr to the structure, which of course only gives me the data of the first index. It looks like after doing the copy, the destination array contains a bunch of broken IntPtr's

I also tried

    var size = Marshal.SizeOf(Marshal.ReadIntPtr(myIntPtr));

    for (int i = 0; i < length; i++)
    {
        IntPtr iP = new IntPtr(myIntPtr.ToInt64() + i * size);
    
        MyStruct ms =
            (MyStruct ) Marshal.PtrToStructure(iP, typeof(MyStruct ));
    }

which doesn't throw any error, but the data of the array of structs that I get out of my source IntPtr are not accurate.

This is the struct that I'm trying to convert to

struct MyStruct
{
    public Categories categories;  
    public Dimensions dimensions;
}



public struct Categories {
    public double d;
    public double c;
    public double b;
    public double a;
}



struct Dimensions {
    public double v;
    public double e;
}

I have [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] on top of the structs, but removing that doesn't break my code.

Thanks in advance


Solution

  • This...

    Marshal.ReadIntPtr(myIntPtr)
    

    ...returns an IntPtr, as the name implies. When passed to Marshal.SizeOf()...

    var size = Marshal.SizeOf(Marshal.ReadIntPtr(myIntPtr));
    

    ...it does not return the size of the MyStruct to which it points but the size of IntPtr for the current process (either 4 or 8 bytes). To get the unmanaged size of MyStruct you need to call SizeOf() like this...

    var size = Marshal.SizeOf<MyStruct>();
    

    ...or this...

    var size = Marshal.SizeOf(typeof(MyStruct));
    

    Also, the whole point of IntPtr is that it is address width-agnostic, yet you are explicitly retrieving it as a long...

    IntPtr iP = new IntPtr(myIntPtr.ToInt64() + i * size);
    

    I don't think that will have a negative effect on anything (as opposed to if you called ToInt32() in a 64-bit process, which could throw an OverflowException), but it would be better to let IntPtr handle calculating the pointer to each MyStruct element, which you can do like this...

    IntPtr iP = IntPtr.Add(myIntPtr, i * size);
    

    ...or simply this...

    IntPtr iP = myIntPtr + i * size;