Marshal.StructureToPtr抛出“试图读取或写入受保护的内存”错误

时间:2011-10-20 15:30:50

标签: marshalling

我有以下代码,我是.Net中的Marshaling的新手,并且不知道为什么Marshal.StructureToPtr仅在我分配>时才有效Marshal.AllocHGlobal的32个字节。任何< = 32,throw“尝试读取或写入受保护的内存。这通常表示其他内存已损坏。”。

我需要两个结构中数组的大小都是动态的。

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;
    }
}

提前致谢。

1 个答案:

答案 0 :(得分:2)

不要过多考虑你的大小计算,让我感到震惊的是你将true传递给Marshal.StructureToPtr的调用,这意味着它将尝试释放非托管内存,就像它被分配一样对于在编组之前指定的结构(以及为结构中所需的任何指针分配内存)。由于您尚未初始化非托管内存,因此可能会导致不正确的指针被释放。

如果您将false传递给此调用,它似乎工作得很好。此外,如果在调用似乎具有相同效果之前将所有非托管内存初始化为零,例如:

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);

请注意,安全数组包含指针,并且并非所有必需的内存都存储在您分配的块中,除非我弄错了。如果我是正确的而不是所有的大小计算你应该能够做到:

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

如果要使用UnmanagedType.ByValArray指定编组,则数组将与结构内联存储,并且您还必须分配数组元素所需的所有内存。但是在这种情况下,您还必须提供SizeConst属性来指定数组的常量大小,这样除非手动编组结构的内容,否则无法在运行时确定它。