1

I have the following code and I'm new to Marshaling in .Net and have no idea why the Marshal.StructureToPtr only works when I allocate > 32 bytes to Marshal.AllocHGlobal. Anything <= 32, throw "Attempted to read or write protected memory. This is often an indication that other memory is corrupt.".

I need the size of the array in both structure are dynamic.

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

namespace LPArrayMarshalTest
{
    class Program
    {        
        static void Main(string[] args)
        {
            try
            {

                SubInfo sInfo = new SubInfo();
                sInfo.SubID = Encoding.ASCII.GetBytes("SUB1");
                sInfo.ArrayOfItem = new ushort[1] { 1 };

                MainInfo mInfo = new MainInfo();
                mInfo.ArrayOfSubItem = new SubInfo[1] { sInfo };
                mInfo.MainID = Encoding.ASCII.GetBytes("MAIN");

                int mInfoSize = 0;
                mInfoSize += mInfo.MainID.Length;
                foreach (SubInfo sub in mInfo.ArrayOfSubItem)
                {
                    int sInfoSize = sub.SubID.Length + (sub.ArrayOfItem.Length * Marshal.SizeOf(typeof(ushort)));
                    mInfoSize += sInfoSize;
                }

                IntPtr mInfoPtr = Marshal.AllocHGlobal(mInfoSize * 3); //throw error
                //IntPtr mInfoPtr = Marshal.AllocHGlobal(mInfoSize * 4); //No Error

                Marshal.StructureToPtr(mInfo, mInfoPtr, true);

                Marshal.FreeHGlobal(mInfoPtr);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }

            Console.ReadLine();
        }        
    }


    [StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct MainInfo
    {
        [MarshalAs(UnmanagedType.SafeArray)]
        public byte[] MainID;
        [MarshalAs(UnmanagedType.SafeArray, 
        SafeArraySubType = VarEnum.VT_RECORD, 
        SafeArrayUserDefinedSubType = typeof(SubInfo))]
        public SubInfo[] ArrayOfSubItem;
    }

    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential, Pack=1)]
    public struct SubInfo
    {
        [MarshalAs(UnmanagedType.SafeArray)]
        public byte[] SubID;
        [MarshalAs(UnmanagedType.SafeArray)]
        public ushort[] ArrayOfItem;
    }
}

Thanks in advance.

Henri
  • 69
  • 1
  • 6
  • 1
    You are not taking care of the size of a SAFEARRAY. Tough to do accurately, Marshal.SizeOf() doesn't either. 24 bytes plus the size of the elements in 32-bit mode. – Hans Passant Oct 20 '11 at 17:16

1 Answers1

2

Without looking too much at your size calculations, what struck me is that you're passing true to the call to Marshal.StructureToPtr which means it will try to free the unmanaged memory as if it was allocated for the structure specified before marshalling it (and allocating memory for any pointers needed in the structure). Since you have not initialized your unmanaged memory this would likely lead to incorrect pointers being freed.

If you pass false to this call, it seems to work just fine. Also if you initialize all the unmanaged memory to zeroes before the call seems to have the same effect, for example:

IntPtr mInfoPtr = Marshal.AllocHGlobal(mInfoSize * 3); //throw error
for (int i = 0; i < mInfoSize * 3; i++)
{
   Marshal.WriteByte(mInfoPtr, i, 0);   
}
Marshal.StructureToPtr(mInfo, mInfoPtr, true);

Note that the safe array contains pointers and not all the required memory is stored in the block you allocate unless I'm mistaken. If I'm correct instead of all the size calculations you should be able to simply do:

IntPtr mInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(mInfo))

If you were to specify marshalling using UnmanagedType.ByValArray the array would be stored inline with the structure and you would have to allocate all the memory required for the elements of the array as well. However in this case you would have to also supply the SizeConst property to specify the constant size of the array so it would not be able to be determined at runtime unless manually marshalling the contents of the structure.

DeCaf
  • 6,026
  • 1
  • 29
  • 51
  • DeCaf, how can i recognize which `SizeConst` of my inner structure i neet to supply? – Konstantin Nov 28 '13 at 04:49
  • Depends on what the unmanaged structure defines. Some structures have arrays "inline", but this is probably not very common. In this case they have a constant size. – DeCaf Nov 28 '13 at 07:52