FatalExecutionEngineError во время C # Marshalling - PullRequest
2 голосов
/ 25 марта 2011

Я сталкиваюсь с проблемой при попытке прочитать структуры c ++ из memofields в ряде файлов DBase IV в C # (.Net 4) и затем вставить их в MSSQL 2008. Данные извлекаются нормально из файлов DBaseно я, похоже, что-то не так делал с управлением небезопасными вызовами, потому что я случайно получаю следующую ошибку:

FatalExecutionEngineError was detected
Message: The runtime has encountered a fatal error. The address of the error was
at 0x791fa62c, on thread 0x16c0. The error code is 0xc0000005. This error may be
a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common
sources of this bug include user marshaling errors for COM-interop or PInvoke, 
which may corrupt the stack. 

Ниже приведен код, который я использую для чтения данных.Этот код успешно завершен, и данные, которые я получаю из файлов, верны.Ошибка возникает через несколько минут после вызова этой функции, когда объекты вставляются в базу данных с помощью nhibernate (я пропустил это, потому что не думал, что это действительно имеет значение для проблемы).

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Event
{        
    public int      Number;
    public int      Month;
    public int      Day;
    public int      Year;
    public int      Hour;
    public int      Minute;
    public int      Second;
    public UInt32   UPCTime;
    public int      BlobSize;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = DataLengths.MAX_EVENT_TITLE_LENGTH)]
    public string   Title;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = DataLengths.MAX_TRIGGER_LENGTH)]
    public string Trigger;
 }

public struct Trigger
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string Control;
    public int Index;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string Mode;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string RecordFreq;
    public int Pre;
    public int Post;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string Source;
    public int Delay;
    public int EventUserNotify;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
    public int[] Spare;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = DataLengths.MAX_EVENT_SENSORS)]
    public int[] Sensors;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Value
{
    public Trigger Trigger;
    public string[] SensorLabels;
    public Point[] Point;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Point
{
    public Single Value;
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string State;
}

//The dbf is from the java xBasej library that I compiled into a dll using IKVM.net
public Dictionary<Event, Value> Read(DBF dbf)
{     
Dictionary<Event, Value> eventData = new Dictionary<Event, Value>();

try
{
    for (int i = 1; i <= dbf.getRecordCount(); i++)
    {
         dbf.gotoRecord(i);
         MemoField memofield = (MemoField)dbf.getField("MemoField");

         // Perform the conversion from one encoding to the other.
         byte[] blob = memofield.getBytes();

         if (blob.Length == 0)
         {
             continue;
         }

         MemoryStream memoryStream = null;
         BinaryReader binaryReader = null;
         GCHandle eventHandle = new GCHandle();
         GCHandle triggerHandle = new GCHandle();
         GCHandle sensorLabelHandle = new GCHandle();
         GCHandle pointHandle = new GCHandle();

         try
         {
             memoryStream = new MemoryStream(blob);
             binaryReader = new BinaryReader(memoryStream);

             //The data was orignally C++ structures so we read the bytes back into C# equivalent
             //structures.
             int eventDataSize = Marshal.SizeOf(typeof(Event));

             eventHandle = GCHandle.Alloc(binaryReader.ReadBytes(eventDataSize), GCHandleType.Pinned);
             Event @event = (Event)Marshal.PtrToStructure(eventHandle.AddrOfPinnedObject(), typeof(Event));

             //Read the event trigger data
             int triggerDataSize = Marshal.SizeOf(typeof(Trigger));
             triggerHandle = GCHandle.Alloc(binaryReader.ReadBytes(triggerDataSize), GCHandleType.Pinned);
             Trigger trigger = (Trigger)Marshal.PtrToStructure(triggerHandle.AddrOfPinnedObject(), typeof(Trigger));

             Value value = new Value();
             value.Trigger = trigger;

             triggerHandle.Free();

             //Read all the sensor labels
             List<string> sensorLableList = new List<string>();
             for (int sensorIndex = 0; sensorIndex < DataLengths.MAX_EVENT_SENSORS; sensorIndex++)
             {
                   int sensorLableDataSize = DataLengths.MAX_LABEL_LENGTH;
                   sensorLabelHandle = GCHandle.Alloc(binaryReader.ReadBytes(sensorLableDataSize), GCHandleType.Pinned);
                  string label = Marshal.PtrToStringAnsi(sensorLabelHandle.AddrOfPinnedObject(), sensorLableDataSize).Trim();
                   sensorLableList.Add(label);

                   sensorLabelHandle.Free();
             }
             value.SensorLabels = sensorLableList.ToArray();
             //Read all the recorded sensor data
             List<Point> pointList = new List<Point>();
             for (int pointIndex = 0; pointIndex < DataLengths.MAX_EVENT_SENSORS; pointIndex++)
             {
                  int pointDataSize = Marshal.SizeOf(typeof(Point));

                  pointHandle = GCHandle.Alloc(binaryReader.ReadBytes(pointDataSize), GCHandleType.Pinned);
                  Point point = (Point)Marshal.PtrToStructure(pointHandle.AddrOfPinnedObject(), typeof(Point));
                  pointList.Add(point);

                  pointHandle.Free();
             }

             value.Point = pointList.ToArray();

             eventData.Add(@event, value);
             eventHandle.Free();
        }
        finally
        {
             //Free all the resources used to get the data
             if (memoryStream != null) { memoryStream.Close(); }
             if (binaryReader != null) { binaryReader.Close(); }
             if (eventHandle.IsAllocated) { eventHandle.Free(); }
             if (triggerHandle.IsAllocated) { triggerHandle.Free(); }
             if (sensorLabelHandle.IsAllocated) { sensorLabelHandle.Free(); }
             if (pointHandle.IsAllocated) { pointHandle.Free(); }

             GC.Collect();
         }
    }

}
finally
{
     if (dbf != null)
     {
        dbf.close();
     }
}                

return eventData;
}

Ответы [ 2 ]

4 голосов
/ 26 марта 2011

Интересный режим отказа, это не должно произойти.Как правило, вы получаете FEEE, потому что куча мусора уничтожается.Обычно это легко объясняется некорректно работающей встроенной функцией, которая выполняет что-то вроде переполнения буфера.Однако здесь не нужно вводить пинвок.

Тем не менее, есть отличная кандидатура на этот случай:

[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1)]
public string Control;

