我有以下代码,我是.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;
}
}
提前致谢。
答案 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
属性来指定数组的常量大小,这样除非手动编组结构的内容,否则无法在运行时确定它。