none
Marshaling nested structures using IntPtr RRS feed

  • Question

  • Hi friends,

    I have a structure in C that would be something similar to the following

    Struct marks

    {

    int m1, m2.m3, m4;

    }

    Struct person

    {

    marks[32];

    int m5, m6;

    }

    The exported method would be something like the one below

    Void Method1(person* p1)

    {

    //We populate the structure within this method….

    }

    Now when I marshel this code in C# using Pinvoke I have a similar structure like

    Struct marks

    {

    int m1, m2,m3;

    }

    Struct person

    {

    Intptr ptr1; //for handling the marks[32] structure element

    Int m5 ,m6;

    }




    1. Now I first create an object of type person and marks

    Person P1;

    Marks M1;

    2.  I allocate memory for my IntPtr

    P1.ptr1 = Marshal.AllocHGlobal(Marshal.SizeOf( M1)*32) //as there are 32 marks structure within a person object..

    3.Now I call my exported method in C. (I’ve got my DllImport stmt correct. So lets not worry about it )

    Method1(ref  P1);

    Now I know we’ve done somethings right as I’m able to access m5 and m6 which are the other 2 members of object P1.

    4. Now in order to access the array what I do is the following

    Marks[] temp = new Marks[32];

    Then I Call

    Marshal.PtrToStructure(P1.Ptr1, temp);

    Expecting that within temp which is an array of 32 elements of type marks would automatically get populated. But that does not happen

    Instead I get the following error message

    System.ArgumentException was unhandled

      Message="The specified structure must be blittable or have layout information

      Source="mscorlib"

      ParamName="structure"

    Could you frinds figure this out for me. I’d be very glad to hear from you folks.

    Thanks in advance.

    Best Regards,

    Raj

    Monday, March 24, 2008 4:06 PM

Answers

  • How about like this.


            public struct Mark
            {

    public Mark(int m1,int m2, int m3, int m4)
    {
    M1 = m1;
    M2 = m2;
    M3 = m3;
    M4 = m4;
    }
                public int M1;
                public int M2;
                public int M3;
                public int M4;

            }

            [StructLayout(LayoutKind.Sequential, Size = 520)]
            public unsafe struct Person
            {

                public fixed int Marks[128];
                public int M5;
                public int M6;

                public Mark this [int index]
                {
                    get
                    {
                        return new Mark(
                            Marks[index * 4],
                            Marks[index * 4] + 1,
                            Marks[index * 4] + 2,
                            Marks[index * 4] + 3);
                    }
                }

            }

    or this maybe this will work.  But I dont this it works with structs either.

            [StructLayout(LayoutKind.Sequential, Size = 520)]
            public unsafe struct Person
            {
                [MarshalAs(UnmanagedType.ByValArray,SizeConst=512)]
                public fixed Mark Marks[32];
                public int M5;
                public int M6;

            }
    Tuesday, March 25, 2008 5:36 AM
  • Wow Rahim,
    We did it.......Thank you so so much...

    It should be
    struct Person
    {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public Mark[] studMarks; //C# says during declaration i will not be able to specify the size of the array
    public short m1;
    public short m2;
    public short m3;
    public short m4;
    }

    and all falls in their place buddy.
    Thanks a lot for keenly following this one up..
    Great work man..Thanks
    God Bless u

    Best regards,
    Raj
    Tuesday, March 25, 2008 6:56 AM

