Marshal PtrToStructure Creating Garbage Memory

O

O.B.

In the following example, the MyStructure constructor takes an array
of 8 bytes. I was hoping that PtrToStructure would copy only the
first 8 bytes. But for some reason, it is also instantiating Records
with an array of length 1 and putting random values in it. I desire
for this array to remain null if there is no memory to marshal to it.
What am I doing wrong?

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
public class Program
{
public struct Human
{
public int NumLegs;
public int NumArms;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class MyStructure
{
public int Padding;

public uint NumRecords;

[MarshalAs(UnmanagedType.ByValArray, ArraySubType =
UnmanagedType.Struct)]
public Human[] Records;

public MyStructure(byte[] rawData)
{
unsafe
{
fixed (byte* pData = rawData)
{
Marshal.PtrToStructure((IntPtr)pData, this);
}
}
}
}

public Program()
{
byte[] testData = new byte[8];
MyStructure test = new MyStructure(testData);

// I'm expecting test.Records to be null. Why is it
instantiated
// with a size of 1?
Console.WriteLine("Number of Records = " +
test.Records.Length);
}

public static void Main(string[] args)
{
new Program();
}
}
}
 
P

Pavel Minaev

In the following example, the MyStructure constructor takes an array
of 8 bytes.  I was hoping that PtrToStructure would copy only the
first 8 bytes.  But for some reason, it is also instantiating Records
with an array of length 1 and putting random values in it.  I desire
for this array to remain null if there is no memory to marshal to it.
What am I doing wrong?

using System;
using System.Runtime.InteropServices;

namespace MarshallingTest
{
    public class Program
    {
        public struct Human
        {
            public int NumLegs;
            public int NumArms;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public class MyStructure
        {
            public int Padding;

            public uint NumRecords;

            [MarshalAs(UnmanagedType.ByValArray, ArraySubType=
UnmanagedType.Struct)]
            public Human[] Records;

            public MyStructure(byte[] rawData)
            {
                unsafe
                {
                    fixed (byte* pData = rawData)
                    {
                        Marshal.PtrToStructure((IntPtr)pData, this);
                    }
                }
            }
        }

        public Program()
        {
            byte[] testData = new byte[8];
            MyStructure test = new MyStructure(testData);

            // I'm expecting test.Records to be null.  Why is it
instantiated
            // with a size of 1?
            Console.WriteLine("Number of Records = " +
test.Records.Length);
        }

        public static void Main(string[] args)
        {
            new Program();
        }
    }
}

MSDN says:

"When MarshalAsAttribute.Value is set to ByValArray, the SizeConst
must be set to indicate the number of elements in the array."

So it doesn't use any kind of magic to guess how many array elements
are there - it just assumes that there will be as many as SizeConst
says. The default for it is 0, which is technically invalid (it must
be >= 1), but I suspect that it just quietly treats it as 1 in this
case.
 
B

Ben Voigt [C++ MVP]

O.B. said:
In the following example, the MyStructure constructor takes an array
of 8 bytes. I was hoping that PtrToStructure would copy only the
first 8 bytes. But for some reason, it is also instantiating Records

But why would it stop at 8 bytes? You're giving it an unsafe pointer, which
carries no length information of any sort. PtrToStructure will start at the
memory you gave it and happily continue reading until satiated or it crosses
into invalid address space.
public MyStructure(byte[] rawData)
{
unsafe
{
fixed (byte* pData = rawData)
{
Marshal.PtrToStructure((IntPtr)pData, this);
}
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top