You have a couple of issues.
First of all, C# DateTime
does not map to Delphi TDateTime
. You would need to use double
in the C# code and code up a mapping from C# date/time to Delphi date/time.
The second problem, and much more serious, is indeed the string. In Delphi, ShortString
is 256 bytes wide. The first byte contains the string length, the remaining 255 are the payload.
You cannot overlay a reference type with a non-reference type in a C# union. And this is your problem. All your overlaid variables are value types, apart from the string which is a reference type. Hans Passant discusses the issue here: C# Platform-invoke, c-style union with reference and value types. Note that he explicitly calls out the exception that you have encountered.
Over at MSDN, you can find the same information:
In managed code, value types and reference types are not permitted to overlap.
The normal response to this problem is to stop mixing reference and value types. It's best if you can use only blittable value types. But I don't see an obvious way for you to do that. You could use a fixed byte array, but that would force you to use unsafe
and handling fixed arrays is not much fun. An example of that approach can be found here: Marshaling structure with reference-type and value-type members inside a union.
So, I recommend that you marshal the variant part of the record by hand. Declare it as byte[]
with UnmanagedType.ByValArray
.
[StructLayout(LayoutKind.Sequential)]
[Serializable]
internal struct DataTypeParam
{
public DataType dtType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=256)]
public byte[] dtUnion;
};
And then you need to write helpers to read/write individual fields from/to the byte array. With those helpers your struct might look at little like this:
[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); }
}
};
I'll leave you to write the rest of the helpers.