e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
690 lines
22 KiB
C#
690 lines
22 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
/*============================================================
|
|
**
|
|
** Class: Variant
|
|
**
|
|
**
|
|
** 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
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
internal extern double GetR8FromVar();
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
internal extern float GetR4FromVar();
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
internal extern void SetFieldsR4(float val);
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
internal extern void SetFieldsR8(double val);
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[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
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[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"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|