none
Bug in by value array marshaling? RRS feed

  • Question

  • I believe there is a problem with "by value" array marshaling of arrays of pointers.  The test program below defines 4 structures, each containing a "by value" array of 8 pointers.  Since pointers are always 4 bytes (I'm compiling for x86), I would expect that Marshal.SizeOf() would return the same value for each of these structures.  However the size returned is 8 times the size of the type the pointers are pointing to, rather than 8 times the size of a pointer.

    Here is my test program:

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using System.Runtime.InteropServices; 
     
    namespace MarshalTest 
        class Program 
        { 
            static void Main(string[] args) 
            { 
                BytePointers bp = new BytePointers(); 
                ShortPointers sp = new ShortPointers(); 
                IntPointers ip = new IntPointers(); 
                LongPointers lp = new LongPointers(); 
                Console.WriteLine("sizeof(BytePointers) : {0}", Marshal.SizeOf(bp)); 
                Console.WriteLine("sizeof(ShortPointers) : {0}", Marshal.SizeOf(sp)); 
                Console.WriteLine("sizeof(IntPointers) : {0}", Marshal.SizeOf(ip)); 
                Console.WriteLine("sizeof(LongPointers) : {0}", Marshal.SizeOf(lp)); 
     
            } 
        } 
     
        [StructLayout(LayoutKind.Sequential)] 
        public unsafe struct BytePointers 
        { 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
            public byte*[] pointers; 
        } 
     
        [StructLayout(LayoutKind.Sequential)] 
        public unsafe struct ShortPointers 
        { 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
            public short*[] pointers; 
        } 
     
        [StructLayout(LayoutKind.Sequential)] 
        public unsafe struct IntPointers 
        { 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
            public int*[] pointers; 
        } 
     
        [StructLayout(LayoutKind.Sequential)] 
        public unsafe struct LongPointers 
        { 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
            public long*[] pointers; 
        } 
     

    The output of this program is:

    sizeof(BytePointers) : 8
    sizeof(ShortPointers) : 16
    sizeof(IntPointers) : 32
    sizeof(LongPointers) : 64
    Is this indeed a bug, or is there some magic option that I'm missing to get it to behave the way I expect?

    Thanks.

      --Jeff


    Monday, July 7, 2008 6:32 AM

Answers

  • Hmm that does look odd, it may well be a bug or it may be interpreting the syntax in some non-intuitive manner, I'm not sure.

    However if you do this it will work, this is a small change and actually makes for more readable code I think:

    Note I made the arrays a size of two as I played around, and I only ran it as 64-bit, but each array comes out as being 16 bytes which is correct under these circumstances.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices; 

    namespace Trash
    {
        class Program
        {
            static unsafe void Main(string[] args)
            {
               
                BytePointers bp = new BytePointers();
                ShortPointers sp = new ShortPointers();
                IntPointers ip = new IntPointers();
                LongPointers lp = new LongPointers();
               
                Console.WriteLine("sizeof(BytePointers) : {0}", Marshal.SizeOf(bp));
                Console.WriteLine("sizeof(ShortPointers) : {0}", Marshal.SizeOf(sp));
                Console.WriteLine("sizeof(IntPointers) : {0}", Marshal.SizeOf(ip));
                Console.WriteLine("sizeof(LongPointers) : {0}", Marshal.SizeOf(lp));

            }
        }

        public unsafe struct Byte_Ptr
        {
            public byte *    pointer;
        }
       
        public unsafe struct Short_Ptr
        {
            public short * pointer;
        }
       
        public unsafe struct Int_Ptr
        {
            public int *    pointer;
        }
       
        public unsafe struct Long_Ptr
        {
            public long *   pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct BytePointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Byte_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct ShortPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Short_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct IntPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Int_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct LongPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Long_Ptr[] pointers;
        }
    }


    Regards
    Hugh



    • Edited by Captain Kernel Monday, July 7, 2008 5:04 PM fixup
    • Marked as answer by Zhi-Xin Ye Thursday, July 10, 2008 8:28 AM
    Monday, July 7, 2008 4:53 PM

All replies

  • Bug is a big word, Marshal is probably just not designed to safely marshal unsafe pointers.  Using ArraySubType doesn't help either, I only see declaring the array element type as IntPtr as a workaround.  You can post to the Connect web site but don't be disappointed if your case is closed with "By Design".
    Hans Passant.
    Monday, July 7, 2008 12:50 PM
    Moderator
  • Hmm that does look odd, it may well be a bug or it may be interpreting the syntax in some non-intuitive manner, I'm not sure.

    However if you do this it will work, this is a small change and actually makes for more readable code I think:

    Note I made the arrays a size of two as I played around, and I only ran it as 64-bit, but each array comes out as being 16 bytes which is correct under these circumstances.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices; 

    namespace Trash
    {
        class Program
        {
            static unsafe void Main(string[] args)
            {
               
                BytePointers bp = new BytePointers();
                ShortPointers sp = new ShortPointers();
                IntPointers ip = new IntPointers();
                LongPointers lp = new LongPointers();
               
                Console.WriteLine("sizeof(BytePointers) : {0}", Marshal.SizeOf(bp));
                Console.WriteLine("sizeof(ShortPointers) : {0}", Marshal.SizeOf(sp));
                Console.WriteLine("sizeof(IntPointers) : {0}", Marshal.SizeOf(ip));
                Console.WriteLine("sizeof(LongPointers) : {0}", Marshal.SizeOf(lp));

            }
        }

        public unsafe struct Byte_Ptr
        {
            public byte *    pointer;
        }
       
        public unsafe struct Short_Ptr
        {
            public short * pointer;
        }
       
        public unsafe struct Int_Ptr
        {
            public int *    pointer;
        }
       
        public unsafe struct Long_Ptr
        {
            public long *   pointer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct BytePointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Byte_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct ShortPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Short_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct IntPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Int_Ptr[] pointers;
        }

        [StructLayout(LayoutKind.Sequential)]
        public unsafe struct LongPointers
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public Long_Ptr[] pointers;
        }
    }


    Regards
    Hugh



    • Edited by Captain Kernel Monday, July 7, 2008 5:04 PM fixup
    • Marked as answer by Zhi-Xin Ye Thursday, July 10, 2008 8:28 AM
    Monday, July 7, 2008 4:53 PM
  • In case anyone else runs across this problem, I wanted to mention the final resolution.  I just heard back from Microsoft that the issue has been fixed for the next release of .NET.

      --Jeff
    Monday, August 25, 2008 6:55 AM