C# 编组期间出现 FatalExecutionEngineError
当尝试将许多 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;
}
I'm running into an issue when trying to read c++ structures from memofields in a number of DBase IV files into C# (.Net 4) and then insert them into MSSQL 2008. The data is being extracted ok from the DBase files but I seemed to be doing something wrong with managing the unsafe calls because I randomly get the following error:
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.
The following is the code I use to read the data. This code completes successfully and the data I get from the files are correct. The error occurs a number of minutes after this function is called, when the objects are being inserted into the database using nhibernate (I left it out because I didn't think it really matter to the issue).
[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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有趣的故障模式,这是不应该发生的。您通常会收到费用,因为垃圾收集堆正在被破坏。这通常很容易通过行为不当的 pinvoked 本机函数来解释,该函数会执行诸如溢出缓冲区之类的操作。不过这里没有pinvoke。
然而,有一个很好的候选者可以解决这个问题:
此问题的编组旨在编组 C 字符串,即以零结尾的字符数组。根据设计,SizeConst = 1 无法正常工作,它只为零终止符留出空间,而不为任何字符留出空间。最重要的是,.dbf 文件不包含以零结尾的字符串,它们根据字段声明是固定宽度的。
不过,访问冲突是否实际上是由编组器犯这个错误引起的,这是一个悬而未决的问题,但它在尝试找到零终止符时也很容易失败。找不到一个并错误地进入未映射的内存页。
无论如何,你做错了。您必须在结构声明中使用 char[] 而不是字符串,并在 [MarshalAs] 属性中使用 ByValArray。顺便说一句,编码非常痛苦。
Interesting failure mode, this is not supposed to happen. You get the FEEE typically because the garbage collected heap is getting destroyed. This is normally easily explained by a misbehaving pinvoked native function that does something like overflow a buffer. No pinvoke here though.
There's however an excellent candidate for this mishap:
The marshaling for this is designed to marshal a C string, a zero-terminated array of characters. A SizeConst = 1 cannot work properly by design, that only leaves room for the zero terminator, not any character. On top of which, a .dbf file doesn't contain zero terminated strings, they are fixed-width according to the field declaration.
Whether the access violation is actually caused by the marshaller getting this wrong is an open question though, it can just as easily bomb on trying to find the zero terminator. Not find one and blunder into an unmapped memory page.
Anyhoo, you're doing it wrong. You must use a char[] instead of a string in the structure declaration and use ByValArray in the [MarshalAs] attribute. Pretty painful coding btw.
在您的触发器结构中,您确定包装正确吗?看起来 Index 成员将位于偏移量 4 处,而您可能希望它位于偏移量 1 处?与前和后相同。
另外,Control 应该有多大?如果是字符串,通常非托管字符串以 null 结尾。通过指定长度 1,这对我来说表示一个字符。如果是这样,为什么不使用 char 而不是 string 呢?
错误 0xc0000005 是访问冲突。
您是否尝试过在调试器下运行并启用非托管代码调试?然后,您可以将访问冲突设置为在抛出时立即捕获,因为它可能发生在 .NET 框架代码内部。然后你可以查看内存并了解它可能在做什么。
Within your Trigger structure, are you sure you have the right packing? It looks like the Index member would be at offset 4 where as you might have intended it to be at offset 1? Same with Pre and Post.
Also, what size is Control supposed to be? If it is a string, usually unmanaged strings are null-terminated. By specifying the length of 1, that would indicate to me a single character. If that is the case, why not use char for it instead of string?
Error 0xc0000005 is an access violation.
Have you tried running under the debugger and enabling unmanaged code debugging? You can then set the access violation to be caught right when thrown as it might be happening inside .NET framework code. You then could look at memory and get an idea of what it might be doing.