NorSD NorSD
NorSD NorSD

Reputation: 233

The result of Marshaling and Un-marshaling DateTime.MinValue is weird in c#

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

Answers (1)

Hans Passant
Hans Passant

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

Related Questions