Маршалинг для этого предназначен для маршалинга строки C, массива символов с нулем в конце.SizeConst = 1 не может работать должным образом по дизайну, который оставляет место для нулевого терминатора, а не для любого символа.Вдобавок ко всему, файл .dbf не содержит строки с нулевым символом в конце, они имеют фиксированную ширину в соответствии с объявлением поля.

Если нарушение прав доступа действительно вызвано тем, что маршаллер получил эту ошибку, является открытымвопрос, однако, он может так же легко бомбить при попытке найти нулевой терминатор.Не найдите и промахнитесь на не нанесенной на карту странице памяти.

Во всяком случае, вы делаете это неправильно.Вы должны использовать char [] вместо строки в объявлении структуры и использовать ByValArray в атрибуте [MarshalAs].Между прочим, довольно болезненное кодирование.

0 голосов
/ 26 марта 2011

В вашей структуре триггера вы уверены, что у вас есть правильная упаковка? Похоже, что член Index будет по смещению 4, где, как вы могли бы предполагать, это будет по смещению 1? То же самое с до и после.

Кроме того, какого размера должен быть элемент управления? Если это строка, обычно неуправляемые строки заканчиваются нулем. Указав длину 1, это указало бы мне на один символ. Если это так, почему бы не использовать для него символ вместо строки?

Ошибка 0xc0000005 - нарушение прав доступа.

Вы пытались запустить под отладчиком и включить отладку неуправляемого кода? Затем вы можете настроить нарушение доступа, чтобы оно было зафиксировано при его выдаче, как это может происходить внутри кода .NET Framework. Затем вы могли бы взглянуть на память и понять, что она может делать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...