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); }
}
};
我会让你写下剩下的帮手.
