Страницы

Поиск по вопросам

пятница, 21 июня 2019 г.

System.ExecutionEngineException при попытке маршализации структуры

Есть программа на C#, предназначенная для конфигурирования устройства на микроконтроллере через USB порт. Для того, чтобы корректно передавать данные микроконтроллеру (Си, жесткие требования к ресурсам), я храню их в виде структуры фиксированного размера:
SystemPack InternalSystemPacket = new SystemPack(); // Текущий пакет данных со стороны ПК
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public unsafe struct SystemPack { public ushort PackLength; // Перед отправкой пакета контроллеру сюда надо записать общую длину пакета
// Вкладка "Информация" [MarshalAs(UnmanagedType.ByValArray, SizeConst = DEVICENAMELENGTH)] public byte[] DeviceName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = STRINGLENGTH)] public byte[] DeviceVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = REQUESTLENGTH)] public byte[] DeviceIMEI;
и т. д.
Перед передачей данных контроллеру по USB (а также для записи данных на диск) я подвергаю структуру маршализации для превращения ее в массив данных:
byte[] buffer = new byte[System.Runtime.InteropServices.Marshal.SizeOf(InternalSystemPacket)];
GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); Marshal.StructureToPtr(InternalSystemPacket, h.AddrOfPinnedObject(), false); h.Free();
Все работало нормально. Но при изменении структуры (я удлинил один ее член):
[MarshalAs(UnmanagedType.ByValArray, SizeConst = REQUESTLENGTH + 1)] public byte[] DeviceIMEI;
при попытке маршализации вылетает System.ExecutionEngineException


Ответ

Рискну предположить, что массив, записанный в поле DeviceIMEI, остался старого размера.
PS Но зачем все так сложно? Зачем вам вообще маршаллинг структуры, если все что нужно - это сформировать правильный массив?
var ms = new MemoryStream(); var writer = new BinaryWriter(ms); writer.Write((ushort)0); // Место для длины пакета
Debug.Assert(packet.DeviceName.Length == DEVICENAMELENGTH); writer.Write(packet.DeviceName);
Debug.Assert(packet.DeviceVersion.Length == STRINGLENGTH); writer.Write(packet.DeviceVersion);
Debug.Assert(packet.DeviceIMEI.Length == REQUESTLENGTH); writer.Write(packet.DeviceIMEI);
var length = writer.Seek(0, SeekOrigin.Current); writer.Seek(0, SeekOrigin.Begin); writer.Write((ushort)length); writer.Flush();
var packetData = ms.ToArray();
Кстати, BinaryWriter умеет и строки напрямую записывать, если ему в конструктор кодировку передать. Только с проверкой длины придется что-нибудь думать, вроде такого:
// Код ниже напрашивается на вынесение в отдельную подпрограмму var i = writer.Seek(0, SeekOrigin.Current); writer.Write(packet.DeviceName); // Допустим, это - строка i = writer.Seek(0, SeekOrigin.Current) - i; if (i > DEVICENAMELENGTH) throw new ArgumentException("too long", "packet.DeviceName"); else for (; i < DEVICENAMELENGTH; i++) writer.Write((byte)0);

Комментариев нет:

Отправить комментарий