Marshal.StructureToPtr throw Ошибка попытки чтения или записи защищенной памяти

У меня есть следующий код, и я новичок в маршалинге в .Net и понятия не имею, почему Marshal.StructureToPtr работает только тогда, когда я выделяю> 32 байта для 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;
    }
}

Заранее спасибо.


person Henri    schedule 20.10.2011    source источник
comment
Вы не заботитесь о размере SAFEARRAY. Трудно сделать точно, Marshal.SizeOf() тоже не делает. 24 байта плюс размер элементов в 32-битном режиме.   -  person Hans Passant    schedule 20.10.2011


Ответы (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, чтобы указать постоянный размер массива, чтобы его нельзя было определить во время выполнения, если только вручную не упорядочить содержимое структуры.

person DeCaf    schedule 20.10.2011
comment
ДеКаф, как я могу узнать, какую SizeConst часть моей внутренней структуры мне нужно поставить? - person Konstantin; 28.11.2013
comment
Зависит от того, что определяет неуправляемая структура. Некоторые структуры имеют встроенные массивы, но это, вероятно, не очень распространено. В этом случае они имеют постоянный размер. - person DeCaf; 28.11.2013