Reputation: 233
Here are the codes:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public class STTEST
{
public DateTime DateTime;
}
static void Main(string[] args)
{
var st0 = new STTEST();
var bs0 = st0.ToBytes();
var st1 = bs0.ToObject<STTEST>();
}
Helper codes:
public static byte[] ToBytes(this object obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
IntPtr structPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, structPtr, false);
Marshal.Copy(structPtr, bytes, 0, size);
Marshal.FreeHGlobal(structPtr);
return bytes;
}
public static T ToObject<T>(this byte[] bytes)
{
int size = Marshal.SizeOf(typeof(T));
IntPtr structPtr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, structPtr, size);
object obj = Marshal.PtrToStructure(structPtr, typeof(T));
Marshal.FreeHGlobal(structPtr);
return (T)obj;
}
The marshaling operation is right, and the content of bytes is zero. But un-marshaling operation return the st1.DateTime is "1899/12/30". It is supposed to be "0001/01/01"! What's up? :-(
Upvotes: 0
Views: 944
Reputation: 942109
Marshal.PtrToStructure() is intended to convert the content of a structure to a representation that native code can understand. There is no native code that has any idea whatsoever what a DateTime might look like. There are multiple "standards", native code tends to use a Unix timestamp (seconds since 1/1/1970) or a FILETIME (100 nsec since 1/1/1600) or a COM automation DATE (floating point days since 12/30/1899).
Being forced to choose between these incompatible standards, the CLR designers went for the most common representation in interop code, the COM standard. Also exposed as DateTime.To/FromOADate(). Since you left the DateTime uninitialized, it is forced to convert to the lowest legal value for a DATE, a floating point value of 0. Converting back thus produces 12/30/1899.
Using the pinvoke marshaller to serialize objects to bytes is an iffy proposition with non-zero odds for unexpected outcomes like this one. You'd get ahead somewhat by not leaving the DateTime uninitialized perhaps.
Upvotes: 4