All replies

  • The way the structs look like a pointer will not work.  Try using the fixed keyword.  Here is an example below.

     

    [StructLayout(LayoutKind.Sequential,Size=16)]

    public struct Mark

    {

    public int M1;

    public int M2;

    public int M3;

    public int M4;

    }

    [StructLayout(LayoutKind.Sequential, Size = 520)]

    public struct Person

    {

    public fixed Mark Marks[32];

    public int M5;

    public int M6;

    }

    Monday, March 24, 2008 6:52 PM
  • Hi Abdel,
    Thanks a lot in the first place to looking into the issue.
    Now I believe such a thing is not possible for the compiler says that "Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double "

    But here as you see we have the type of a structure so keyword fixed could not be used with it. I understand that we got to marshal things only with an Intptr but the problem again is as you see in my first post that when I do Marshal.PtrToStructure(P1.Ptr1, temp); the temp structure does not get populated.

    And after further research I found out that we could do Marshal.PtrToStructure() only with Blittable types. And in case of non blittable types we need to do it manually. Now we need to figure out how to do this manual marshalling. This is my understanding.

    Correct me if I got something wrong.

    Thanks,

    Best Regards,

    Raj
    Tuesday, March 25, 2008 5:29 AM
  • How about like this.


            public struct Mark
            {

    public Mark(int m1,int m2, int m3, int m4)
    {
    M1 = m1;
    M2 = m2;
    M3 = m3;
    M4 = m4;
    }
                public int M1;
                public int M2;
                public int M3;
                public int M4;

            }

            [StructLayout(LayoutKind.Sequential, Size = 520)]
            public unsafe struct Person
            {

                public fixed int Marks[128];
                public int M5;
                public int M6;

                public Mark this [int index]
                {
                    get
                    {
                        return new Mark(
                            Marks[index * 4],
                            Marks[index * 4] + 1,
                            Marks[index * 4] + 2,
                            Marks[index * 4] + 3);
                    }
                }

            }

    or this maybe this will work.  But I dont this it works with structs either.

            [StructLayout(LayoutKind.Sequential, Size = 520)]
            public unsafe struct Person
            {
                [MarshalAs(UnmanagedType.ByValArray,SizeConst=512)]
                public fixed Mark Marks[32];
                public int M5;
                public int M6;

            }
    Tuesday, March 25, 2008 5:36 AM
  • Hi Rahim,
    Think we are making some progress. But still not working fine..
    Ok let me share some of my code with you ok..

    The structure that would be embedded
     [StructLayout(LayoutKind.Sequential)]
        struct Mark
    {
    public short x1;
    public short x2;
    public short x3;
    public short x4;

    }

    The structure that would contain the embedded structure

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi,Size=528)]
        struct Person
    {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] // is this 128 correct ? SizeConst says its the no of elements present in the array.
            public Mark[] studMarks; //C# says during declaration i will not be able to specify the size of the array
    public short m1;
    public short m2;
    public short m3;
    public short m4;
    }

    Now my exported method would have the following signature

    void Calculate(ref Person);

    now before calling Calculate what i do is
    Person P;
    P.StudMarks = new Mark[32];  // I initialize only here
    after this ,I say
    Calculate(ref P)
    Now the new error which I get is "Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout."

    Please look at this problem itself for i think we are missing some trivial thing only...

    Best regards,

    Raj

    Tuesday, March 25, 2008 6:40 AM
  • Wow Rahim,
    We did it.......Thank you so so much...

    It should be
    struct Person
    {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public Mark[] studMarks; //C# says during declaration i will not be able to specify the size of the array
    public short m1;
    public short m2;
    public short m3;
    public short m4;
    }

    and all falls in their place buddy.
    Thanks a lot for keenly following this one up..
    Great work man..Thanks
    God Bless u

    Best regards,
    Raj
    Tuesday, March 25, 2008 6:56 AM
  • You changed to shorts.  Add Pack=1 to the struct layout on both structs since pack defaults to 4.  Also make sure the sizeconst is correct, it should be number of items in the array.  If 128 is the number of elements then the size for your struct would be 128 * (2*4) + (2*2).  Which would be 1028.  Again I am not sure the marshaller will work with structs just like the fixed keyword.  You should maybe try the other example with the fixed keyword and ints or shorts.  Just make just you get all the sizes right.  Here is also one more example assuming the total size of the struct is 1028 but this way is no good.


                IntPtr _Ptr =  Marshal.AllocHGlobal(1028);
                Calc(_Ptr);
                byte[] _Data = new byte[1028];
                Marshal.Copy(_Ptr, _Data, 0, 1028);
                Marshal.FreeHGlobal(_Ptr);

    // Parse the byte array
    Person _Peron = Person.ParseBytes ( _Data);

    Tuesday, March 25, 2008 7:14 AM
  • Hi Rahiem,
    Hope you took a note of my previous post. The issue has been resolved. And the SizeConst has to be the number of array elements and it should be 32. Though each element would in turn have four ints we need not bother about it. It just works fine
    But let me also try this with Intptr (thats what we intended to do in the begining right Smile ) Thanks for your support
    Tuesday, March 25, 2008 12:41 PM