diff options
Diffstat (limited to 'src/mscorlib/src/System/Runtime/InteropServices/Variant.cs')
-rw-r--r-- | src/mscorlib/src/System/Runtime/InteropServices/Variant.cs | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs b/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs new file mode 100644 index 0000000000..fbd1b3c15d --- /dev/null +++ b/src/mscorlib/src/System/Runtime/InteropServices/Variant.cs @@ -0,0 +1,658 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices { + using System.Diagnostics; + + /// <summary> + /// Variant is the basic COM type for late-binding. It can contain any other COM data type. + /// This type definition precisely matches the unmanaged data layout so that the struct can be passed + /// to and from COM calls. + /// </summary> + [StructLayout(LayoutKind.Explicit)] + [System.Security.SecurityCritical] + internal struct Variant { + +#if DEBUG + static Variant() { + // Variant size is the size of 4 pointers (16 bytes) on a 32-bit processor, + // and 3 pointers (24 bytes) on a 64-bit processor. + int variantSize = Marshal.SizeOf(typeof(Variant)); + if (IntPtr.Size == 4) { + BCLDebug.Assert(variantSize == (4 * IntPtr.Size), "variant"); + } else { + BCLDebug.Assert(IntPtr.Size == 8, "variant"); + BCLDebug.Assert(variantSize == (3 * IntPtr.Size), "variant"); + } + } +#endif + + // Most of the data types in the Variant are carried in _typeUnion + [FieldOffset(0)] private TypeUnion _typeUnion; + + // Decimal is the largest data type and it needs to use the space that is normally unused in TypeUnion._wReserved1, etc. + // Hence, it is declared to completely overlap with TypeUnion. A Decimal does not use the first two bytes, and so + // TypeUnion._vt can still be used to encode the type. + [FieldOffset(0)] private Decimal _decimal; + + [StructLayout(LayoutKind.Sequential)] + private struct TypeUnion { + internal ushort _vt; + internal ushort _wReserved1; + internal ushort _wReserved2; + internal ushort _wReserved3; + + internal UnionTypes _unionTypes; + } + + [StructLayout(LayoutKind.Sequential)] + private struct Record { + private IntPtr _record; + private IntPtr _recordInfo; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + [StructLayout(LayoutKind.Explicit)] + private struct UnionTypes { + #region Generated Variant union types + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_UnionTypes from: generate_comdispatch.py + + [FieldOffset(0)] internal SByte _i1; + [FieldOffset(0)] internal Int16 _i2; + [FieldOffset(0)] internal Int32 _i4; + [FieldOffset(0)] internal Int64 _i8; + [FieldOffset(0)] internal Byte _ui1; + [FieldOffset(0)] internal UInt16 _ui2; + [FieldOffset(0)] internal UInt32 _ui4; + [FieldOffset(0)] internal UInt64 _ui8; + [FieldOffset(0)] internal Int32 _int; + [FieldOffset(0)] internal UInt32 _uint; + [FieldOffset(0)] internal Int16 _bool; + [FieldOffset(0)] internal Int32 _error; + [FieldOffset(0)] internal Single _r4; + [FieldOffset(0)] internal Double _r8; + [FieldOffset(0)] internal Int64 _cy; + [FieldOffset(0)] internal double _date; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _bstr; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _unknown; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] + [FieldOffset(0)] internal IntPtr _dispatch; + + // *** END GENERATED CODE *** + + #endregion + + [FieldOffset(0)] internal IntPtr _pvarVal; + [FieldOffset(0)] internal IntPtr _byref; + [FieldOffset(0)] internal Record _record; + } + + /// <summary> + /// Primitive types are the basic COM types. It includes valuetypes like ints, but also reference types + /// like BStrs. It does not include composite types like arrays and user-defined COM types (IUnknown/IDispatch). + /// </summary> + internal static bool IsPrimitiveType(VarEnum varEnum) { + switch(varEnum) { + #region Generated Variant IsPrimitiveType + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_IsPrimitiveType from: generate_comdispatch.py + + case VarEnum.VT_I1: + case VarEnum.VT_I2: + case VarEnum.VT_I4: + case VarEnum.VT_I8: + case VarEnum.VT_UI1: + case VarEnum.VT_UI2: + case VarEnum.VT_UI4: + case VarEnum.VT_UI8: + case VarEnum.VT_INT: + case VarEnum.VT_UINT: + case VarEnum.VT_BOOL: + case VarEnum.VT_R4: + case VarEnum.VT_R8: + case VarEnum.VT_DECIMAL: + case VarEnum.VT_DATE: + case VarEnum.VT_BSTR: + + // *** END GENERATED CODE *** + + #endregion + return true; + } + + return false; + } + + unsafe public void CopyFromIndirect(object value) { + + VarEnum vt = (VarEnum)(((int)this.VariantType) & ~((int)VarEnum.VT_BYREF)); + + if (value == null) { + if (vt == VarEnum.VT_DISPATCH || vt == VarEnum.VT_UNKNOWN || vt == VarEnum.VT_BSTR) { + *(IntPtr*)this._typeUnion._unionTypes._byref = IntPtr.Zero; + } + return; + } + + switch (vt) { + case VarEnum.VT_I1: + *(sbyte*)this._typeUnion._unionTypes._byref = (sbyte)value; + break; + + case VarEnum.VT_UI1: + *(byte*)this._typeUnion._unionTypes._byref = (byte)value; + break; + + case VarEnum.VT_I2: + *(short*)this._typeUnion._unionTypes._byref = (short)value; + break; + + case VarEnum.VT_UI2: + *(ushort*)this._typeUnion._unionTypes._byref = (ushort)value; + break; + + case VarEnum.VT_BOOL: + *(short*)this._typeUnion._unionTypes._byref = (bool)value ? (short)-1 : (short)0; + break; + + case VarEnum.VT_I4: + case VarEnum.VT_INT: + *(int*)this._typeUnion._unionTypes._byref = (int)value; + break; + + case VarEnum.VT_UI4: + case VarEnum.VT_UINT: + *(uint*)this._typeUnion._unionTypes._byref = (uint)value; + break; + + case VarEnum.VT_ERROR: + *(int*)this._typeUnion._unionTypes._byref = ((ErrorWrapper)value).ErrorCode; + break; + + case VarEnum.VT_I8: + *(Int64*)this._typeUnion._unionTypes._byref = (Int64)value; + break; + + case VarEnum.VT_UI8: + *(UInt64*)this._typeUnion._unionTypes._byref = (UInt64)value; + break; + + case VarEnum.VT_R4: + *(float*)this._typeUnion._unionTypes._byref = (float)value; + break; + + case VarEnum.VT_R8: + *(double*)this._typeUnion._unionTypes._byref = (double)value; + break; + + case VarEnum.VT_DATE: + *(double*)this._typeUnion._unionTypes._byref = ((DateTime)value).ToOADate(); + break; + + case VarEnum.VT_UNKNOWN: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.GetIUnknownForObject(value); + break; + + case VarEnum.VT_DISPATCH: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.GetIDispatchForObject(value); + break; + + case VarEnum.VT_BSTR: + *(IntPtr*)this._typeUnion._unionTypes._byref = Marshal.StringToBSTR((string)value); + break; + + case VarEnum.VT_CY: + *(long*)this._typeUnion._unionTypes._byref = decimal.ToOACurrency((decimal)value); + break; + + case VarEnum.VT_DECIMAL: + *(decimal*)this._typeUnion._unionTypes._byref = (decimal)value; + break; + + case VarEnum.VT_VARIANT: + Marshal.GetNativeVariantForObject(value, this._typeUnion._unionTypes._byref); + break; + + default: + throw new ArgumentException("invalid argument type"); + } + } + + /// <summary> + /// Get the managed object representing the Variant. + /// </summary> + /// <returns></returns> + public object ToObject() { + // Check the simple case upfront + if (IsEmpty) { + return null; + } + + switch (VariantType) { + case VarEnum.VT_NULL: return DBNull.Value; + + #region Generated Variant ToObject + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_ToObject from: generate_comdispatch.py + + case VarEnum.VT_I1: return AsI1; + case VarEnum.VT_I2: return AsI2; + case VarEnum.VT_I4: return AsI4; + case VarEnum.VT_I8: return AsI8; + case VarEnum.VT_UI1: return AsUi1; + case VarEnum.VT_UI2: return AsUi2; + case VarEnum.VT_UI4: return AsUi4; + case VarEnum.VT_UI8: return AsUi8; + case VarEnum.VT_INT: return AsInt; + case VarEnum.VT_UINT: return AsUint; + case VarEnum.VT_BOOL: return AsBool; + case VarEnum.VT_ERROR: return AsError; + case VarEnum.VT_R4: return AsR4; + case VarEnum.VT_R8: return AsR8; + case VarEnum.VT_DECIMAL: return AsDecimal; + case VarEnum.VT_CY: return AsCy; + case VarEnum.VT_DATE: return AsDate; + case VarEnum.VT_BSTR: return AsBstr; + case VarEnum.VT_UNKNOWN: return AsUnknown; + case VarEnum.VT_DISPATCH: return AsDispatch; + // VarEnum.VT_VARIANT is handled by Marshal.GetObjectForNativeVariant below + + // *** END GENERATED CODE *** + + #endregion + + default: + try { + unsafe { + fixed (void* pThis = &this) { + return Marshal.GetObjectForNativeVariant((System.IntPtr)pThis); + } + } + } + catch (Exception ex) { + throw new NotImplementedException("Variant.ToObject cannot handle" + VariantType, ex); + } + } + } + + /// <summary> + /// Release any unmanaged memory associated with the Variant + /// </summary> + /// <returns></returns> + public void Clear() { + // We do not need to call OLE32's VariantClear for primitive types or ByRefs + // to safe ourselves the cost of interop transition. + // ByRef indicates the memory is not owned by the VARIANT itself while + // primitive types do not have any resources to free up. + // Hence, only safearrays, BSTRs, interfaces and user types are + // handled differently. + VarEnum vt = VariantType; + if ((vt & VarEnum.VT_BYREF) != 0) { + VariantType = VarEnum.VT_EMPTY; + } else if ( + ((vt & VarEnum.VT_ARRAY) != 0) || + ((vt) == VarEnum.VT_BSTR) || + ((vt) == VarEnum.VT_UNKNOWN) || + ((vt) == VarEnum.VT_DISPATCH) || + ((vt) == VarEnum.VT_VARIANT) || + ((vt) == VarEnum.VT_RECORD) || + ((vt) == VarEnum.VT_VARIANT) + ) { + unsafe { + fixed (void* pThis = &this) { + NativeMethods.VariantClear((IntPtr)pThis); + } + } + BCLDebug.Assert(IsEmpty, "variant"); + } else { + VariantType = VarEnum.VT_EMPTY; + } + } + + public VarEnum VariantType { + get { + return (VarEnum)_typeUnion._vt; + } + set { + _typeUnion._vt = (ushort)value; + } + } + + internal bool IsEmpty { + get { + return _typeUnion._vt == ((ushort)VarEnum.VT_EMPTY); + } + } + + internal bool IsByRef { + get { + return (_typeUnion._vt & ((ushort)VarEnum.VT_BYREF)) != 0; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly")] + public void SetAsNULL() { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_NULL; + } + + #region Generated Variant accessors + + // *** BEGIN GENERATED CODE *** + // generated by function: gen_accessors from: generate_comdispatch.py + + // VT_I1 + + public SByte AsI1 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I1, "variant"); + return _typeUnion._unionTypes._i1; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I1; + _typeUnion._unionTypes._i1 = value; + } + } + + // VT_I2 + + public Int16 AsI2 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I2, "variant"); + return _typeUnion._unionTypes._i2; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I2; + _typeUnion._unionTypes._i2 = value; + } + } + + // VT_I4 + + public Int32 AsI4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I4, "variant"); + return _typeUnion._unionTypes._i4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I4; + _typeUnion._unionTypes._i4 = value; + } + } + + // VT_I8 + + public Int64 AsI8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_I8, "variant"); + return _typeUnion._unionTypes._i8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_I8; + _typeUnion._unionTypes._i8 = value; + } + } + + // VT_UI1 + + public Byte AsUi1 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI1, "variant"); + return _typeUnion._unionTypes._ui1; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI1; + _typeUnion._unionTypes._ui1 = value; + } + } + + // VT_UI2 + + public UInt16 AsUi2 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI2, "variant"); + return _typeUnion._unionTypes._ui2; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI2; + _typeUnion._unionTypes._ui2 = value; + } + } + + // VT_UI4 + + public UInt32 AsUi4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI4, "variant"); + return _typeUnion._unionTypes._ui4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI4; + _typeUnion._unionTypes._ui4 = value; + } + } + + // VT_UI8 + + public UInt64 AsUi8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UI8, "variant"); + return _typeUnion._unionTypes._ui8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UI8; + _typeUnion._unionTypes._ui8 = value; + } + } + + // VT_INT + + public Int32 AsInt { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_INT, "variant"); + return _typeUnion._unionTypes._int; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_INT; + _typeUnion._unionTypes._int = value; + } + } + + // VT_UINT + + public UInt32 AsUint { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UINT, "variant"); + return _typeUnion._unionTypes._uint; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UINT; + _typeUnion._unionTypes._uint = value; + } + } + + // VT_BOOL + + public bool AsBool { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_BOOL, "variant"); + return _typeUnion._unionTypes._bool != 0; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BOOL; + _typeUnion._unionTypes._bool = value ? (short)-1 : (short)0; + } + } + + // VT_ERROR + + public Int32 AsError { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_ERROR, "variant"); + return _typeUnion._unionTypes._error; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_ERROR; + _typeUnion._unionTypes._error = value; + } + } + + // VT_R4 + + public Single AsR4 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_R4, "variant"); + return _typeUnion._unionTypes._r4; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R4; + _typeUnion._unionTypes._r4 = value; + } + } + + // VT_R8 + + public Double AsR8 { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_R8, "variant"); + return _typeUnion._unionTypes._r8; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_R8; + _typeUnion._unionTypes._r8 = value; + } + } + + // VT_DECIMAL + + public Decimal AsDecimal { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DECIMAL, "variant"); + // The first byte of Decimal is unused, but usually set to 0 + Variant v = this; + v._typeUnion._vt = 0; + return v._decimal; + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DECIMAL; + _decimal = value; + // _vt overlaps with _decimal, and should be set after setting _decimal + _typeUnion._vt = (ushort)VarEnum.VT_DECIMAL; + } + } + + // VT_CY + + public Decimal AsCy { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_CY, "variant"); + return Decimal.FromOACurrency(_typeUnion._unionTypes._cy); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_CY; + _typeUnion._unionTypes._cy = Decimal.ToOACurrency(value); + } + } + + // VT_DATE + + public DateTime AsDate { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DATE, "variant"); + return DateTime.FromOADate(_typeUnion._unionTypes._date); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DATE; + _typeUnion._unionTypes._date = value.ToOADate(); + } + } + + // VT_BSTR + + public String AsBstr { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_BSTR, "variant"); + return (string)Marshal.PtrToStringBSTR(this._typeUnion._unionTypes._bstr); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_BSTR; + this._typeUnion._unionTypes._bstr = Marshal.StringToBSTR(value); + } + } + + // VT_UNKNOWN + + public Object AsUnknown { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_UNKNOWN, "variant"); + if (_typeUnion._unionTypes._unknown == IntPtr.Zero) + return null; + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._unknown); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_UNKNOWN; + if (value == null) + _typeUnion._unionTypes._unknown = IntPtr.Zero; + else + _typeUnion._unionTypes._unknown = Marshal.GetIUnknownForObject(value); + } + } + + // VT_DISPATCH + + public Object AsDispatch { + get { + BCLDebug.Assert(VariantType == VarEnum.VT_DISPATCH, "variant"); + if (_typeUnion._unionTypes._dispatch == IntPtr.Zero) + return null; + return Marshal.GetObjectForIUnknown(_typeUnion._unionTypes._dispatch); + } + set { + BCLDebug.Assert(IsEmpty, "variant"); // The setter can only be called once as VariantClear might be needed otherwise + VariantType = VarEnum.VT_DISPATCH; + if (value == null) + _typeUnion._unionTypes._dispatch = IntPtr.Zero; + else + _typeUnion._unionTypes._dispatch = Marshal.GetIDispatchForObject(value); + } + } + + + // *** END GENERATED CODE *** + + internal IntPtr AsByRefVariant + { + get { + BCLDebug.Assert(VariantType == (VarEnum.VT_BYREF | VarEnum.VT_VARIANT), "variant"); + return _typeUnion._unionTypes._pvarVal; + } + } + + #endregion + } +} |