// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*============================================================ ** ** ** ** Purpose: The CLR implementation of Variant. ** ** ===========================================================*/ namespace System { using System; using System.Reflection; using System.Threading; using System.Runtime.InteropServices; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Diagnostics.Contracts; [Serializable] [StructLayout(LayoutKind.Sequential)] internal struct Variant { //Do Not change the order of these fields. //They are mapped to the native VariantData * data structure. private Object m_objref; private int m_data1; private int m_data2; private int m_flags; // The following bits have been taken up as follows // bits 0-15 - Type code // bit 16 - Array // bits 19-23 - Enums // bits 24-31 - Optional VT code (for roundtrip VT preservation) //What are the consequences of making this an enum? /////////////////////////////////////////////////////////////////////// // If you update this, update the corresponding stuff in OAVariantLib.cs, // COMOAVariant.cpp (2 tables, forwards and reverse), and perhaps OleVariant.h /////////////////////////////////////////////////////////////////////// internal const int CV_EMPTY=0x0; internal const int CV_VOID=0x1; internal const int CV_BOOLEAN=0x2; internal const int CV_CHAR=0x3; internal const int CV_I1=0x4; internal const int CV_U1=0x5; internal const int CV_I2=0x6; internal const int CV_U2=0x7; internal const int CV_I4=0x8; internal const int CV_U4=0x9; internal const int CV_I8=0xa; internal const int CV_U8=0xb; internal const int CV_R4=0xc; internal const int CV_R8=0xd; internal const int CV_STRING=0xe; internal const int CV_PTR=0xf; internal const int CV_DATETIME = 0x10; internal const int CV_TIMESPAN = 0x11; internal const int CV_OBJECT=0x12; internal const int CV_DECIMAL = 0x13; internal const int CV_ENUM=0x15; internal const int CV_MISSING=0x16; internal const int CV_NULL=0x17; internal const int CV_LAST=0x18; internal const int TypeCodeBitMask=0xffff; internal const int VTBitMask=unchecked((int)0xff000000); internal const int VTBitShift=24; internal const int ArrayBitMask =0x10000; // Enum enum and Mask internal const int EnumI1 =0x100000; internal const int EnumU1 =0x200000; internal const int EnumI2 =0x300000; internal const int EnumU2 =0x400000; internal const int EnumI4 =0x500000; internal const int EnumU4 =0x600000; internal const int EnumI8 =0x700000; internal const int EnumU8 =0x800000; internal const int EnumMask =0xF00000; internal static readonly Type [] ClassTypes = { typeof(System.Empty), typeof(void), typeof(Boolean), typeof(Char), typeof(SByte), typeof(Byte), typeof(Int16), typeof(UInt16), typeof(Int32), typeof(UInt32), typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(String), typeof(void), // ptr for the moment typeof(DateTime), typeof(TimeSpan), typeof(Object), typeof(Decimal), typeof(Object), // Treat enum as Object typeof(System.Reflection.Missing), typeof(System.DBNull), }; internal static readonly Variant Empty = new Variant(); internal static readonly Variant Missing = new Variant(Variant.CV_MISSING, Type.Missing, 0, 0); internal static readonly Variant DBNull = new Variant(Variant.CV_NULL, System.DBNull.Value, 0, 0); // // Native Methods // [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern double GetR8FromVar(); [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern float GetR4FromVar(); [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern void SetFieldsR4(float val); [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern void SetFieldsR8(double val); [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] internal extern void SetFieldsObject(Object val); // Use this function instead of an ECALL - saves about 150 clock cycles // by avoiding the ecall transition and because the JIT inlines this. // Ends up only taking about 1/8 the time of the ECALL version. internal long GetI8FromVar() { return ((long)m_data2<<32 | ((long)m_data1 & 0xFFFFFFFFL)); } // // Constructors // internal Variant(int flags, Object or, int data1, int data2) { m_flags = flags; m_objref=or; m_data1=data1; m_data2=data2; } public Variant(bool val) { m_objref= null; m_flags = CV_BOOLEAN; m_data1 = (val)?Boolean.True:Boolean.False; m_data2 = 0; } public Variant(sbyte val) { m_objref=null; m_flags=CV_I1; m_data1=(int)val; m_data2=(int)(((long)val)>>32); } public Variant(byte val) { m_objref=null; m_flags=CV_U1; m_data1=(int)val; m_data2=0; } public Variant(short val) { m_objref=null; m_flags=CV_I2; m_data1=(int)val; m_data2=(int)(((long)val)>>32); } public Variant(ushort val) { m_objref=null; m_flags=CV_U2; m_data1=(int)val; m_data2=0; } public Variant(char val) { m_objref=null; m_flags=CV_CHAR; m_data1=(int)val; m_data2=0; } public Variant(int val) { m_objref=null; m_flags=CV_I4; m_data1=val; m_data2=val >> 31; } public Variant(uint val) { m_objref=null; m_flags=CV_U4; m_data1=(int)val; m_data2=0; } public Variant(long val) { m_objref=null; m_flags=CV_I8; m_data1 = (int)val; m_data2 = (int)(val >> 32); } public Variant(ulong val) { m_objref=null; m_flags=CV_U8; m_data1 = (int)val; m_data2 = (int)(val >> 32); } [System.Security.SecuritySafeCritical] // auto-generated public Variant(float val) { m_objref=null; m_flags=CV_R4; m_data1=0; m_data2=0; SetFieldsR4(val); } [System.Security.SecurityCritical] // auto-generated public Variant(double val) { m_objref=null; m_flags=CV_R8; m_data1=0; m_data2=0; SetFieldsR8(val); } public Variant(DateTime val) { m_objref=null; m_flags=CV_DATETIME; ulong ticks = (ulong)val.Ticks; m_data1 = (int)ticks; m_data2 = (int)(ticks>>32); } public Variant(Decimal val) { m_objref = (Object)val; m_flags = CV_DECIMAL; m_data1=0; m_data2=0; } [System.Security.SecuritySafeCritical] // auto-generated public Variant(Object obj) { m_data1=0; m_data2=0; VarEnum vt = VarEnum.VT_EMPTY; if (obj is DateTime) { m_objref=null; m_flags=CV_DATETIME; ulong ticks = (ulong)((DateTime)obj).Ticks; m_data1 = (int)ticks; m_data2 = (int)(ticks>>32); return; } if (obj is String) { m_flags=CV_STRING; m_objref=obj; return; } if (obj == null) { this = Empty; return; } if (obj == System.DBNull.Value) { this = DBNull; return; } if (obj == Type.Missing) { this = Missing; return; } if (obj is Array) { m_flags=CV_OBJECT | ArrayBitMask; m_objref=obj; return; } // Compiler appeasement m_flags = CV_EMPTY; m_objref = null; // Check to see if the object passed in is a wrapper object. if (obj is UnknownWrapper) { vt = VarEnum.VT_UNKNOWN; obj = ((UnknownWrapper)obj).WrappedObject; } else if (obj is DispatchWrapper) { vt = VarEnum.VT_DISPATCH; obj = ((DispatchWrapper)obj).WrappedObject; } else if (obj is ErrorWrapper) { vt = VarEnum.VT_ERROR; obj = (Object)(((ErrorWrapper)obj).ErrorCode); Contract.Assert(obj != null, "obj != null"); } else if (obj is CurrencyWrapper) { vt = VarEnum.VT_CY; obj = (Object)(((CurrencyWrapper)obj).WrappedObject); Contract.Assert(obj != null, "obj != null"); } else if (obj is BStrWrapper) { vt = VarEnum.VT_BSTR; obj = (Object)(((BStrWrapper)obj).WrappedObject); } if (obj != null) { SetFieldsObject(obj); } // If the object passed in is one of the wrappers then set the VARIANT type. if (vt != VarEnum.VT_EMPTY) m_flags |= ((int)vt << VTBitShift); } [System.Security.SecurityCritical] // auto-generated unsafe public Variant(void* voidPointer,Type pointerType) { if (pointerType == null) throw new ArgumentNullException("pointerType"); if (!pointerType.IsPointer) throw new ArgumentException(Environment.GetResourceString("Arg_MustBePointer"),"pointerType"); Contract.EndContractBlock(); m_objref = pointerType; m_flags=CV_PTR; m_data1=(int)voidPointer; m_data2=0; } //This is a family-only accessor for the CVType. //This is never to be exposed externally. internal int CVType { get { return (m_flags&TypeCodeBitMask); } } [System.Security.SecuritySafeCritical] // auto-generated public Object ToObject() { switch (CVType) { case CV_EMPTY: return null; case CV_BOOLEAN: return (Object)(m_data1!=0); case CV_I1: return (Object)((sbyte)m_data1); case CV_U1: return (Object)((byte)m_data1); case CV_CHAR: return (Object)((char)m_data1); case CV_I2: return (Object)((short)m_data1); case CV_U2: return (Object)((ushort)m_data1); case CV_I4: return (Object)(m_data1); case CV_U4: return (Object)((uint)m_data1); case CV_I8: return (Object)(GetI8FromVar()); case CV_U8: return (Object)((ulong)GetI8FromVar()); case CV_R4: return (Object)(GetR4FromVar()); case CV_R8: return (Object)(GetR8FromVar()); case CV_DATETIME: return new DateTime(GetI8FromVar()); case CV_TIMESPAN: return new TimeSpan(GetI8FromVar()); case CV_ENUM: return BoxEnum(); case CV_MISSING: return Type.Missing; case CV_NULL: return System.DBNull.Value; case CV_DECIMAL: case CV_STRING: case CV_OBJECT: default: return m_objref; } } // This routine will return an boxed enum. [System.Security.SecurityCritical] // auto-generated [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern Object BoxEnum(); // Helper code for marshaling managed objects to VARIANT's (we use // managed variants as an intermediate type. [System.Security.SecuritySafeCritical] // auto-generated internal static void MarshalHelperConvertObjectToVariant(Object o, ref Variant v) { #if FEATURE_REMOTING IConvertible ic = System.Runtime.Remoting.RemotingServices.IsTransparentProxy(o) ? null : o as IConvertible; #else IConvertible ic = o as IConvertible; #endif if (o == null) { v = Empty; } else if (ic == null) { // This path should eventually go away. But until // the work is done to have all of our wrapper types implement // IConvertible, this is a cheapo way to get the work done. v = new Variant(o); } else { IFormatProvider provider = CultureInfo.InvariantCulture; switch (ic.GetTypeCode()) { case TypeCode.Empty: v = Empty; break; case TypeCode.Object: v = new Variant((Object)o); break; case TypeCode.DBNull: v = DBNull; break; case TypeCode.Boolean: v = new Variant(ic.ToBoolean(provider)); break; case TypeCode.Char: v = new Variant(ic.ToChar(provider)); break; case TypeCode.SByte: v = new Variant(ic.ToSByte(provider)); break; case TypeCode.Byte: v = new Variant(ic.ToByte(provider)); break; case TypeCode.Int16: v = new Variant(ic.ToInt16(provider)); break; case TypeCode.UInt16: v = new Variant(ic.ToUInt16(provider)); break; case TypeCode.Int32: v = new Variant(ic.ToInt32(provider)); break; case TypeCode.UInt32: v = new Variant(ic.ToUInt32(provider)); break; case TypeCode.Int64: v = new Variant(ic.ToInt64(provider)); break; case TypeCode.UInt64: v = new Variant(ic.ToUInt64(provider)); break; case TypeCode.Single: v = new Variant(ic.ToSingle(provider)); break; case TypeCode.Double: v = new Variant(ic.ToDouble(provider)); break; case TypeCode.Decimal: v = new Variant(ic.ToDecimal(provider)); break; case TypeCode.DateTime: v = new Variant(ic.ToDateTime(provider)); break; case TypeCode.String: v = new Variant(ic.ToString(provider)); break; default: throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnknownTypeCode", ic.GetTypeCode())); } } } // Helper code for marshaling VARIANTS to managed objects (we use // managed variants as an intermediate type. internal static Object MarshalHelperConvertVariantToObject(ref Variant v) { return v.ToObject(); } // Helper code: on the back propagation path where a VT_BYREF VARIANT* // is marshaled to a "ref Object", we use this helper to force the // updated object back to the original type. [System.Security.SecurityCritical] // auto-generated internal static void MarshalHelperCastVariant(Object pValue, int vt, ref Variant v) { IConvertible iv = pValue as IConvertible; if (iv == null) { switch (vt) { case 9: /*VT_DISPATCH*/ v = new Variant(new DispatchWrapper(pValue)); break; case 12: /*VT_VARIANT*/ v = new Variant(pValue); break; case 13: /*VT_UNKNOWN*/ v = new Variant(new UnknownWrapper(pValue)); break; case 36: /*VT_RECORD*/ v = new Variant(pValue); break; case 8: /*VT_BSTR*/ if (pValue == null) { v = new Variant(null); v.m_flags = CV_STRING; } else { throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant")); } break; default: throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant")); } } else { IFormatProvider provider = CultureInfo.InvariantCulture; switch (vt) { case 0: /*VT_EMPTY*/ v = Empty; break; case 1: /*VT_NULL*/ v = DBNull; break; case 2: /*VT_I2*/ v = new Variant(iv.ToInt16(provider)); break; case 3: /*VT_I4*/ v = new Variant(iv.ToInt32(provider)); break; case 4: /*VT_R4*/ v = new Variant(iv.ToSingle(provider)); break; case 5: /*VT_R8*/ v = new Variant(iv.ToDouble(provider)); break; case 6: /*VT_CY*/ v = new Variant(new CurrencyWrapper(iv.ToDecimal(provider))); break; case 7: /*VT_DATE*/ v = new Variant(iv.ToDateTime(provider)); break; case 8: /*VT_BSTR*/ v = new Variant(iv.ToString(provider)); break; case 9: /*VT_DISPATCH*/ v = new Variant(new DispatchWrapper((Object)iv)); break; case 10: /*VT_ERROR*/ v = new Variant(new ErrorWrapper(iv.ToInt32(provider))); break; case 11: /*VT_BOOL*/ v = new Variant(iv.ToBoolean(provider)); break; case 12: /*VT_VARIANT*/ v = new Variant((Object)iv); break; case 13: /*VT_UNKNOWN*/ v = new Variant(new UnknownWrapper((Object)iv)); break; case 14: /*VT_DECIMAL*/ v = new Variant(iv.ToDecimal(provider)); break; // case 15: /*unused*/ // NOT SUPPORTED case 16: /*VT_I1*/ v = new Variant(iv.ToSByte(provider)); break; case 17: /*VT_UI1*/ v = new Variant(iv.ToByte(provider)); break; case 18: /*VT_UI2*/ v = new Variant(iv.ToUInt16(provider)); break; case 19: /*VT_UI4*/ v = new Variant(iv.ToUInt32(provider)); break; case 20: /*VT_I8*/ v = new Variant(iv.ToInt64(provider)); break; case 21: /*VT_UI8*/ v = new Variant(iv.ToUInt64(provider)); break; case 22: /*VT_INT*/ v = new Variant(iv.ToInt32(provider)); break; case 23: /*VT_UINT*/ v = new Variant(iv.ToUInt32(provider)); break; default: throw new InvalidCastException(Environment.GetResourceString("InvalidCast_CannotCoerceByRefVariant")); } } } } }