TDataTypeParam = packed record dtType : integer; case integer of cInt :(dtInt : integer); cFloat :(dtFloat : real); cLongInt :(dtLongInt : Int64); cDateTime:(dtDateTime : TDateTime); cShortStr:(dtShortString : ShortString); end; TDataParam = packed record NumberParam : integer; Param : array [1..MaxParam] of TDataTypeParam; end; TEvData = packed record dm : TDateTime; CodeEV : integer; IDCAM : integer; Reserv1 : integer; Data : TDataParam; end; TArrSrvData = packed record NumberPack : integer; Address : Cardinal; tpCL : integer; tpEv : integer; Reserv : integer; Packs : array [1..MaxPacks] of TEvData; end;
这段代码抛出System.TypeLoadException:
//TDataTypeParam = packed record //dtType : integer;//data type // case integer of // cInt :(dtInt : integer); // cFloat :(dtFloat : real); // cLongInt :(dtLongInt : Int64); // cDateTime:(dtDateTime : TDateTime); // cShortStr:(dtShortString : ShortString); //end; [StructLayout(LayoutKind.Explicit)] [Serializable] internal struct DataTypeParam { [FieldOffset(0)] public DataType dtType; [FieldOffset(4)] public int dtInt; [FieldOffset(4)] public double dtFloat; [FieldOffset(4)] public long dtLongInt; [FieldOffset(4)] public double dtDateTime; [FieldOffset(4)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public byte[] dtShortString; }; //TDataParam = packed record // NumberParam : integer; // Param : array [1..MaxParam] of TDataTypeParam; // end; [StructLayout(LayoutKind.Sequential, Pack = 1)] [Serializable] internal struct DataParam { public int NumberParam; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = Consts.MaxParam)] public DataTypeParam[] Param; }; // TEvData = packed record // dm : TDateTime; // CodeEV : integer; // IDCAM : integer; // Reserv1 : integer; // Data : TDataParam; // end; [StructLayout(LayoutKind.Sequential, Pack = 1)] [Serializable] internal struct EvData { public DateTime dm; public int CodeEV; public int IDCAM; public int Reserv1; public DataParam Data; } // TArrSrvData = packed record // NumberPack : integer; // Address : Cardinal; // tpCL : integer; // tpEv : integer; // Reserv : integer; // Packs : array [1..MaxPacks] of TEvData; // end; // PArrSrvData = ^TArrSrvData; [StructLayout(LayoutKind.Sequential, Pack = 1)] [Serializable] internal struct ArrSrvData { public int NumberPack; public uint Address; public int tpCL; public int tpEv; public int Reserv; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = Consts.MaxPacks)] public EvData[] Param; }
这个问题出在cShortStr:(dtShortString:ShortString);转换…
这是实际代码:
void mySink_NewEvent(ref object Comm) { byte[] arr = Comm as byte[]; if(arr == null) return; var data = (ArrSrvData)MarshalSerializer.RawDeserialize(arr, typeof(ArrSrvData)); }
和RawDeserialize代码:
public static object RawDeserialize(byte[] rawData, Type type) { if (rawData == null) throw new ArgumentNullException(MethodBase.GetCurrentMethod().GetParameters()[0].Name); int rawsize = Marshal.SizeOf(type); if (rawsize > rawData.Length) return null; object retobj; GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned); try { IntPtr buffer = handle.AddrOfPinnedObject(); retobj = Marshal.PtrToStructure(buffer, type); } finally { handle.Free(); } return retobj; }你有几个问题.
首先,C#DateTime不映射到Delphi TDateTime.您需要在C#代码中使用double,并编写从C#日期/时间到Delphi日期/时间的映射.
第二个问题,更严重的是,确实是字符串.在Delphi中,ShortString是256字节宽.第一个字节包含字符串长度,其余255个是有效负载.
您不能在C#联合中使用非引用类型覆盖引用类型.这是你的问题.除了作为引用类型的字符串之外,所有重叠变量都是值类型. Hans Passant在这里讨论了这个问题:C# Platform-invoke, c-style union with reference and value types.请注意,他明确地提到了您遇到的异常.
在MSDN,您可以找到相同的信息:
In managed code, value types and reference types are not permitted to overlap.
对此问题的正常响应是停止混合引用和值类型.最好只使用blittable值类型.但我没有看到一个明显的方法来做到这一点.您可以使用固定的字节数组,但这会强制您使用不安全的处理固定数组并不是很有趣.可以在此处找到该方法的示例:Marshaling structure with reference-type and value-type members inside a union.
因此,我建议您手动编组记录的变体部分.使用UnmanagedType.ByValArray将其声明为byte [].
[StructLayout(LayoutKind.Sequential)] [Serializable] internal struct DataTypeParam { public DataType dtType; [MarshalAs(UnmanagedType.ByValArray, SizeConst=256)] public byte[] dtUnion; };
然后你需要编写助手来从/向字节数组读/写单个字段.有了这些助手,你的结构可能看起来像这样:
[StructLayout(LayoutKind.Sequential)] [Serializable] internal struct DataTypeParam { public int dtType; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] private byte[] dtUnion; public int cInt { get { return BitConverter.ToInt32(dtUnion, 0); } set { dtUnion = BitConverter.GetBytes(value); } } public double cFloat { get { return BitConverter.ToDouble(dtUnion, 0); } set { dtUnion = BitConverter.GetBytes(value); } } };
我会让你写下剩下的帮手.