【问题标题】:Marshal.StructureToPtr throw "Attempted to read or write protected memory" errorMarshal.StructureToPtr 抛出“试图读取或写入受保护的内存”错误
【发布时间】:2011-10-20 15:30:49
【问题描述】:

我有以下代码,我是 .Net 编组的新手,不知道为什么 Marshal.StructureToPtr 仅在我为 Marshal.AllocHGlobal 分配 > 32 个字节时才有效。任何

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

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

提前致谢。

【问题讨论】:

  • 您没有注意 SAFEARRAY 的大小。很难准确地做到, Marshal.SizeOf() 也没有。 24 字节加上 32 位模式中元素的大小。

标签: marshalling


【解决方案1】:

无需过多查看您的大小计算,令我印象深刻的是您将 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 属性来指定数组的常量大小,因此除非手动编组结构的内容,否则无法在运行时确定它。

【讨论】:

  • DeCaf,我如何识别我需要提供的内部结构的 SizeConst
  • 取决于非托管结构定义的内容。一些结构具有“内联”数组,但这可能不是很常见。在这种情况下,它们的大小是恒定的。
猜你喜欢
  • 2023-04-08
  • 2010-12-27
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
相关资源
最近更新 更多