【问题标题】:Convert Delphi variant record to C#将 Delphi 变体记录转换为 C#
【发布时间】:2014-01-24 20:16:05
【问题描述】:

如何将此代码从 Delphi 转换为 C#?我需要 struct 来与无人管理的代码进行交互。

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;
    }

【问题讨论】:

  • "This code is throw System.TypeLoadException" Buy 你没有显示任何实际的代码,只有结构定义。显示异常发生的位置。
  • @Jonathon Reinhart,Marshal.PtrToStructure 方法中抛出异常。
  • @Ken Variant 记录与联合相同。但是使用FieldOffset 可以解决这个问题。 AFIR,real 长期以来一直是double 的别名(我怀疑是Delphi 2)。还有SizeOf(ShortString)=256

标签: c# delphi pinvoke marshalling


【解决方案1】:

您有几个问题。

首先,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,您可以找到相同的信息:

在托管代码中,值类型和引用类型不允许重叠。

对此问题的正常反应是停止混合引用和值类型。最好只能使用 blittable 值类型。但我没有看到一个明显的方法让你做到这一点。您可以使用固定字节数组,但这会迫使您使用unsafe,并且处理固定数组并不是很有趣。可以在此处找到该方法的示例: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); }
    }
};

剩下的助手由你来写。

【讨论】:

  • 感谢您的提问。我总是喜欢这些。 P/Invoke 和 Delphi。啊啊啊。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
相关资源
最近更新 更多