Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: HenriОтветов: 1

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

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

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

marshalling

Ответы

Ответ 1 · рейтинг 2 · DeCaf

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы