问题
I want to read a native struct into a C# type using Marshalling. My method to Marshal structs is like so:
T ReadObject<T>(BinaryReader br) {
var bytes = br.ReadBytes(Marshal.SizeOf(typeof(T)));
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try {
return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
}
finally {
handle.Free();
}
}
Now this works fine in general, the problem arises with the following type:
[StructLayout(LayoutKind.Sequential, Pack=1)]
class SubData {
public short A1;
public short A2;
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
class Data {
public short Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
public SubData[] SubDatas;
}
Note that this works fine if SubData is a struct! But if SubData is a class, it causes Marshal.PtrToStructure to throw a FatalExecutionEngineError. I'd like to stick with classes because sometimes my types have default values and structs cannot have field initializers nor default constructors, and also some of these types are rather large.
Thanks for the help.
Edit: the error message is "The runtime has encountered a fatal error. The address of the error was at 0x6af99aec, on thread 0x348. 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."
回答1:
A class is a reference type, thus when using Marshal.PtrToStructure it will copy a pointer and no values at the position of Subdata.
When declaring Subdata as struct, then the actual values of subdata will be copied.
Thus when doing the Marshalling, you must use a struct. You might still have a class as well, which will take the struct version in a constructor.
You can for instance prove this by using
sizeof
to see that the sizes will differ.
来源:https://stackoverflow.com/questions/9883308/marshalling-array-of-structs-vs-classes