none
Marshal: struct containing pointer-to-array-of-structs from C to C# RRS feed

  • Question

  • Hi,

    I have a function in an unmanaged DLL.  The function returns a pointer to a structure where one field of that structure points to an array of structures.  Something like this:

     

    typedef struct tStructA
    {
        int i;
        float f;
        char c;
    } StructA;
    
    typedef struct tStructB
    {
        unsigned int numElements;
        StructA * pStructA;
    } StructB;
    
    extern __declspec(dllexport) void GetStructFromDll(StructB *p);
    
    static StructA structA[255] = {0};
    static StructB structB = { 255, structA };
    
    void GetStructFromDll(StructB *p)
    {
        structA[0].i = 1;
        structA[0].f = 1.0f;
        structA[0].c = 128;
        *p = structB;
    }
    
    

     


    Here's a snip of C# code calling this DLL function:

     

    [StructLayout(LayoutKind.Sequential)]
    private struct StructA
    {
        public int i;
        public float f;
        public byte c;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    private struct StructB
    {
        public uint numElements;
        public IntPtr pStructA;
    }
    
    [DllImport("NameOfLibrary.dll",
               EntryPoint = "GetStructFromDll")]
    private static extern void GetStructFromDll(ref StructB structB);
    
    public void TestDllFunc()
    {
        StructB structB = new StructB();
    
        GetStructFromDll(ref structB);
        Console.WriteLine(structB.numElements);
        if (structB.numElements > 0)
        {
            // how do I retrieve all 255 elements of the array?
        }
    }
    
    

    The value of structB.numElements after the call to GetStructFromDll is correct (255) so the C# debugger enters the "if".  I've tried many attempts to retrieve the array of StructA[255] but usually end up with an exception (note that one of the fields of StructA must be "float" which is giving me blittable grief).   Rather than mislead with code which I know doesn't work can I simply ask if anybody can help me retrieve StructA[] inside the "if" please?

     






    • Edited by xyz123 Monday, January 9, 2012 2:13 PM
    • Moved by Leo Liu - MSFT Tuesday, January 10, 2012 7:07 AM Moved for better support. (From:Visual C# General)
    Monday, January 9, 2012 12:38 PM

Answers

  • Hi,
    StructA[] elements = new StructA[structB.numElements];
    int sizeOfStructA = Marshal.SizeOf(typeof(StructA));
    for (int i = 0; i < structB.numElements; i++)
    {
            elements[i] = (StructA)Marshal.PtrToStructure(structB.pStructA + sizeOfStructA * i, typeof(StructA));
    }
    

    Thanks for this suggestion but the line inside the "for" loop doesn't compile reporting "Operator '+' cannot be applied to operands of type 'System.IntPtr' and 'int'".  The suggestion prompted me to look up an IntPtr.Add method, which exists, but only for .NET 4.0 or later (which I don't have).  I've come up with two solutions now.  One is based on your suggestion:

     

    if (structB.numElements > 0)
    {
        StructA[] elements = new StructA[structB.numElements];
        int sizeOfStructA = Marshal.SizeOf(typeof(StructA));
        for (int i = 0; i < structB.numElements; i++)
        {
            IntPtr intPtr = new IntPtr(structB.pStructA.ToInt32() + (sizeOfStructA * i));
            elements[i] = (StructA)Marshal.PtrToStructure(intPtr, typeof(StructA));
        }
    }
    
    

    The second uses unsafe pointers (so the project is marked as permitting unsafe code):

    public unsafe void TestDllFunc()
    {
        /* "added "unsafe" keyword, otherwise same code as before to get structB then... */
        if (structB.numElements > 0)
        {
            StructA[] elements = new StructA[structB.numElements];
            StructA* structA_array = (StructA*)structB.pStructA;
            for (int i = 0; i < structB.numElements; i++)
            {
                elements[i] = structA_array[i];
            }
        }
    }
    
    

    Both of these solutions are iterative.  Is there a mechanism to copy the entire array from unmanaged memory to managed objects without having to iterate over all entries?

     




    • Marked as answer by xyz123 Tuesday, January 10, 2012 2:04 PM
    • Edited by xyz123 Tuesday, January 10, 2012 2:06 PM
    Tuesday, January 10, 2012 9:36 AM

All replies

  • StructA[] elements = new StructA[structB.numElements];
    int sizeOfStructA = Marshal.SizeOf(typeof(StructA));
    for (int i = 0; i < structB.numElements; i++)
    {
            elements[i] = (StructA)Marshal.PtrToStructure(structB.pStructA + sizeOfStructA * i, typeof(StructA));
    }
    

    Monday, January 9, 2012 5:13 PM
  • Hi,
    StructA[] elements = new StructA[structB.numElements];
    int sizeOfStructA = Marshal.SizeOf(typeof(StructA));
    for (int i = 0; i < structB.numElements; i++)
    {
            elements[i] = (StructA)Marshal.PtrToStructure(structB.pStructA + sizeOfStructA * i, typeof(StructA));
    }
    

    Thanks for this suggestion but the line inside the "for" loop doesn't compile reporting "Operator '+' cannot be applied to operands of type 'System.IntPtr' and 'int'".  The suggestion prompted me to look up an IntPtr.Add method, which exists, but only for .NET 4.0 or later (which I don't have).  I've come up with two solutions now.  One is based on your suggestion:

     

    if (structB.numElements > 0)
    {
        StructA[] elements = new StructA[structB.numElements];
        int sizeOfStructA = Marshal.SizeOf(typeof(StructA));
        for (int i = 0; i < structB.numElements; i++)
        {
            IntPtr intPtr = new IntPtr(structB.pStructA.ToInt32() + (sizeOfStructA * i));
            elements[i] = (StructA)Marshal.PtrToStructure(intPtr, typeof(StructA));
        }
    }
    
    

    The second uses unsafe pointers (so the project is marked as permitting unsafe code):

    public unsafe void TestDllFunc()
    {
        /* "added "unsafe" keyword, otherwise same code as before to get structB then... */
        if (structB.numElements > 0)
        {
            StructA[] elements = new StructA[structB.numElements];
            StructA* structA_array = (StructA*)structB.pStructA;
            for (int i = 0; i < structB.numElements; i++)
            {
                elements[i] = structA_array[i];
            }
        }
    }
    
    

    Both of these solutions are iterative.  Is there a mechanism to copy the entire array from unmanaged memory to managed objects without having to iterate over all entries?

     




    • Marked as answer by xyz123 Tuesday, January 10, 2012 2:04 PM
    • Edited by xyz123 Tuesday, January 10, 2012 2:06 PM
    Tuesday, January 10, 2012 9:36 AM
  • Hello xyz123,

    >> Both of these solutions are iterative. Is there a mechanism to copy the entire array from unmanaged memeory to managed objects without having to iterate over all entries?

    Unfortunately, no. This is because each unmanaged StructA structure in the structB.pStructA array must be individually converted to a managed counterpart. This is what Marshal.PtrToStructure() does. It marshals field values from an unmanaged structure to a managed structure.

    Since all the fields of the managed StructA structure are blittable, the marshaling is done directly and efficiently. If there be any non-blittable fields in StructA, then help in the form of MarshalAsAttributes (applied to the non-blittable fields) will be required.

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    Tuesday, January 10, 2012 1:54 PM
  • Thanks for the detail.  Post#3 marked as answer.
    Tuesday, January 10, 2012 2:04 PM