2

我在尝试从多个 DBase IV 文件中的 memofields 中将 c++ 结构读取到 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;
}
4

2 回答 2

4

有趣的故障模式,这不应该发生。您获得 FEEE 通常是因为垃圾收集堆正在被破坏。这通常很容易通过行为不当的 pinvoked 本机函数来解释,该函数执行类似于溢出缓冲区的操作。不过这里没有pinvoke。

然而,这个事故有一个很好的候选人:

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

对此的编组旨在编组 C 字符串,即以零结尾的字符数组。SizeConst = 1 不能按设计正常工作,它只会为零终止符留出空间,而不是任何字符。最重要的是,.dbf 文件不包含以零结尾的字符串,根据字段声明,它们是固定宽度的。

访问冲突是否实际上是由编组器错误引起的,这是一个悬而未决的问题,它可以很容易地尝试找到零终止符。找不到一个并误入未映射的内存页面。

Anyhoo,你做错了。您必须在结构声明中使用 char[] 而不是字符串,并在 [MarshalAs] 属性中使用 ByValArray。顺便说一句,编码非常痛苦。

于 2011-03-25T21:48:15.253 回答
0

在您的触发器结构中,您确定您有正确的包装吗?看起来索引成员将位于偏移量 4,而您可能希望它位于偏移量 1?与 Pre 和 Post 相同。

另外, Control 应该是什么大小?如果是字符串,通常非托管字符串以空值结尾。通过指定长度 1,这将向我指示一个字符。如果是这样,为什么不使用 char 而不是 string 呢?

错误 0xc0000005 是访问冲突。

您是否尝试过在调试器下运行并启用非托管代码调试?然后,您可以将访问冲突设置为在抛出时正确捕获,因为它可能发生在 .NET 框架代码中。然后,您可以查看内存并了解它可能在做什么。

于 2011-03-25T21:11:27.200 回答