From aa612d1ea1c087ba5e982df4d5929436b63bb465 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 Jun 2022 18:20:38 +0800 Subject: [PATCH 01/35] Convert BoxEnum to managed --- .../src/System/Variant.cs | 10 ++++-- .../classlibnative/bcltype/variant.cpp | 31 ------------------- src/coreclr/classlibnative/bcltype/variant.h | 1 - src/coreclr/vm/ecalllist.h | 1 - 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index b945b28e08a75a..02506fca0e1af1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -303,9 +303,13 @@ public Variant(object? obj) _ => _objref, // CV_DECIMAL, CV_STRING, CV_OBJECT }; - // This routine will return an boxed enum. - [MethodImpl(MethodImplOptions.InternalCall)] - private extern object BoxEnum(); + private unsafe object BoxEnum() + { + Debug.Assert(_objref != null); + return RuntimeHelpers.Box( + RuntimeHelpers.GetMethodTable(_objref), + ref Unsafe.As(ref _data))!; + } // Helper code for marshaling managed objects to VARIANT's (we use // managed variants as an intermediate type. diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index a269644d797005..2bbb01c7b6b9ec 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -156,37 +156,6 @@ FCIMPL2(void, COMVariant::SetFieldsObject, VariantData* var, Object* vVal) FCIMPLEND -FCIMPL1(Object*, COMVariant::BoxEnum, VariantData* var) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(var)); - PRECONDITION(var->GetObjRef() != NULL); - } - CONTRACTL_END; - - OBJECTREF retO = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_1(retO); - -#ifdef _DEBUG - CVTypes vType = (CVTypes) var->GetType(); -#endif - - _ASSERTE(vType == CV_ENUM); - - MethodTable* mt = ((REFLECTCLASSBASEREF) var->GetObjRef())->GetType().GetMethodTable(); - _ASSERTE(mt); - - retO = mt->Box(var->GetData()); - - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(retO); -} -FCIMPLEND - - /*===============================GetTypeFromClass=============================== **Action: Takes an MethodTable * and returns the associated CVType. **Arguments: MethodTable * -- a pointer to the class for which we want the CVType. diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h index aae0a5f9741ac6..d8b6fb6c0c60ab 100644 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -31,7 +31,6 @@ class COMVariant // static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal); - static FCDECL1(Object*, BoxEnum, VariantData* var); private: // GetCVTypeFromClass diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 9d7e8f7125c39e..3d1b8b469993ca 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -361,7 +361,6 @@ FCFuncEnd() #ifdef FEATURE_COMINTEROP FCFuncStart(gVariantFuncs) FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject) - FCFuncElement("BoxEnum", COMVariant::BoxEnum) FCFuncEnd() #endif // FEATURE_COMINTEROP From a3dfc8e18986e9dd2fca1dca0c2b640bb46a78ac Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 Jun 2022 19:59:04 +0800 Subject: [PATCH 02/35] Temp commit --- .../src/System/Variant.cs | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 02506fca0e1af1..e3988d5aeda59f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -70,11 +70,43 @@ internal struct Variant internal static Variant Missing => new Variant(CV_MISSING, Type.Missing, 0); internal static Variant DBNull => new Variant(CV_NULL, System.DBNull.Value, 0); - // - // Native Methods - // - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern void SetFieldsObject(object val); + internal unsafe void SetFieldsObject(object val) + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(val); + + if (!pMT->IsValueType) + { + _objref = val; + _flags = CV_OBJECT; + } + else if (val.GetType().FullName == "System.Drawing.Color") + { + + } + else + { + switch (val) + { + case byte u1: + this = new(u1); + break; + case sbyte i1: + this = new(i1); + break; + case short i2: + this = new(i2); + break; + case ushort u2: + this = new(u2); + break; + case int i4: + this = new(i4); + break; + } + + ref byte data = ref RuntimeHelpers.GetRawData(val); // Unbox? + } + } // // Constructors @@ -206,7 +238,7 @@ public Variant(object? obj) return; } - if (obj == null) + if (obj == null || obj is System.Empty) { this = Empty; return; From 6ceec599e8863fecebf8f7ec9271a89bd9a3417e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 23 Mar 2024 13:33:11 +0800 Subject: [PATCH 03/35] SetFieldsObject to managed --- .../src/System/Enum.CoreCLR.cs | 2 +- .../src/System/Variant.cs | 108 +++++++++++++++++- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index 6f578ccad411fd..121aa79ea37211 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -30,7 +30,7 @@ private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe CorElementType InternalGetCorElementType() + internal unsafe CorElementType InternalGetCorElementType() { CorElementType elementType = InternalGetCorElementType(RuntimeHelpers.GetMethodTable(this)); GC.KeepAlive(this); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index e3988d5aeda59f..08a12058e2e2cf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -12,12 +12,13 @@ using System.Diagnostics; using System.Globalization; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System { - internal struct Variant + internal partial struct Variant { // Do Not change the order of these fields. // They are mapped to the native VariantData * data structure. @@ -60,6 +61,15 @@ internal struct Variant internal const int CV_MISSING = 0x16; internal const int CV_NULL = 0x17; internal const int CV_LAST = 0x18; + 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 const int TypeCodeBitMask = 0xffff; internal const int VTBitMask = unchecked((int)0xff000000); @@ -70,6 +80,12 @@ internal struct Variant internal static Variant Missing => new Variant(CV_MISSING, Type.Missing, 0); internal static Variant DBNull => new Variant(CV_NULL, System.DBNull.Value, 0); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe bool IsSystemDrawingColor(MethodTable* pMT); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] + private static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); + internal unsafe void SetFieldsObject(object val) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(val); @@ -77,14 +93,41 @@ internal unsafe void SetFieldsObject(object val) if (!pMT->IsValueType) { _objref = val; - _flags = CV_OBJECT; + if (val.GetType() == typeof(Missing)) + { + _flags = CV_MISSING; + } + else if (val.GetType() == typeof(DBNull)) + { + _flags = CV_NULL; + } + else if (val.GetType() == typeof(Empty)) + { + _flags = CV_EMPTY; + _objref = null; + } } - else if (val.GetType().FullName == "System.Drawing.Color") + else if (IsSystemDrawingColor(pMT)) { - + // System.Drawing.Color is converted to UInt32 + object obj = val; + _data = ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref obj)); + _flags = CV_U4; } else { + //If this is a primitive type, we need to unbox it, get the value and create a variant + //with just those values. + + _objref = null; + + //copy all of the data. + // Copies must be done based on the exact number of bytes to copy. + // We don't want to read garbage from other blocks of memory. + //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities + //If we don't find one of those ranges, we've found a value class + //of which we don't have inherent knowledge, so just slam that into an + //ObjectRef. switch (val) { case byte u1: @@ -93,18 +136,71 @@ internal unsafe void SetFieldsObject(object val) case sbyte i1: this = new(i1); break; + case bool boolean: + this = new(boolean); + break; case short i2: this = new(i2); break; case ushort u2: this = new(u2); break; + case char ch: + this = new(ch); + break; case int i4: this = new(i4); break; + case uint u4: + this = new(u4); + break; + case float r4: + this = new(r4); + break; + // CV_DATETIME handled by caller + case TimeSpan ts: + _flags = CV_TIMESPAN; + _data = ts.Ticks; + break; + // CV_CURRENCY handled by caller + case long i8: + this = new(i8); + break; + case ulong u8: + this = new(u8); + break; + case double r8: + this = new(r8); + break; + case Enum e: + _data = (long)Enum.ToUInt64(e); + _objref = e.GetType(); + _flags = e.InternalGetCorElementType() switch + { + CorElementType.ELEMENT_TYPE_I1 => CV_ENUM | EnumI1, + CorElementType.ELEMENT_TYPE_U1 => CV_ENUM | EnumU1, + CorElementType.ELEMENT_TYPE_I2 => CV_ENUM | EnumI2, + CorElementType.ELEMENT_TYPE_U2 => CV_ENUM | EnumU2, + CorElementType.ELEMENT_TYPE_I4 => CV_ENUM | EnumI4, + CorElementType.ELEMENT_TYPE_U4 => CV_ENUM | EnumU4, +#if TARGET_64BIT + CorElementType.ELEMENT_TYPE_I => CV_ENUM | EnumI8, + CorElementType.ELEMENT_TYPE_U => CV_ENUM | EnumU8, +#else + + CorElementType.ELEMENT_TYPE_I => CV_ENUM | EnumI4, + CorElementType.ELEMENT_TYPE_U => CV_ENUM | EnumU4, +#endif + CorElementType.ELEMENT_TYPE_I8 => CV_ENUM | EnumI8, + CorElementType.ELEMENT_TYPE_U8 => CV_ENUM | EnumU8, + _ => throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType) + }; + break; + default: + // Decimals and other boxed value classes get handled here. + _objref = val; + break; } - - ref byte data = ref RuntimeHelpers.GetRawData(val); // Unbox? } } From 5eb3f8064ca1e30ad43c57c06d1dc9b67ea373fc Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 23 Mar 2024 13:43:15 +0800 Subject: [PATCH 04/35] Color call --- .../classlibnative/bcltype/variant.cpp | 24 +++++++++++++++++++ src/coreclr/classlibnative/bcltype/variant.h | 3 +++ src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/qcallentrypoints.cpp | 1 + 4 files changed, 29 insertions(+) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index 2bbb01c7b6b9ec..18313f84edb00e 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -34,6 +34,30 @@ #define EnumU8 0x800000 #define EnumMask 0xF00000 +FCIMPL1(CLR_BOOL, COMVariant::IsSystemDrawingColor, MethodTable* valMT) +{ + FCALL_CONTRACT; + + return IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl()); +} +FCIMPLEND + +extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj) +{ + QCALL_CONTRACT; + + uint32_t ret = 0; + + BEGIN_QCALL; + + GCX_COOP(); + OBJECTREF srcObj = obj.Get(); + ret = ConvertSystemColorToOleColor(&srcObj); + + END_QCALL; + + return ret; +} /*===============================SetFieldsObject================================ ** diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h index d8b6fb6c0c60ab..c52bbaee2a9ad2 100644 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -31,6 +31,7 @@ class COMVariant // static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal); + static FCDECL1(CLR_BOOL, IsSystemDrawingColor, MethodTable* valMT); private: // GetCVTypeFromClass @@ -39,5 +40,7 @@ class COMVariant static int GetEnumFlags(TypeHandle th); }; +extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); + #endif // _VARIANT_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 3d1b8b469993ca..35cb3ae8a3465a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -361,6 +361,7 @@ FCFuncEnd() #ifdef FEATURE_COMINTEROP FCFuncStart(gVariantFuncs) FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject) + FCFuncElement("IsSystemDrawingColor", COMVariant::IsSystemDrawingColor) FCFuncEnd() #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 8ff379f40f6e08..b6663dfc4db375 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -316,6 +316,7 @@ static const Entry s_QCall[] = DllImportEntry(MngdSafeArrayMarshaler_ConvertContentsToManaged) DllImportEntry(MngdSafeArrayMarshaler_ClearNative) DllImportEntry(OAVariant_ChangeType) + DllImportEntry(Variant_ConvertSystemColorToOleColor) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) DllImportEntry(NativeLibrary_LoadByName) From f82d6b62ca59e52accd339717ecd86b2ccdc7407 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 23 Mar 2024 13:48:08 +0800 Subject: [PATCH 05/35] Cleanup FCall --- .../classlibnative/bcltype/variant.cpp | 233 ------------------ src/coreclr/classlibnative/bcltype/variant.h | 13 - src/coreclr/vm/ecalllist.h | 1 - 3 files changed, 247 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index 18313f84edb00e..652679fbb21c3e 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -20,19 +20,6 @@ #include "vars.hpp" #include "variant.h" #include "string.h" -#include "field.h" - -// The following values are used to represent underlying -// type of the Enum.. -#define EnumI1 0x100000 -#define EnumU1 0x200000 -#define EnumI2 0x300000 -#define EnumU2 0x400000 -#define EnumI4 0x500000 -#define EnumU4 0x600000 -#define EnumI8 0x700000 -#define EnumU8 0x800000 -#define EnumMask 0xF00000 FCIMPL1(CLR_BOOL, COMVariant::IsSystemDrawingColor, MethodTable* valMT) { @@ -59,224 +46,4 @@ extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::Object return ret; } -/*===============================SetFieldsObject================================ -** -==============================================================================*/ -FCIMPL2(void, COMVariant::SetFieldsObject, VariantData* var, Object* vVal) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(var)); - PRECONDITION(CheckPointer(vVal)); - } - CONTRACTL_END; - - OBJECTREF val = ObjectToOBJECTREF(vVal); - - HELPER_METHOD_FRAME_BEGIN_1(val); - GCPROTECT_BEGININTERIOR(var) - - CVTypes cvt = CV_EMPTY; - TypeHandle typeHandle; - - MethodTable *valMT = val->GetMethodTable(); - - //If this isn't a value class, we should just skip out because we're not going - //to do anything special with it. - if (!valMT->IsValueType()) - { - var->SetObjRef(val); - typeHandle = TypeHandle(valMT); - - if (typeHandle==GetTypeHandleForCVType(CV_MISSING)) - { - var->SetType(CV_MISSING); - } - else if (typeHandle==GetTypeHandleForCVType(CV_NULL)) - { - var->SetType(CV_NULL); - } - else if (typeHandle==GetTypeHandleForCVType(CV_EMPTY)) - { - var->SetType(CV_EMPTY); - var->SetObjRef(NULL); - } - else - { - var->SetType(CV_OBJECT); - } - } - else if (IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl())) - { - // System.Drawing.Color is converted to UInt32 - var->SetDataAsUInt32(ConvertSystemColorToOleColor(&val)); - var->SetType(CV_U4); - } - else - { - //If this is a primitive type, we need to unbox it, get the value and create a variant - //with just those values. - void *UnboxData = val->UnBox(); - - ClearObjectReference(var->GetObjRefPtr()); - typeHandle = TypeHandle(valMT); - CorElementType cet = typeHandle.GetSignatureCorElementType(); - - if (cet>=ELEMENT_TYPE_BOOLEAN && cet<=ELEMENT_TYPE_STRING) - { - cvt = (CVTypes)cet; - } - else - { - cvt = GetCVTypeFromClass(valMT); - } - var->SetType(cvt); - - - //copy all of the data. - // Copies must be done based on the exact number of bytes to copy. - // We don't want to read garbage from other blocks of memory. - //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities - //If we don't find one of those ranges, we've found a value class - //of which we don't have inherent knowledge, so just slam that into an - //ObjectRef. - if (cvt>=CV_BOOLEAN && cvt<=CV_U1 && cvt != CV_CHAR) - { - var->SetDataAsInt64(*((UINT8 *)UnboxData)); - } - else if (cvt==CV_CHAR || cvt>=CV_I2 && cvt<=CV_U2) - { - var->SetDataAsInt64(*((UINT16 *)UnboxData)); - } - else if (cvt>=CV_I4 && cvt<=CV_U4 || cvt==CV_R4) - { - var->SetDataAsInt64(*((UINT32 *)UnboxData)); - } - else if ((cvt>=CV_I8 && cvt<=CV_R8) || (cvt==CV_DATETIME) || (cvt==CV_TIMESPAN) || (cvt==CV_CURRENCY)) - { - var->SetDataAsInt64(*((INT64 *)UnboxData)); - } - else if (cvt==CV_EMPTY || cvt==CV_NULL || cvt==CV_MISSING) - { - var->SetType(cvt); - } - else if (cvt==CV_ENUM) - { - var->SetDataAsInt64(*((INT32 *)UnboxData)); - var->SetObjRef(typeHandle.GetManagedClassObject()); - var->SetType(GetEnumFlags(typeHandle)); - } - else - { - // Decimals and other boxed value classes get handled here. - var->SetObjRef(val); - } - } - - GCPROTECT_END(); - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - - -/*===============================GetTypeFromClass=============================== -**Action: Takes an MethodTable * and returns the associated CVType. -**Arguments: MethodTable * -- a pointer to the class for which we want the CVType. -**Returns: The CVType associated with the MethodTable or CV_OBJECT if this can't be -** determined. -**Exceptions: None -==============================================================================*/ - -CVTypes COMVariant::GetCVTypeFromClass(TypeHandle th) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - if (th.IsNull()) - return CV_EMPTY; - - //We'll start looking from Variant. Empty and Void are handled below. - for (int i=CV_EMPTY; i check this approximation - we may be losing exact type information - ApproxFieldDescIterator fdIterator(th.GetMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS); - FieldDesc* p = fdIterator.Next(); - if (NULL == p) - { - _ASSERTE(!"NULL FieldDesc returned"); - return 0; - } - -#ifdef _DEBUG - WORD fldCnt = th.GetMethodTable()->GetNumInstanceFields(); -#endif - - _ASSERTE(fldCnt == 1); - - CorElementType cet = p[0].GetFieldType(); - switch (cet) - { - case ELEMENT_TYPE_I1: - return (CV_ENUM | EnumI1); - - case ELEMENT_TYPE_U1: - return (CV_ENUM | EnumU1); - - case ELEMENT_TYPE_I2: - return (CV_ENUM | EnumI2); - - case ELEMENT_TYPE_U2: - return (CV_ENUM | EnumU2); - - IN_TARGET_32BIT(case ELEMENT_TYPE_I:) - case ELEMENT_TYPE_I4: - return (CV_ENUM | EnumI4); - - IN_TARGET_32BIT(case ELEMENT_TYPE_U:) - case ELEMENT_TYPE_U4: - return (CV_ENUM | EnumU4); - - IN_TARGET_64BIT(case ELEMENT_TYPE_I:) - case ELEMENT_TYPE_I8: - return (CV_ENUM | EnumI8); - - IN_TARGET_64BIT(case ELEMENT_TYPE_U:) - case ELEMENT_TYPE_U8: - return (CV_ENUM | EnumU8); - - default: - _ASSERTE(!"UNknown Type"); - return 0; - } -} - #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h index c52bbaee2a9ad2..d0e3a55c0b98d3 100644 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -23,21 +23,8 @@ class COMVariant { - friend class OleVariant; - public: - // - // Helper Routines - // - - static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal); static FCDECL1(CLR_BOOL, IsSystemDrawingColor, MethodTable* valMT); - -private: - // GetCVTypeFromClass - // This method will return the CVTypes from the Variant instance - static CVTypes GetCVTypeFromClass(TypeHandle th); - static int GetEnumFlags(TypeHandle th); }; extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 35cb3ae8a3465a..5511867fdc1812 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -360,7 +360,6 @@ FCFuncEnd() #ifdef FEATURE_COMINTEROP FCFuncStart(gVariantFuncs) - FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject) FCFuncElement("IsSystemDrawingColor", COMVariant::IsSystemDrawingColor) FCFuncEnd() #endif // FEATURE_COMINTEROP From e8f3f1fa2020de0ff60860442933c530cf7ee64b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 23 Mar 2024 13:59:21 +0800 Subject: [PATCH 06/35] Handle decimal and other CV_OBJECT --- .../System.Private.CoreLib/src/System/Variant.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 08a12058e2e2cf..26f8d235ecc4a0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -106,6 +106,10 @@ internal unsafe void SetFieldsObject(object val) _flags = CV_EMPTY; _objref = null; } + else + { + _flags = CV_OBJECT; + } } else if (IsSystemDrawingColor(pMT)) { @@ -172,6 +176,9 @@ internal unsafe void SetFieldsObject(object val) case double r8: this = new(r8); break; + case decimal d: + this = new(d); + break; case Enum e: _data = (long)Enum.ToUInt64(e); _objref = e.GetType(); @@ -197,8 +204,9 @@ internal unsafe void SetFieldsObject(object val) }; break; default: - // Decimals and other boxed value classes get handled here. + // Other boxed value classes get handled here. _objref = val; + _flags = CV_OBJECT; break; } } From 88f9e55f3c3fb28ef925d54ffa80d455632a8459 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 25 Mar 2024 19:43:34 +0800 Subject: [PATCH 07/35] Fix BoxEnum --- .../System.Private.CoreLib/src/System/Enum.CoreCLR.cs | 2 +- .../System.Private.CoreLib/src/System/Variant.cs | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index 121aa79ea37211..597da509513884 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -15,7 +15,7 @@ public abstract partial class Enum private static partial void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object InternalBoxEnum(RuntimeType enumType, long value); + internal static extern object InternalBoxEnum(RuntimeType enumType, long value); [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 26f8d235ecc4a0..a5b82455ea2a59 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -433,20 +433,12 @@ public Variant(object? obj) CV_R8 => BitConverter.Int64BitsToDouble(_data), CV_DATETIME => new DateTime(_data), CV_TIMESPAN => new TimeSpan(_data), - CV_ENUM => BoxEnum(), + CV_ENUM => Enum.InternalBoxEnum((RuntimeType)_objref!, _data), CV_MISSING => Type.Missing, CV_NULL => System.DBNull.Value, _ => _objref, // CV_DECIMAL, CV_STRING, CV_OBJECT }; - private unsafe object BoxEnum() - { - Debug.Assert(_objref != null); - return RuntimeHelpers.Box( - RuntimeHelpers.GetMethodTable(_objref), - ref Unsafe.As(ref _data))!; - } - // Helper code for marshaling managed objects to VARIANT's (we use // managed variants as an intermediate type. internal static void MarshalHelperConvertObjectToVariant(object o, ref Variant v) From 4013082aaa030c8a1fe68117495b15264a542e15 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 25 Mar 2024 20:42:30 +0800 Subject: [PATCH 08/35] Body of OAVariant_ChangeType --- .../src/Microsoft/Win32/OAVariantLib.cs | 114 ++++++++++++++++-- .../src/System/Variant.cs | 9 +- 2 files changed, 112 insertions(+), 11 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index a5fbad6504f357..3d190d5b701dd5 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -18,6 +18,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; namespace Microsoft.Win32 { @@ -81,16 +82,115 @@ internal static Variant ChangeType(Variant source, Type targetClass, short optio ArgumentNullException.ThrowIfNull(culture); Variant result = default; - ChangeType( - &result, - &source, - culture.LCID, - targetClass.TypeHandle.Value, - GetCVTypeFromClass(targetClass), - options); + bool converted = false; + + TypeHandle th = ((RuntimeType)targetClass).GetNativeTypeHandle(); + Debug.Assert(!th.IsTypeDesc); + if (Variant.IsSystemDrawingColor(th.AsMethodTable())) + { + if (source.CVType is Variant.CV_I4 or Variant.CV_U4) + { + // Int32/UInt32 can be converted to System.Drawing.Color + object? ret = null; + ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref ret), (uint)source._data); + Debug.Assert(ret != null); + result._objref = ret; + result._flags = Variant.CV_OBJECT; + + converted = true; + } + } + + if (!converted) + { + VarEnum vt = source.CVType switch + { + Variant.CV_EMPTY => VarEnum.VT_EMPTY, + Variant.CV_VOID => VarEnum.VT_VOID, + Variant.CV_BOOLEAN => VarEnum.VT_BOOL, + Variant.CV_CHAR => VarEnum.VT_UI2, + Variant.CV_I1 => VarEnum.VT_I1, + Variant.CV_U1 => VarEnum.VT_UI1, + Variant.CV_I2 => VarEnum.VT_I2, + Variant.CV_U2 => VarEnum.VT_UI2, + Variant.CV_I4 => VarEnum.VT_I4, + Variant.CV_U4 => VarEnum.VT_UI4, + Variant.CV_I8 => VarEnum.VT_UI8, + Variant.CV_R4 => VarEnum.VT_R4, + Variant.CV_R8 => VarEnum.VT_R8, + Variant.CV_STRING => VarEnum.VT_BSTR, + // Variant.CV_PTR => INVALID_MAPPING, + Variant.CV_DATETIME => VarEnum.VT_DATE, + // Variant.CV_TIMESPAN => INVALID_MAPPING, + Variant.CV_OBJECT => VarEnum.VT_UNKNOWN, + Variant.CV_DECIMAL => VarEnum.VT_DECIMAL, + Variant.CV_CURRENCY => VarEnum.VT_CY, + // Variant.CV_ENUM => INVALID_MAPPING, + // Variant.CV_MISSING => INVALID_MAPPING, + Variant.CV_NULL => VarEnum.VT_NULL, + _ => throw new NotSupportedException(SR.NotSupported_ChangeType) + }; + + ComVariant vOp = ToOAVariant(source); + ComVariant ret = default; + + int hr = VariantChangeTypeEx(ref ret, ref vOp, culture.LCID, options, (ushort)vt); + + using (vOp) + using (ret) + { + if (hr < 0) + OAFailed(hr); + + result = FromOAVariant(ret); + if (source.CVType == Variant.CV_CHAR) + { + result._flags = Variant.CV_CHAR; + } + } + } + return result; } + private static void OAFailed(int hr) + { + switch (hr) + { + case HResults.COR_E_OUTOFMEMORY: + throw new OutOfMemoryException(); + case unchecked((int)0x80020008): // DISP_E_BADVARTYPE + throw new NotSupportedException(SR.NotSupported_OleAutBadVarType); + case HResults.COR_E_DIVIDEBYZERO: + throw new DivideByZeroException(); + case HResults.COR_E_OVERFLOW: + throw new OverflowException(); + case HResults.TYPE_E_TYPEMISMATCH: + throw new InvalidCastException(SR.InvalidCast_OATypeMismatch); + case HResults.E_INVALIDARG: + throw new ArgumentException(); + default: + Debug.Fail("Unrecognized HResult - OAVariantLib routine failed in an unexpected way!"); + throw Marshal.GetExceptionForHR(hr); + } + } + + private static ComVariant ToOAVariant(Variant input) + { + + } + + private static Variant FromOAVariant(ComVariant input) + { + + } + + [LibraryImport(Interop.Libraries.OleAut32)] + private static partial int VariantChangeTypeEx(ref ComVariant pVarRes, ref ComVariant pVarSrc, int lcid, short wFlags, ushort vt); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] + private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_ChangeType")] private static partial void ChangeType(Variant* result, Variant* source, int lcid, IntPtr typeHandle, int cvType, short flags); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index a5b82455ea2a59..1cd441a1cd4d53 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -22,9 +22,9 @@ internal partial struct Variant { // Do Not change the order of these fields. // They are mapped to the native VariantData * data structure. - private object? _objref; - private long _data; - private int _flags; + internal object? _objref; + internal long _data; + internal int _flags; // The following bits have been taken up as follows // bits 0-15 - Type code @@ -57,6 +57,7 @@ internal partial struct Variant internal const int CV_TIMESPAN = 0x11; internal const int CV_OBJECT = 0x12; internal const int CV_DECIMAL = 0x13; + internal const int CV_CURRENCY = 0x14; internal const int CV_ENUM = 0x15; internal const int CV_MISSING = 0x16; internal const int CV_NULL = 0x17; @@ -81,7 +82,7 @@ internal partial struct Variant internal static Variant DBNull => new Variant(CV_NULL, System.DBNull.Value, 0); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe bool IsSystemDrawingColor(MethodTable* pMT); + internal static extern unsafe bool IsSystemDrawingColor(MethodTable* pMT); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] private static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); From f12f8f6b78fc942e32b45c83b357aff2cecf0877 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 25 Mar 2024 22:47:33 +0800 Subject: [PATCH 09/35] Avoid converting through Variant in OAVariantLib --- .../src/Microsoft/Win32/OAVariantLib.cs | 167 ++++++------------ .../src/System/OleAutBinder.cs | 3 +- 2 files changed, 56 insertions(+), 114 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 3d190d5b701dd5..032945ceb351b9 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -13,9 +13,9 @@ ===========================================================*/ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -33,35 +33,29 @@ internal static unsafe partial class OAVariantLib public const int CalendarHijri = 0x08; public const int LocalBool = 0x10; - internal static readonly Type?[] ClassTypes = { - typeof(Empty), - typeof(void), - typeof(bool), - typeof(char), - typeof(sbyte), - typeof(byte), - typeof(short), - typeof(ushort), - typeof(int), - typeof(uint), - typeof(long), - typeof(ulong), - typeof(float), - typeof(double), - typeof(string), - typeof(void), - typeof(DateTime), - typeof(TimeSpan), - typeof(object), - typeof(decimal), - null, // Enums - what do we do here? - typeof(Missing), - typeof(DBNull), + private static readonly Dictionary ClassTypes = new Dictionary + { + { typeof(Empty), VarEnum.VT_EMPTY }, + { typeof(void), VarEnum.VT_VOID }, + { typeof(bool), VarEnum.VT_BOOL }, + { typeof(char), VarEnum.VT_I2 }, + { typeof(sbyte), VarEnum.VT_I1 }, + { typeof(byte), VarEnum.VT_UI1 }, + { typeof(short), VarEnum.VT_I2 }, + { typeof(ushort), VarEnum.VT_UI2 }, + { typeof(int), VarEnum.VT_I4 }, + { typeof(uint), VarEnum.VT_UI4 }, + { typeof(long), VarEnum.VT_I8 }, + { typeof(ulong), VarEnum.VT_UI8 }, + { typeof(float), VarEnum.VT_R4 }, + { typeof(double), VarEnum.VT_R8 }, + { typeof(string), VarEnum.VT_BSTR }, + { typeof(DateTime), VarEnum.VT_DATE }, + { typeof(object), VarEnum.VT_UNKNOWN }, + { typeof(decimal), VarEnum.VT_DECIMAL }, + { typeof(DBNull), VarEnum.VT_NULL }, }; - // Keep these numbers in sync w/ the above array. - private const int CV_OBJECT = 0x12; - #endregion @@ -76,77 +70,44 @@ internal static unsafe partial class OAVariantLib * Variant and the types that CLR supports explicitly in the * CLR Variant class. */ - internal static Variant ChangeType(Variant source, Type targetClass, short options, CultureInfo culture) + internal static object ChangeType(object source, Type targetClass, short options, CultureInfo culture) { ArgumentNullException.ThrowIfNull(targetClass); ArgumentNullException.ThrowIfNull(culture); - Variant result = default; - bool converted = false; + object? result = null; TypeHandle th = ((RuntimeType)targetClass).GetNativeTypeHandle(); Debug.Assert(!th.IsTypeDesc); if (Variant.IsSystemDrawingColor(th.AsMethodTable())) { - if (source.CVType is Variant.CV_I4 or Variant.CV_U4) + if (source.GetType() == typeof(int) || source.GetType() == typeof(uint)) { + uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color - object? ret = null; - ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref ret), (uint)source._data); - Debug.Assert(ret != null); - result._objref = ret; - result._flags = Variant.CV_OBJECT; - - converted = true; + ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData); + Debug.Assert(result != null); + return result; } } - if (!converted) + VarEnum vt = GetVTFromClass(targetClass); + + ComVariant vOp = ToOAVariant(source); + ComVariant ret = default; + + int hr = VariantChangeTypeEx(ref ret, ref vOp, culture.LCID, options, (ushort)vt); + + using (vOp) + using (ret) { - VarEnum vt = source.CVType switch - { - Variant.CV_EMPTY => VarEnum.VT_EMPTY, - Variant.CV_VOID => VarEnum.VT_VOID, - Variant.CV_BOOLEAN => VarEnum.VT_BOOL, - Variant.CV_CHAR => VarEnum.VT_UI2, - Variant.CV_I1 => VarEnum.VT_I1, - Variant.CV_U1 => VarEnum.VT_UI1, - Variant.CV_I2 => VarEnum.VT_I2, - Variant.CV_U2 => VarEnum.VT_UI2, - Variant.CV_I4 => VarEnum.VT_I4, - Variant.CV_U4 => VarEnum.VT_UI4, - Variant.CV_I8 => VarEnum.VT_UI8, - Variant.CV_R4 => VarEnum.VT_R4, - Variant.CV_R8 => VarEnum.VT_R8, - Variant.CV_STRING => VarEnum.VT_BSTR, - // Variant.CV_PTR => INVALID_MAPPING, - Variant.CV_DATETIME => VarEnum.VT_DATE, - // Variant.CV_TIMESPAN => INVALID_MAPPING, - Variant.CV_OBJECT => VarEnum.VT_UNKNOWN, - Variant.CV_DECIMAL => VarEnum.VT_DECIMAL, - Variant.CV_CURRENCY => VarEnum.VT_CY, - // Variant.CV_ENUM => INVALID_MAPPING, - // Variant.CV_MISSING => INVALID_MAPPING, - Variant.CV_NULL => VarEnum.VT_NULL, - _ => throw new NotSupportedException(SR.NotSupported_ChangeType) - }; - - ComVariant vOp = ToOAVariant(source); - ComVariant ret = default; - - int hr = VariantChangeTypeEx(ref ret, ref vOp, culture.LCID, options, (ushort)vt); - - using (vOp) - using (ret) + if (hr < 0) + OAFailed(hr); + + result = FromOAVariant(ret); + if (targetClass == typeof(char)) { - if (hr < 0) - OAFailed(hr); - - result = FromOAVariant(ret); - if (source.CVType == Variant.CV_CHAR) - { - result._flags = Variant.CV_CHAR; - } + result = (char)(uint)result; } } @@ -175,16 +136,24 @@ private static void OAFailed(int hr) } } - private static ComVariant ToOAVariant(Variant input) + private static ComVariant ToOAVariant(object input) { } - private static Variant FromOAVariant(ComVariant input) + private static object FromOAVariant(ComVariant input) { } + private static VarEnum GetVTFromClass(Type type) + { + if (ClassTypes.TryGetValue(type, out VarEnum vt)) + return vt; + + throw new NotSupportedException(SR.NotSupported_ChangeType); + } + [LibraryImport(Interop.Libraries.OleAut32)] private static partial int VariantChangeTypeEx(ref ComVariant pVarRes, ref ComVariant pVarSrc, int lcid, short wFlags, ushort vt); @@ -196,32 +165,6 @@ private static Variant FromOAVariant(ComVariant input) #pragma warning restore CS8500 - #endregion - - - #region Private Helpers - - private static int GetCVTypeFromClass(Type ctype) - { - Debug.Assert(ctype != null); - Debug.Assert(ClassTypes[CV_OBJECT] == typeof(object), "OAVariantLib::ClassTypes[CV_OBJECT] == Object.class"); - - // OleAut Binder works better if unrecognized - // types were changed to Object. - int cvtype = CV_OBJECT; - - for (int i = 0; i < ClassTypes.Length; i++) - { - if (ctype.Equals(ClassTypes[i])) - { - cvtype = i; - break; - } - } - - return cvtype; - } - - #endregion +#endregion } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs index d1072ff86ec8a4..bfbf717112c721 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs @@ -18,7 +18,6 @@ internal sealed class OleAutBinder : DefaultBinder // This binder uses OLEAUT to change the type of the variant. public override object ChangeType(object value, Type type, CultureInfo? cultureInfo) { - Variant myValue = new Variant(value); cultureInfo ??= CultureInfo.CurrentCulture; #if DISPLAY_DEBUG_INFO @@ -62,7 +61,7 @@ public override object ChangeType(object value, Type type, CultureInfo? cultureI #endif // Specify the LocalBool flag to have BOOL values converted to local language rather // than 0 or -1. - object RetObj = OAVariantLib.ChangeType(myValue, type, OAVariantLib.LocalBool, cultureInfo).ToObject()!; + object RetObj = OAVariantLib.ChangeType(value, type, OAVariantLib.LocalBool, cultureInfo); #if DISPLAY_DEBUG_INFO Console.WriteLine("Object returned from ChangeType is of type: " + RetObj.GetType().Name); From f2c96e2902b0f003b881c791f939853eb965e939 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 14:47:40 +0800 Subject: [PATCH 10/35] Managed ToOAVariant and FromOAVariant --- .../src/Microsoft/Win32/OAVariantLib.cs | 52 +++++++++++++++++-- .../src/System/Variant.cs | 2 +- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 032945ceb351b9..3b2a66d216125e 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -138,13 +139,54 @@ private static void OAFailed(int hr) private static ComVariant ToOAVariant(object input) { - + return input switch + { + string str => ComVariant.Create(str), + char ch => ComVariant.Create(ch.ToString()), // We should override the VTtoVT default of VT_UI2 for this case. + DateTime dateTime => ComVariant.Create(dateTime), + bool b => ComVariant.Create(b), + decimal d => ComVariant.Create(d), + sbyte i1 => ComVariant.Create(i1), + byte u1 => ComVariant.Create(u1), + short i2 => ComVariant.Create(i2), + ushort u2 => ComVariant.Create(u2), + int i4 => ComVariant.Create(i4), + uint u4 => ComVariant.Create(u4), + long i8 => ComVariant.Create(i8), + ulong u8 => ComVariant.Create(u8), + float r4 => ComVariant.Create(r4), + double r8 => ComVariant.Create(r8), + null => default, + Missing missing => ComVariant.Create(missing), + DBNull => ComVariant.Null, + _ when Variant.IsSystemDrawingColor(RuntimeHelpers.GetMethodTable(input)) + => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), + _ => throw new NotImplementedException() // Convert the object to an IDispatch/IUnknown pointer. + }; } - private static object FromOAVariant(ComVariant input) - { - - } + private static object FromOAVariant(ComVariant input) => + input.VarType switch + { + VarEnum.VT_BSTR => input.As()!, + VarEnum.VT_DATE => input.As()!, + VarEnum.VT_BOOL => input.As()!, + VarEnum.VT_DECIMAL => input.As()!, + VarEnum.VT_I1 => input.As()!, + VarEnum.VT_UI1 => input.As()!, + VarEnum.VT_I2 => input.As()!, + VarEnum.VT_UI2 => input.As()!, + VarEnum.VT_I4 => input.As()!, + VarEnum.VT_UI4 => input.As()!, + VarEnum.VT_I8 => input.As()!, + VarEnum.VT_UI8 => input.As()!, + VarEnum.VT_R4 => input.As()!, + VarEnum.VT_R8 => input.As()!, + VarEnum.VT_EMPTY => null!, + VarEnum.VT_NULL => DBNull.Value, + VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => throw new NotImplementedException(), // Convert the IUnknown pointer to an OBJECTREF. + _ => throw new NotSupportedException(SR.NotSupported_ChangeType), + }; private static VarEnum GetVTFromClass(Type type) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 1cd441a1cd4d53..ea5878ab6f911f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -85,7 +85,7 @@ internal partial struct Variant internal static extern unsafe bool IsSystemDrawingColor(MethodTable* pMT); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] - private static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); + internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); internal unsafe void SetFieldsObject(object val) { From 9787743ea478af300969e74756cf2ae2a8ddee63 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 21:57:32 +0800 Subject: [PATCH 11/35] Marshal for IUnknown and IDispatch --- .../src/Microsoft/Win32/OAVariantLib.cs | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 3b2a66d216125e..5e410ce863091d 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -161,10 +161,16 @@ private static ComVariant ToOAVariant(object input) DBNull => ComVariant.Null, _ when Variant.IsSystemDrawingColor(RuntimeHelpers.GetMethodTable(input)) => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), - _ => throw new NotImplementedException() // Convert the object to an IDispatch/IUnknown pointer. + _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; } + private static ComVariant GetComIPFromObjectRef(object obj) + { + IntPtr pUnk = GetComIPFromObjectRef(ObjectHandleOnStack.Create(ref obj), ComIpType.ComIpType_Both, out ComIpType FetchedIpType); + return ComVariant.CreateRaw(FetchedIpType == ComIpType.ComIpType_Dispatch ? VarEnum.VT_DISPATCH : VarEnum.VT_UNKNOWN, pUnk); + } + private static object FromOAVariant(ComVariant input) => input.VarType switch { @@ -184,10 +190,18 @@ private static object FromOAVariant(ComVariant input) => VarEnum.VT_R8 => input.As()!, VarEnum.VT_EMPTY => null!, VarEnum.VT_NULL => DBNull.Value, - VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => throw new NotImplementedException(), // Convert the IUnknown pointer to an OBJECTREF. + VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => GetObjectRefFromComIP(input), // Convert the IUnknown pointer to an OBJECTREF. _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; + private static object GetObjectRefFromComIP(ComVariant variant) + { + object? ret = null; + GetObjectRefFromComIP(ObjectHandleOnStack.Create(ref ret), variant.GetRawDataRef()); + Debug.Assert(ret != null); + return ret; + } + private static VarEnum GetVTFromClass(Type type) { if (ClassTypes.TryGetValue(type, out VarEnum vt)) @@ -205,8 +219,23 @@ private static VarEnum GetVTFromClass(Type type) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_ChangeType")] private static partial void ChangeType(Variant* result, Variant* source, int lcid, IntPtr typeHandle, int cvType, short flags); + [DllImport("Foo")] + private static extern IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); + + [DllImport("Foo")] + private static extern void GetObjectRefFromComIP(ObjectHandleOnStack objRet, IntPtr pUnk); + #pragma warning restore CS8500 -#endregion + #endregion + } + + internal enum ComIpType : int + { + ComIpType_None = 0x0, + ComIpType_Unknown = 0x1, + ComIpType_Dispatch = 0x2, + ComIpType_Both = 0x3, + ComIpType_OuterUnknown = 0x5, } } From 216486510eeb68350d66c59ef8b955f50256eab0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 22:09:09 +0800 Subject: [PATCH 12/35] Fullfill interop for OAVariant --- .../src/Microsoft/Win32/OAVariantLib.cs | 15 +- .../classlibnative/bcltype/oavariant.cpp | 401 +----------------- .../classlibnative/bcltype/oavariant.h | 3 +- src/coreclr/vm/qcallentrypoints.cpp | 3 +- 4 files changed, 21 insertions(+), 401 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 5e410ce863091d..37ccab99e5b095 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -62,8 +62,6 @@ internal static unsafe partial class OAVariantLib #region Internal Methods -#pragma warning disable CS8500 - /** * Changes a Variant from one type to another, calling the OLE * Automation VariantChangeTypeEx routine. Note the legal types here are @@ -216,16 +214,11 @@ private static VarEnum GetVTFromClass(Type type) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_ChangeType")] - private static partial void ChangeType(Variant* result, Variant* source, int lcid, IntPtr typeHandle, int cvType, short flags); - - [DllImport("Foo")] - private static extern IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); - - [DllImport("Foo")] - private static extern void GetObjectRefFromComIP(ObjectHandleOnStack objRet, IntPtr pUnk); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetComIPFromObjectRef")] + private static partial IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); -#pragma warning restore CS8500 + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetObjectRefFromComIP")] + private static partial void GetObjectRefFromComIP(ObjectHandleOnStack objRet, IntPtr pUnk); #endregion } diff --git a/src/coreclr/classlibnative/bcltype/oavariant.cpp b/src/coreclr/classlibnative/bcltype/oavariant.cpp index ac8c55f359d809..7919e80e5d2d73 100644 --- a/src/coreclr/classlibnative/bcltype/oavariant.cpp +++ b/src/coreclr/classlibnative/bcltype/oavariant.cpp @@ -9,410 +9,35 @@ #ifdef FEATURE_COMINTEROP #include -#include "excep.h" #include "oavariant.h" -#include "comdatetime.h" // DateTime <-> OleAut date conversions -#include "interoputil.h" #include "interopconverter.h" -#include "excep.h" -#include "string.h" -#include "comutilnative.h" // for COMDate -#define INVALID_MAPPING (BYTE)(-1) - -static const BYTE CVtoVTTable [] = -{ - VT_EMPTY, // CV_EMPTY - VT_VOID, // CV_VOID - VT_BOOL, // CV_BOOLEAN - VT_UI2, // CV_CHAR - VT_I1, // CV_I1 - VT_UI1, // CV_U1 - VT_I2, // CV_I2 - VT_UI2, // CV_U2 - VT_I4, // CV_I4 - VT_UI4, // CV_U4 - VT_I8, // CV_I8 - VT_UI8, // CV_U8 - VT_R4, // CV_R4 - VT_R8, // CV_R8 - VT_BSTR, // CV_STRING - INVALID_MAPPING, // CV_PTR - VT_DATE, // CV_DATETIME - INVALID_MAPPING, // CV_TIMESPAN - VT_UNKNOWN, // CV_OBJECT - VT_DECIMAL, // CV_DECIMAL - VT_CY, // CV_CURRENCY - INVALID_MAPPING, // CV_ENUM - INVALID_MAPPING, // CV_MISSING - VT_NULL, // CV_NULL - INVALID_MAPPING // CV_LAST -}; - -static const BYTE VTtoCVTable[] = -{ - CV_EMPTY, // VT_EMPTY - CV_NULL, // VT_NULL - CV_I2, // VT_I2 - CV_I4, // VT_I4 - CV_R4, // VT_R4 - CV_R8, // VT_R8 - CV_CURRENCY,// VT_CY - CV_DATETIME,// VT_DATE - CV_STRING, // VT_BSTR - INVALID_MAPPING, // VT_DISPATCH - INVALID_MAPPING, // VT_ERROR - CV_BOOLEAN, // VT_BOOL - CV_OBJECT, // VT_VARIANT - CV_OBJECT, // VT_UNKNOWN - CV_DECIMAL, // VT_DECIMAL - INVALID_MAPPING, // An unused enum table entry - CV_I1, // VT_I1 - CV_U1, // VT_UI1 - CV_U2, // VT_UI2 - CV_U4, // VT_UI4 - CV_I8, // VT_I8 - CV_U8, // VT_UI8 - CV_I4, // VT_INT - CV_U4, // VT_UINT - CV_VOID // VT_VOID -}; - -// Need translations from CVType to VARENUM and vice versa. CVTypes -// is defined in olevariant.h. VARENUM is defined in OleAut's variant.h -// Assumption here is we will only deal with VARIANTs and not other OLE -// constructs such as property sets or safe arrays. -static VARENUM CVtoVT(const CVTypes cv) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(cv >= 0 && cv < CV_LAST); - } - CONTRACTL_END; - - if (CVtoVTTable[cv] == INVALID_MAPPING) - COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType")); - - return (VARENUM) CVtoVTTable[cv]; -} - -// Need translations from CVType to VARENUM and vice versa. CVTypes -// is defined in olevariant.h. VARENUM is defined in OleAut's variant.h -static CVTypes VTtoCV(const VARENUM vt) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(vt < VT_VOID); - } - CONTRACTL_END; - - if (vt <0 || vt > VT_VOID || VTtoCVTable[vt]==INVALID_MAPPING) - COMPlusThrow(kNotSupportedException, W("NotSupported_ChangeType")); - - return (CVTypes) VTtoCVTable[vt]; -} - - -// Converts a COM+ Variant to an OleAut Variant. Returns true if -// there was a native object allocated by this method that must be freed, -// else false. -static bool ToOAVariant(VariantData const* src, VARIANT* oa) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(src)); - PRECONDITION(CheckPointer(oa)); - } - CONTRACTL_END; - - SafeVariantInit(oa); - UINT64 * dest = (UINT64*) &V_UI1(oa); - *dest = 0; - - WCHAR * chars; - int strLen; - - // Set the data field of the OA Variant to be either the object reference - // or the data (ie int) that it needs. - - switch (src->GetType()) - { - case CV_STRING: - if (src->GetObjRef() == NULL) - { - V_BSTR(oa) = NULL; - V_VT(oa) = static_cast(CVtoVT(src->GetType())); - - // OA perf feature: VarClear calls SysFreeString(null), which access violates. - return false; - } - - ((STRINGREF) (src->GetObjRef()))->RefInterpretGetStringValuesDangerousForGC(&chars, &strLen); - V_BSTR(oa) = SysAllocStringLen(chars, strLen); - if (V_BSTR(oa) == NULL) - COMPlusThrowOM(); - - V_VT(oa) = static_cast(CVtoVT(src->GetType())); - - return true; - - case CV_CHAR: - chars = (WCHAR*)src->GetData(); - V_BSTR(oa) = SysAllocStringLen(chars, 1); - if (V_BSTR(oa) == NULL) - COMPlusThrowOM(); - - // We should override the VTtoVT default of VT_UI2 for this case. - V_VT(oa) = VT_BSTR; - - return true; - - case CV_DATETIME: - V_DATE(oa) = COMDateTime::TicksToDoubleDate(src->GetDataAsInt64()); - V_VT(oa) = static_cast(CVtoVT(src->GetType())); - return false; - - case CV_BOOLEAN: - V_BOOL(oa) = (src->GetDataAsInt64()==0 ? VARIANT_FALSE : VARIANT_TRUE); - V_VT(oa) = static_cast(CVtoVT(src->GetType())); - return false; - - case CV_DECIMAL: - { - OBJECTREF obj = src->GetObjRef(); - DECIMAL * d = (DECIMAL*) obj->GetData(); - // DECIMALs and Variants are the same size. Variants are a union between - // all the normal Variant fields (vt, bval, etc) and a Decimal. Decimals - // also have the first 2 bytes reserved, for a VT field. - - V_DECIMAL(oa) = *d; - V_VT(oa) = VT_DECIMAL; - return false; - } - - case CV_OBJECT: - { - OBJECTREF obj = src->GetObjRef(); - GCPROTECT_BEGIN(obj) - { - IUnknown *pUnk = NULL; - - // Convert the object to an IDispatch/IUnknown pointer. - ComIpType FetchedIpType = ComIpType_None; - pUnk = GetComIPFromObjectRef(&obj, ComIpType_Both, &FetchedIpType); - V_UNKNOWN(oa) = pUnk; - V_VT(oa) = static_cast(FetchedIpType == ComIpType_Dispatch ? VT_DISPATCH : VT_UNKNOWN); - } - GCPROTECT_END(); - return true; - } - - default: - *dest = src->GetDataAsInt64(); - V_VT(oa) = static_cast(CVtoVT(src->GetType())); - return false; - } -} - -// Converts an OleAut Variant into a COM+ Variant. -// Note that we pass the VariantData Byref so that if GC happens, 'var' gets updated -static void FromOAVariant(VARIANT const* src, VariantData*& var) +extern "C" IUnknown* QCALLTYPE OAVariant_GetComIPFromObjectRef(QCall::ObjectHandleOnStack obj, ComIpType reqIPType, ComIpType* fetchedIpType) { - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(src)); - } - CONTRACTL_END; - - // Clear the return variant value. It's allocated on - // the stack and we only want valid state data in there. - memset(var, 0, sizeof(VariantData)); - - CVTypes type = VTtoCV((VARENUM) V_VT(src)); - var->SetType(type); - - switch (type) - { - case CV_STRING: - { - // BSTRs have an int with the string buffer length (not the string length) - // followed by the data. The pointer to the BSTR points to the start of the - // characters, NOT the start of the BSTR. - WCHAR* chars = V_BSTR(src); - int strLen = SysStringLen(V_BSTR(src)); - STRINGREF str = StringObject::NewString(chars, strLen); - var->SetObjRef((OBJECTREF)str); - break; - } - case CV_DATETIME: - var->SetDataAsInt64(COMDateTime::DoubleDateToTicks(V_DATE(src))); - break; - - case CV_BOOLEAN: - var->SetDataAsInt64(V_BOOL(src) == VARIANT_FALSE ? FALSE : TRUE); - break; - - case CV_DECIMAL: - { - MethodTable * pDecimalMT = GetTypeHandleForCVType(CV_DECIMAL).GetMethodTable(); - _ASSERTE(pDecimalMT); - OBJECTREF pDecimalRef = AllocateObject(pDecimalMT); + QCALL_CONTRACT; - *(DECIMAL *) pDecimalRef->GetData() = V_DECIMAL(src); - var->SetObjRef(pDecimalRef); - break; - } + IUnknown* ret = nullptr; - // All types less than 4 bytes need an explicit cast from their original - // type to be sign extended to 8 bytes. This makes Variant's ToInt32 - // function simpler for these types. - case CV_I1: - var->SetDataAsInt64(V_I1(src)); - break; - - case CV_U1: - var->SetDataAsInt64(V_UI1(src)); - break; - - case CV_I2: - var->SetDataAsInt64(V_I2(src)); - break; - - case CV_U2: - var->SetDataAsInt64(V_UI2(src)); - break; - - case CV_EMPTY: - case CV_NULL: - // Must set up the Variant's m_or to the appropriate classes. - // Note that OleAut doesn't have any VT_MISSING. - VariantData::NewVariant(var, type, NULL DEBUG_ARG(TRUE)); - break; - - case CV_OBJECT: - { - // Convert the IUnknown pointer to an OBJECTREF. - OBJECTREF oref = NULL; - GCPROTECT_BEGIN(oref); - GetObjectRefFromComIP(&oref, V_UNKNOWN(src)); - var->SetObjRef(oref); - GCPROTECT_END(); - break; - } - default: - // Copy all the bits there, and make sure we don't do any float to int conversions. - void* data = (void*)&(V_UI1(src)); - var->SetData(data); - break; - } -} - -static void OAFailed(HRESULT hr) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(FAILED(hr)); - } - CONTRACTL_END; - - switch (hr) - { - case E_OUTOFMEMORY: - COMPlusThrowOM(); - - case DISP_E_BADVARTYPE: - COMPlusThrow(kNotSupportedException, W("NotSupported_OleAutBadVarType")); - - case DISP_E_DIVBYZERO: - COMPlusThrow(kDivideByZeroException); - - case DISP_E_OVERFLOW: - COMPlusThrow(kOverflowException); + BEGIN_QCALL; - case DISP_E_TYPEMISMATCH: - COMPlusThrow(kInvalidCastException, W("InvalidCast_OATypeMismatch")); + GCX_COOP(); + OBJECTREF objectRef = obj.Get(); + ret = GetComIPFromObjectRef(&objectRef, reqIPType, fetchedIpType); - case E_INVALIDARG: - COMPlusThrow(kArgumentException); + END_QCALL; - default: - _ASSERTE(!"Unrecognized HResult - OAVariantLib routine failed in an unexpected way!"); - COMPlusThrowHR(hr); - } + return ret; } -extern "C" void QCALLTYPE OAVariant_ChangeType(VariantData* result, VariantData* source, LCID lcid, void* targetType, int cvType, INT16 flags) +extern "C" void* QCALLTYPE OAVariant_GetObjectRefFromComIP(QCall::ObjectHandleOnStack objRet, IUnknown* pUnk) { - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(CheckPointer(result)); - } - CONTRACTL_END; + QCALL_CONTRACT; BEGIN_QCALL; GCX_COOP(); - - bool converted = false; - - TypeHandle thTarget = TypeHandle::FromPtr(targetType); - if (cvType == CV_OBJECT && IsTypeRefOrDef(g_ColorClassName, thTarget.GetModule(), thTarget.GetCl())) - { - CVTypes sourceType = source->GetType(); - if (sourceType == CV_I4 || sourceType == CV_U4) - { - // Int32/UInt32 can be converted to System.Drawing.Color - SYSTEMCOLOR SystemColor; - ConvertOleColorToSystemColor(source->GetDataAsUInt32(), &SystemColor); - - result->SetObjRef(thTarget.AsMethodTable()->Box(&SystemColor)); - result->SetType(CV_OBJECT); - - converted = true; - } - } - - if (!converted) - { - VariantHolder ret; - VariantHolder vOp; - - VARENUM vt = CVtoVT((CVTypes) cvType); - ToOAVariant(source, &vOp); - - HRESULT hr = SafeVariantChangeTypeEx(&ret, &vOp, lcid, flags, static_cast(vt)); - - if (FAILED(hr)) - OAFailed(hr); - - if ((CVTypes) cvType == CV_CHAR) - { - result->SetType(CV_CHAR); - result->SetDataAsUInt16(V_UI2(&ret)); - } - else - { - FromOAVariant(&ret, result); - } - } + OBJECTREF objectRef = NULL; + GetObjectRefFromComIP(&objectRef, pUnk); END_QCALL; } diff --git a/src/coreclr/classlibnative/bcltype/oavariant.h b/src/coreclr/classlibnative/bcltype/oavariant.h index 2ee4de551b693c..1be5b23427d58f 100644 --- a/src/coreclr/classlibnative/bcltype/oavariant.h +++ b/src/coreclr/classlibnative/bcltype/oavariant.h @@ -13,6 +13,7 @@ #include "olevariant.h" -extern "C" void QCALLTYPE OAVariant_ChangeType(VariantData* result, VariantData* source, LCID lcid, void* targetType, int cvType, INT16 flags); +extern "C" IUnknown* QCALLTYPE OAVariant_GetComIPFromObjectRef(QCall::ObjectHandleOnStack obj, ComIpType reqIPType, ComIpType* fetchedIpType); +extern "C" void* QCALLTYPE OAVariant_GetObjectRefFromComIP(QCall::ObjectHandleOnStack objRet, IUnknown* pUnk); #endif // _OAVARIANT_H_ diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index b6663dfc4db375..b62cb4ab815d35 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -315,7 +315,8 @@ static const Entry s_QCall[] = DllImportEntry(MngdSafeArrayMarshaler_ConvertSpaceToManaged) DllImportEntry(MngdSafeArrayMarshaler_ConvertContentsToManaged) DllImportEntry(MngdSafeArrayMarshaler_ClearNative) - DllImportEntry(OAVariant_ChangeType) + DllImportEntry(OAVariant_GetComIPFromObjectRef) + DllImportEntry(OAVariant_GetObjectRefFromComIP) DllImportEntry(Variant_ConvertSystemColorToOleColor) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) From aa963e045c624ed0815ea3d681ed5b938891dc7a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 22:23:23 +0800 Subject: [PATCH 13/35] VariantChangeTypeEx interop --- .../src/Microsoft/Win32/OAVariantLib.cs | 5 +---- .../OleAut32/Interop.VariantChangeTypeEx.cs | 14 ++++++++++++++ .../src/System.Private.CoreLib.Shared.projitems | 3 +++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/OleAut32/Interop.VariantChangeTypeEx.cs diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 37ccab99e5b095..68047b1eb0f0c3 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -95,7 +95,7 @@ internal static object ChangeType(object source, Type targetClass, short options ComVariant vOp = ToOAVariant(source); ComVariant ret = default; - int hr = VariantChangeTypeEx(ref ret, ref vOp, culture.LCID, options, (ushort)vt); + int hr = Interop.OleAut32.VariantChangeTypeEx(&ret, &vOp, culture.LCID, options, (ushort)vt); using (vOp) using (ret) @@ -208,9 +208,6 @@ private static VarEnum GetVTFromClass(Type type) throw new NotSupportedException(SR.NotSupported_ChangeType); } - [LibraryImport(Interop.Libraries.OleAut32)] - private static partial int VariantChangeTypeEx(ref ComVariant pVarRes, ref ComVariant pVarSrc, int lcid, short wFlags, ushort vt); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value); diff --git a/src/libraries/Common/src/Interop/Windows/OleAut32/Interop.VariantChangeTypeEx.cs b/src/libraries/Common/src/Interop/Windows/OleAut32/Interop.VariantChangeTypeEx.cs new file mode 100644 index 00000000000000..edb77c858b3813 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/OleAut32/Interop.VariantChangeTypeEx.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class OleAut32 + { + [LibraryImport(Libraries.OleAut32)] + internal static unsafe partial int VariantChangeTypeEx(void* pVarRes, void* pVarSrc, int lcid, short wFlags, ushort vt); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index ff26f3bb27c8f5..823b7eb498a62a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2067,6 +2067,9 @@ Common\Interop\Windows\OleAut32\Interop.SysFreeString.cs + + Common\Interop\Windows\OleAut32\Interop.VariantChangeTypeEx.cs + Common\Interop\Windows\Ole32\Interop.CLSIDFromProgID.cs From 568ba08bec4f08f1343876ab06ae732578d52a9d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 22:34:15 +0800 Subject: [PATCH 14/35] Complete GetObjectRefFromComIP --- src/coreclr/classlibnative/bcltype/oavariant.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/classlibnative/bcltype/oavariant.cpp b/src/coreclr/classlibnative/bcltype/oavariant.cpp index 7919e80e5d2d73..f46d6b2aa987ef 100644 --- a/src/coreclr/classlibnative/bcltype/oavariant.cpp +++ b/src/coreclr/classlibnative/bcltype/oavariant.cpp @@ -38,6 +38,7 @@ extern "C" void* QCALLTYPE OAVariant_GetObjectRefFromComIP(QCall::ObjectHandleOn GCX_COOP(); OBJECTREF objectRef = NULL; GetObjectRefFromComIP(&objectRef, pUnk); + objRet.Set(objectRef); END_QCALL; } From e6338a13ac7f207bfaf9ca353b0d93fb58ef66df Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 26 Mar 2024 22:40:38 +0800 Subject: [PATCH 15/35] System.Color interop --- .../src/Microsoft/Win32/OAVariantLib.cs | 10 ++++----- .../src/System/Variant.cs | 5 ++--- .../classlibnative/bcltype/variant.cpp | 22 ++++++++++++------- src/coreclr/classlibnative/bcltype/variant.h | 7 +----- src/coreclr/vm/ecalllist.h | 9 -------- src/coreclr/vm/qcallentrypoints.cpp | 1 + 6 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 68047b1eb0f0c3..9c8db94a7c0739 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -76,15 +76,13 @@ internal static object ChangeType(object source, Type targetClass, short options object? result = null; - TypeHandle th = ((RuntimeType)targetClass).GetNativeTypeHandle(); - Debug.Assert(!th.IsTypeDesc); - if (Variant.IsSystemDrawingColor(th.AsMethodTable())) + if (Variant.IsSystemDrawingColor(targetClass)) { if (source.GetType() == typeof(int) || source.GetType() == typeof(uint)) { uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color - ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData); + ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); Debug.Assert(result != null); return result; } @@ -157,7 +155,7 @@ private static ComVariant ToOAVariant(object input) null => default, Missing missing => ComVariant.Create(missing), DBNull => ComVariant.Null, - _ when Variant.IsSystemDrawingColor(RuntimeHelpers.GetMethodTable(input)) + _ when Variant.IsSystemDrawingColor(input.GetType()) => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; @@ -209,7 +207,7 @@ private static VarEnum GetVTFromClass(Type type) } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] - private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value); + private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value, IntPtr pMT); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetComIPFromObjectRef")] private static partial IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index ea5878ab6f911f..04f8e7758a8b0f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -81,8 +81,7 @@ internal partial struct Variant internal static Variant Missing => new Variant(CV_MISSING, Type.Missing, 0); internal static Variant DBNull => new Variant(CV_NULL, System.DBNull.Value, 0); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe bool IsSystemDrawingColor(MethodTable* pMT); + internal static bool IsSystemDrawingColor(Type type) => type.Name == "System.Drawing.Color"; // Matches the behavior of IsTypeRefOrDef [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); @@ -112,7 +111,7 @@ internal unsafe void SetFieldsObject(object val) _flags = CV_OBJECT; } } - else if (IsSystemDrawingColor(pMT)) + else if (IsSystemDrawingColor(val.GetType())) { // System.Drawing.Color is converted to UInt32 object obj = val; diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index 652679fbb21c3e..351f999d02d10f 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -21,14 +21,6 @@ #include "variant.h" #include "string.h" -FCIMPL1(CLR_BOOL, COMVariant::IsSystemDrawingColor, MethodTable* valMT) -{ - FCALL_CONTRACT; - - return IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl()); -} -FCIMPLEND - extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj) { QCALL_CONTRACT; @@ -46,4 +38,18 @@ extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::Object return ret; } +extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + GCX_COOP(); + SYSTEMCOLOR SystemColor = {}; + ConvertOleColorToSystemColor(oleColor, &SystemColor); + objRet.Set(pMT->Box(&SystemColor)); + + END_QCALL; +} + #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h index d0e3a55c0b98d3..037e598ebcca3b 100644 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -21,13 +21,8 @@ #include "fcall.h" #include "olevariant.h" -class COMVariant -{ -public: - static FCDECL1(CLR_BOOL, IsSystemDrawingColor, MethodTable* valMT); -}; - extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); +extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT); #endif // _VARIANT_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 5511867fdc1812..4e461937988a3e 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -358,12 +358,6 @@ FCFuncStart(gWaitHandleFuncs) FCFuncElement("SignalAndWaitNative", WaitHandleNative::CorSignalAndWaitOneNative) FCFuncEnd() -#ifdef FEATURE_COMINTEROP -FCFuncStart(gVariantFuncs) - FCFuncElement("IsSystemDrawingColor", COMVariant::IsSystemDrawingColor) -FCFuncEnd() -#endif // FEATURE_COMINTEROP - FCFuncStart(gCastHelpers) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) @@ -598,9 +592,6 @@ FCClassElement("Thread", "System.Threading", gThreadFuncs) FCClassElement("ThreadPool", "System.Threading", gThreadPoolFuncs) FCClassElement("Type", "System", gSystem_Type) FCClassElement("TypedReference", "System", gTypedReferenceFuncs) -#ifdef FEATURE_COMINTEROP -FCClassElement("Variant", "System", gVariantFuncs) -#endif FCClassElement("WaitHandle", "System.Threading", gWaitHandleFuncs) #undef FCFuncElement diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index b62cb4ab815d35..80cc047724a0c3 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -318,6 +318,7 @@ static const Entry s_QCall[] = DllImportEntry(OAVariant_GetComIPFromObjectRef) DllImportEntry(OAVariant_GetObjectRefFromComIP) DllImportEntry(Variant_ConvertSystemColorToOleColor) + DllImportEntry(Variant_ConvertOleColorToSystemColor) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) DllImportEntry(NativeLibrary_LoadByName) From 6cda5f3cbd528336d5b84005970d77ae610c036a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Mar 2024 12:44:42 +0800 Subject: [PATCH 16/35] Use managed reflection for System.Drawing.Color conversion --- .../src/Microsoft/Win32/OAVariantLib.cs | 7 +-- .../src/System/Variant.cs | 29 ++++++++-- .../classlibnative/bcltype/CMakeLists.txt | 1 - .../classlibnative/bcltype/variant.cpp | 55 ------------------- src/coreclr/classlibnative/bcltype/variant.h | 28 ---------- src/coreclr/vm/corelib.cpp | 1 - src/coreclr/vm/qcallentrypoints.cpp | 3 - 7 files changed, 26 insertions(+), 98 deletions(-) delete mode 100644 src/coreclr/classlibnative/bcltype/variant.cpp delete mode 100644 src/coreclr/classlibnative/bcltype/variant.h diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 9c8db94a7c0739..24b21eeac06576 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -82,7 +82,7 @@ internal static object ChangeType(object source, Type targetClass, short options { uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color - ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); + result = Variant.SystemColorTranslatorAccess.ConvertOleColorToSystemColor(sourceData); Debug.Assert(result != null); return result; } @@ -156,7 +156,7 @@ private static ComVariant ToOAVariant(object input) Missing missing => ComVariant.Create(missing), DBNull => ComVariant.Null, _ when Variant.IsSystemDrawingColor(input.GetType()) - => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), + => ComVariant.Create(Variant.SystemColorTranslatorAccess.ConvertSystemColorToOleColor(input)), _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; } @@ -206,9 +206,6 @@ private static VarEnum GetVTFromClass(Type type) throw new NotSupportedException(SR.NotSupported_ChangeType); } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] - private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value, IntPtr pMT); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetComIPFromObjectRef")] private static partial IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 04f8e7758a8b0f..773b9b511e965e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -83,9 +83,6 @@ internal partial struct Variant internal static bool IsSystemDrawingColor(Type type) => type.Name == "System.Drawing.Color"; // Matches the behavior of IsTypeRefOrDef - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] - internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); - internal unsafe void SetFieldsObject(object val) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(val); @@ -114,8 +111,7 @@ internal unsafe void SetFieldsObject(object val) else if (IsSystemDrawingColor(val.GetType())) { // System.Drawing.Color is converted to UInt32 - object obj = val; - _data = ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref obj)); + _data = SystemColorTranslatorAccess.ConvertSystemColorToOleColor(val); _flags = CV_U4; } else @@ -564,5 +560,28 @@ internal static void MarshalHelperCastVariant(object pValue, int vt, ref Variant }; } } + + internal static class SystemColorTranslatorAccess + { + private static readonly MethodInfo s_toOleMethod; + private static readonly MethodInfo s_fromOleMethod; + +#pragma warning disable CA1810 // Initialize reference type static fields inline + static SystemColorTranslatorAccess() +#pragma warning restore CA1810 + { + // Reflection pattern recognized by trimmer + Type? colorTranslatorType = Type.GetType("System.Drawing.ColorTranslator, System.Drawing"); + Debug.Assert(colorTranslatorType != null); + s_toOleMethod = colorTranslatorType.GetMethod("ToOle", BindingFlags.Public | BindingFlags.Static)!; + s_fromOleMethod = colorTranslatorType.GetMethod("FromOle", BindingFlags.Public | BindingFlags.Static)!; + } + + public static uint ConvertSystemColorToOleColor(object colorObj) + => (uint)(int)s_toOleMethod.Invoke(null, [colorObj])!; + + public static object ConvertOleColorToSystemColor(uint oleColor) + => s_fromOleMethod.Invoke(null, [(int)oleColor])!; + } } } diff --git a/src/coreclr/classlibnative/bcltype/CMakeLists.txt b/src/coreclr/classlibnative/bcltype/CMakeLists.txt index 8e4baf95751e39..7d1d5f07da9654 100644 --- a/src/coreclr/classlibnative/bcltype/CMakeLists.txt +++ b/src/coreclr/classlibnative/bcltype/CMakeLists.txt @@ -6,7 +6,6 @@ set(BCLTYPE_SOURCES objectnative.cpp system.cpp varargsnative.cpp - variant.cpp ) add_library_clr(bcltype OBJECT ${BCLTYPE_SOURCES}) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp deleted file mode 100644 index 351f999d02d10f..00000000000000 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// File: Variant.cpp -// - -// -// Purpose: Native Implementation of the Variant Class -// - -// - -#include "common.h" - -#ifdef FEATURE_COMINTEROP - -#include "object.h" -#include "excep.h" -#include "frames.h" -#include "vars.hpp" -#include "variant.h" -#include "string.h" - -extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj) -{ - QCALL_CONTRACT; - - uint32_t ret = 0; - - BEGIN_QCALL; - - GCX_COOP(); - OBJECTREF srcObj = obj.Get(); - ret = ConvertSystemColorToOleColor(&srcObj); - - END_QCALL; - - return ret; -} - -extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - GCX_COOP(); - SYSTEMCOLOR SystemColor = {}; - ConvertOleColorToSystemColor(oleColor, &SystemColor); - objRet.Set(pMT->Box(&SystemColor)); - - END_QCALL; -} - -#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h deleted file mode 100644 index 037e598ebcca3b..00000000000000 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// File: Variant.h -// - -// -// Purpose: Headers for the Variant class. -// - -// - -#ifndef _VARIANT_H_ -#define _VARIANT_H_ - -#ifndef FEATURE_COMINTEROP -#error FEATURE_COMINTEROP is required for this file -#endif // FEATURE_COMINTEROP - -#include -#include "fcall.h" -#include "olevariant.h" - -extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); -extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT); - -#endif // _VARIANT_H_ - diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp index c0a217593adc5b..1c6c6908e3ab2e 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -46,7 +46,6 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP -#include "variant.h" #include "oavariant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 80cc047724a0c3..a697cf2f8bfba4 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -42,7 +42,6 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP -#include "variant.h" #include "oavariant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP @@ -317,8 +316,6 @@ static const Entry s_QCall[] = DllImportEntry(MngdSafeArrayMarshaler_ClearNative) DllImportEntry(OAVariant_GetComIPFromObjectRef) DllImportEntry(OAVariant_GetObjectRefFromComIP) - DllImportEntry(Variant_ConvertSystemColorToOleColor) - DllImportEntry(Variant_ConvertOleColorToSystemColor) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) DllImportEntry(NativeLibrary_LoadByName) From 80c9f5621c20a623a4476e823d7abc8b3544cd37 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Mar 2024 13:22:07 +0800 Subject: [PATCH 17/35] Use MarshalNative for IDispatch/IUnknown marshalling --- .../src/Microsoft/Win32/OAVariantLib.cs | 34 ++++---------- .../classlibnative/bcltype/CMakeLists.txt | 1 - .../classlibnative/bcltype/oavariant.cpp | 46 ------------------- .../classlibnative/bcltype/oavariant.h | 19 -------- src/coreclr/vm/corelib.cpp | 1 - src/coreclr/vm/marshalnative.cpp | 27 +++++++++++ src/coreclr/vm/marshalnative.h | 5 ++ src/coreclr/vm/qcallentrypoints.cpp | 4 +- 8 files changed, 41 insertions(+), 96 deletions(-) delete mode 100644 src/coreclr/classlibnative/bcltype/oavariant.cpp delete mode 100644 src/coreclr/classlibnative/bcltype/oavariant.h diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 24b21eeac06576..13fd1dfb71aeed 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -21,6 +21,8 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +#pragma warning disable CA1416 // COM interop is only supported on Windows + namespace Microsoft.Win32 { internal static unsafe partial class OAVariantLib @@ -163,10 +165,13 @@ _ when Variant.IsSystemDrawingColor(input.GetType()) private static ComVariant GetComIPFromObjectRef(object obj) { - IntPtr pUnk = GetComIPFromObjectRef(ObjectHandleOnStack.Create(ref obj), ComIpType.ComIpType_Both, out ComIpType FetchedIpType); - return ComVariant.CreateRaw(FetchedIpType == ComIpType.ComIpType_Dispatch ? VarEnum.VT_DISPATCH : VarEnum.VT_UNKNOWN, pUnk); + IntPtr pUnk = GetIUnknownOrIDispatchForObject(ObjectHandleOnStack.Create(ref obj), out bool isIDispatch); + return ComVariant.CreateRaw(isIDispatch ? VarEnum.VT_DISPATCH : VarEnum.VT_UNKNOWN, pUnk); } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIUnknownOrIDispatchForObject")] + private static partial IntPtr GetIUnknownOrIDispatchForObject(ObjectHandleOnStack o, [MarshalAs(UnmanagedType.Bool)] out bool isIDispatch); + private static object FromOAVariant(ComVariant input) => input.VarType switch { @@ -186,18 +191,10 @@ private static object FromOAVariant(ComVariant input) => VarEnum.VT_R8 => input.As()!, VarEnum.VT_EMPTY => null!, VarEnum.VT_NULL => DBNull.Value, - VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => GetObjectRefFromComIP(input), // Convert the IUnknown pointer to an OBJECTREF. + VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => Marshal.GetObjectForIUnknown(input.GetRawDataRef()), // Convert the IUnknown pointer to an OBJECTREF. _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; - private static object GetObjectRefFromComIP(ComVariant variant) - { - object? ret = null; - GetObjectRefFromComIP(ObjectHandleOnStack.Create(ref ret), variant.GetRawDataRef()); - Debug.Assert(ret != null); - return ret; - } - private static VarEnum GetVTFromClass(Type type) { if (ClassTypes.TryGetValue(type, out VarEnum vt)) @@ -206,21 +203,6 @@ private static VarEnum GetVTFromClass(Type type) throw new NotSupportedException(SR.NotSupported_ChangeType); } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetComIPFromObjectRef")] - private static partial IntPtr GetComIPFromObjectRef(ObjectHandleOnStack obj, ComIpType reqIPType, out ComIpType fetchedIpType); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "OAVariant_GetObjectRefFromComIP")] - private static partial void GetObjectRefFromComIP(ObjectHandleOnStack objRet, IntPtr pUnk); - #endregion } - - internal enum ComIpType : int - { - ComIpType_None = 0x0, - ComIpType_Unknown = 0x1, - ComIpType_Dispatch = 0x2, - ComIpType_Both = 0x3, - ComIpType_OuterUnknown = 0x5, - } } diff --git a/src/coreclr/classlibnative/bcltype/CMakeLists.txt b/src/coreclr/classlibnative/bcltype/CMakeLists.txt index 7d1d5f07da9654..dca77043e4149e 100644 --- a/src/coreclr/classlibnative/bcltype/CMakeLists.txt +++ b/src/coreclr/classlibnative/bcltype/CMakeLists.txt @@ -2,7 +2,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(BCLTYPE_SOURCES arraynative.cpp - oavariant.cpp objectnative.cpp system.cpp varargsnative.cpp diff --git a/src/coreclr/classlibnative/bcltype/oavariant.cpp b/src/coreclr/classlibnative/bcltype/oavariant.cpp deleted file mode 100644 index f46d6b2aa987ef..00000000000000 --- a/src/coreclr/classlibnative/bcltype/oavariant.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// File: OAVariant.cpp -// - -#include - -#ifdef FEATURE_COMINTEROP - -#include -#include "oavariant.h" -#include "interopconverter.h" - -extern "C" IUnknown* QCALLTYPE OAVariant_GetComIPFromObjectRef(QCall::ObjectHandleOnStack obj, ComIpType reqIPType, ComIpType* fetchedIpType) -{ - QCALL_CONTRACT; - - IUnknown* ret = nullptr; - - BEGIN_QCALL; - - GCX_COOP(); - OBJECTREF objectRef = obj.Get(); - ret = GetComIPFromObjectRef(&objectRef, reqIPType, fetchedIpType); - - END_QCALL; - - return ret; -} - -extern "C" void* QCALLTYPE OAVariant_GetObjectRefFromComIP(QCall::ObjectHandleOnStack objRet, IUnknown* pUnk) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - GCX_COOP(); - OBJECTREF objectRef = NULL; - GetObjectRefFromComIP(&objectRef, pUnk); - objRet.Set(objectRef); - - END_QCALL; -} - -#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/oavariant.h b/src/coreclr/classlibnative/bcltype/oavariant.h deleted file mode 100644 index 1be5b23427d58f..00000000000000 --- a/src/coreclr/classlibnative/bcltype/oavariant.h +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// File: OAVariant.h -// - -#ifndef _OAVARIANT_H_ -#define _OAVARIANT_H_ - -#ifndef FEATURE_COMINTEROP -#error FEATURE_COMINTEROP is required for this file -#endif // FEATURE_COMINTEROP - -#include "olevariant.h" - -extern "C" IUnknown* QCALLTYPE OAVariant_GetComIPFromObjectRef(QCall::ObjectHandleOnStack obj, ComIpType reqIPType, ComIpType* fetchedIpType); -extern "C" void* QCALLTYPE OAVariant_GetObjectRefFromComIP(QCall::ObjectHandleOnStack objRet, IUnknown* pUnk); - -#endif // _OAVARIANT_H_ diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp index 1c6c6908e3ab2e..54df2a37d7c250 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -46,7 +46,6 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP -#include "oavariant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index e137f51d7a3e2c..46c6314482fce7 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -515,6 +515,33 @@ extern "C" IDispatch* QCALLTYPE MarshalNative_GetIDispatchForObject(QCall::Objec return retVal; } +//==================================================================== +// return the IUnknown* or IDispatch* for an Object. +//==================================================================== +extern "C" void* QCALLTYPE MarshalNative_GetIUnknownOrIDispatchForObject(QCall::ObjectHandleOnStack o, BOOL* isIDispatch) +{ + QCALL_CONTRACT; + + void* retVal = NULL; + + BEGIN_QCALL; + + // Ensure COM is started up. + EnsureComStarted(); + + GCX_COOP(); + + OBJECTREF oref = o.Get(); + GCPROTECT_BEGIN(oref); + ComIpType fetchedIpType = {}; + retVal = GetComIPFromObjectRef(&oref, ComIpType_Both, &fetchedIpType); + *isIDispatch = fetchedIpType == ComIpType_Dispatch; + GCPROTECT_END(); + + END_QCALL; + return retVal; +} + //==================================================================== // return the IUnknown* representing the interface for the Object // Object o should support Type T diff --git a/src/coreclr/vm/marshalnative.h b/src/coreclr/vm/marshalnative.h index f88b689baa5aee..ed9f67c0dc5073 100644 --- a/src/coreclr/vm/marshalnative.h +++ b/src/coreclr/vm/marshalnative.h @@ -77,6 +77,11 @@ extern "C" IUnknown* QCALLTYPE MarshalNative_GetIUnknownForObject(QCall::ObjectH //==================================================================== extern "C" IDispatch* QCALLTYPE MarshalNative_GetIDispatchForObject(QCall::ObjectHandleOnStack o); +//==================================================================== +// return the IUnknown* or IDispatch* for an Object. +//==================================================================== +extern "C" void* QCALLTYPE MarshalNative_GetIUnknownOrIDispatchForObject(QCall::ObjectHandleOnStack o, BOOL* isIDispatch); + //==================================================================== // return the IUnknown* representing the interface for the Object // Object o should support Type T diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index a697cf2f8bfba4..1b2d46f2192c00 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -42,7 +42,6 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP -#include "oavariant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP @@ -281,6 +280,7 @@ static const Entry s_QCall[] = DllImportEntry(MarshalNative_GetTypeFromCLSID) DllImportEntry(MarshalNative_GetIUnknownForObject) DllImportEntry(MarshalNative_GetIDispatchForObject) + DllImportEntry(MarshalNative_GetIUnknownOrIDispatchForObject) DllImportEntry(MarshalNative_GetComInterfaceForObject) DllImportEntry(MarshalNative_GetObjectForIUnknown) DllImportEntry(MarshalNative_GetUniqueObjectForIUnknown) @@ -314,8 +314,6 @@ static const Entry s_QCall[] = DllImportEntry(MngdSafeArrayMarshaler_ConvertSpaceToManaged) DllImportEntry(MngdSafeArrayMarshaler_ConvertContentsToManaged) DllImportEntry(MngdSafeArrayMarshaler_ClearNative) - DllImportEntry(OAVariant_GetComIPFromObjectRef) - DllImportEntry(OAVariant_GetObjectRefFromComIP) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) DllImportEntry(NativeLibrary_LoadByName) From d4addb92a04e10b3bdcb571f5279be11d717590e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Mar 2024 16:40:40 +0800 Subject: [PATCH 18/35] Fulfill behavior of ToOAVariant --- .../src/Microsoft/Win32/OAVariantLib.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 13fd1dfb71aeed..d18ee98805b276 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -90,7 +90,8 @@ internal static object ChangeType(object source, Type targetClass, short options } } - VarEnum vt = GetVTFromClass(targetClass); + if (!ClassTypes.TryGetValue(targetClass, out VarEnum vt)) + throw new NotSupportedException(SR.NotSupported_ChangeType); ComVariant vOp = ToOAVariant(source); ComVariant ret = default; @@ -154,16 +155,24 @@ private static ComVariant ToOAVariant(object input) ulong u8 => ComVariant.Create(u8), float r4 => ComVariant.Create(r4), double r8 => ComVariant.Create(r8), + TimeSpan => throw new NotSupportedException(SR.NotSupported_ChangeType), + Enum => throw new NotSupportedException(SR.NotSupported_ChangeType), null => default, Missing missing => ComVariant.Create(missing), DBNull => ComVariant.Null, + UnknownWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), + DispatchWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), +#pragma warning disable 0618 // CurrencyWrapper is obsolete + CurrencyWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), +#pragma warning restore 0618 + BStrWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), _ when Variant.IsSystemDrawingColor(input.GetType()) => ComVariant.Create(Variant.SystemColorTranslatorAccess.ConvertSystemColorToOleColor(input)), _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; } - private static ComVariant GetComIPFromObjectRef(object obj) + private static ComVariant GetComIPFromObjectRef(object? obj) { IntPtr pUnk = GetIUnknownOrIDispatchForObject(ObjectHandleOnStack.Create(ref obj), out bool isIDispatch); return ComVariant.CreateRaw(isIDispatch ? VarEnum.VT_DISPATCH : VarEnum.VT_UNKNOWN, pUnk); @@ -195,14 +204,6 @@ private static object FromOAVariant(ComVariant input) => _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; - private static VarEnum GetVTFromClass(Type type) - { - if (ClassTypes.TryGetValue(type, out VarEnum vt)) - return vt; - - throw new NotSupportedException(SR.NotSupported_ChangeType); - } - #endregion } } From d17d42767ea017a4aa3fb57bd038a20536c30c8a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Mar 2024 16:43:29 +0800 Subject: [PATCH 19/35] Delete unreachable special type handling --- .../src/System/Variant.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 773b9b511e965e..d6a4e0af7e629e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -90,23 +90,8 @@ internal unsafe void SetFieldsObject(object val) if (!pMT->IsValueType) { _objref = val; - if (val.GetType() == typeof(Missing)) - { - _flags = CV_MISSING; - } - else if (val.GetType() == typeof(DBNull)) - { - _flags = CV_NULL; - } - else if (val.GetType() == typeof(Empty)) - { - _flags = CV_EMPTY; - _objref = null; - } - else - { - _flags = CV_OBJECT; - } + // Special cases already handled by caller + _flags = CV_OBJECT; } else if (IsSystemDrawingColor(val.GetType())) { From b4b86bbfd18f6644e33878a2f72f8948a83e8e56 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Mar 2024 23:28:34 +0800 Subject: [PATCH 20/35] Revert System.Drawing.Color reflection --- .../src/Microsoft/Win32/OAVariantLib.cs | 7 ++- .../src/System/Variant.cs | 29 ++-------- .../classlibnative/bcltype/CMakeLists.txt | 1 + .../classlibnative/bcltype/variant.cpp | 55 +++++++++++++++++++ src/coreclr/classlibnative/bcltype/variant.h | 28 ++++++++++ src/coreclr/vm/corelib.cpp | 1 + src/coreclr/vm/qcallentrypoints.cpp | 3 + 7 files changed, 98 insertions(+), 26 deletions(-) create mode 100644 src/coreclr/classlibnative/bcltype/variant.cpp create mode 100644 src/coreclr/classlibnative/bcltype/variant.h diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index d18ee98805b276..187faf9fc6d10e 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -84,7 +84,7 @@ internal static object ChangeType(object source, Type targetClass, short options { uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color - result = Variant.SystemColorTranslatorAccess.ConvertOleColorToSystemColor(sourceData); + ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); Debug.Assert(result != null); return result; } @@ -167,7 +167,7 @@ private static ComVariant ToOAVariant(object input) #pragma warning restore 0618 BStrWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), _ when Variant.IsSystemDrawingColor(input.GetType()) - => ComVariant.Create(Variant.SystemColorTranslatorAccess.ConvertSystemColorToOleColor(input)), + => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; } @@ -204,6 +204,9 @@ private static object FromOAVariant(ComVariant input) => _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] + private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value, IntPtr pMT); + #endregion } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index d6a4e0af7e629e..b35c55178d8e72 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -83,6 +83,9 @@ internal partial struct Variant internal static bool IsSystemDrawingColor(Type type) => type.Name == "System.Drawing.Color"; // Matches the behavior of IsTypeRefOrDef + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] + internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); + internal unsafe void SetFieldsObject(object val) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(val); @@ -96,7 +99,8 @@ internal unsafe void SetFieldsObject(object val) else if (IsSystemDrawingColor(val.GetType())) { // System.Drawing.Color is converted to UInt32 - _data = SystemColorTranslatorAccess.ConvertSystemColorToOleColor(val); + object obj = val; + _data = ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref obj)); _flags = CV_U4; } else @@ -545,28 +549,5 @@ internal static void MarshalHelperCastVariant(object pValue, int vt, ref Variant }; } } - - internal static class SystemColorTranslatorAccess - { - private static readonly MethodInfo s_toOleMethod; - private static readonly MethodInfo s_fromOleMethod; - -#pragma warning disable CA1810 // Initialize reference type static fields inline - static SystemColorTranslatorAccess() -#pragma warning restore CA1810 - { - // Reflection pattern recognized by trimmer - Type? colorTranslatorType = Type.GetType("System.Drawing.ColorTranslator, System.Drawing"); - Debug.Assert(colorTranslatorType != null); - s_toOleMethod = colorTranslatorType.GetMethod("ToOle", BindingFlags.Public | BindingFlags.Static)!; - s_fromOleMethod = colorTranslatorType.GetMethod("FromOle", BindingFlags.Public | BindingFlags.Static)!; - } - - public static uint ConvertSystemColorToOleColor(object colorObj) - => (uint)(int)s_toOleMethod.Invoke(null, [colorObj])!; - - public static object ConvertOleColorToSystemColor(uint oleColor) - => s_fromOleMethod.Invoke(null, [(int)oleColor])!; - } } } diff --git a/src/coreclr/classlibnative/bcltype/CMakeLists.txt b/src/coreclr/classlibnative/bcltype/CMakeLists.txt index dca77043e4149e..e8cce56d173555 100644 --- a/src/coreclr/classlibnative/bcltype/CMakeLists.txt +++ b/src/coreclr/classlibnative/bcltype/CMakeLists.txt @@ -5,6 +5,7 @@ set(BCLTYPE_SOURCES objectnative.cpp system.cpp varargsnative.cpp + variant.cpp ) add_library_clr(bcltype OBJECT ${BCLTYPE_SOURCES}) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp new file mode 100644 index 00000000000000..351f999d02d10f --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Variant.cpp +// + +// +// Purpose: Native Implementation of the Variant Class +// + +// + +#include "common.h" + +#ifdef FEATURE_COMINTEROP + +#include "object.h" +#include "excep.h" +#include "frames.h" +#include "vars.hpp" +#include "variant.h" +#include "string.h" + +extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj) +{ + QCALL_CONTRACT; + + uint32_t ret = 0; + + BEGIN_QCALL; + + GCX_COOP(); + OBJECTREF srcObj = obj.Get(); + ret = ConvertSystemColorToOleColor(&srcObj); + + END_QCALL; + + return ret; +} + +extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + GCX_COOP(); + SYSTEMCOLOR SystemColor = {}; + ConvertOleColorToSystemColor(oleColor, &SystemColor); + objRet.Set(pMT->Box(&SystemColor)); + + END_QCALL; +} + +#endif // FEATURE_COMINTEROP diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h new file mode 100644 index 00000000000000..037e598ebcca3b --- /dev/null +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// File: Variant.h +// + +// +// Purpose: Headers for the Variant class. +// + +// + +#ifndef _VARIANT_H_ +#define _VARIANT_H_ + +#ifndef FEATURE_COMINTEROP +#error FEATURE_COMINTEROP is required for this file +#endif // FEATURE_COMINTEROP + +#include +#include "fcall.h" +#include "olevariant.h" + +extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); +extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT); + +#endif // _VARIANT_H_ + diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp index 54df2a37d7c250..e172f6a5423e43 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -46,6 +46,7 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP +#include "variant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 1b2d46f2192c00..8521cda627430c 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -42,6 +42,7 @@ #include "mlinfo.h" #ifdef FEATURE_COMINTEROP +#include "variant.h" #include "mngstdinterfaces.h" #endif // FEATURE_COMINTEROP @@ -314,6 +315,8 @@ static const Entry s_QCall[] = DllImportEntry(MngdSafeArrayMarshaler_ConvertSpaceToManaged) DllImportEntry(MngdSafeArrayMarshaler_ConvertContentsToManaged) DllImportEntry(MngdSafeArrayMarshaler_ClearNative) + DllImportEntry(Variant_ConvertSystemColorToOleColor) + DllImportEntry(Variant_ConvertOleColorToSystemColor) #endif // FEATURE_COMINTEROP DllImportEntry(NativeLibrary_LoadFromPath) DllImportEntry(NativeLibrary_LoadByName) From 756ad96f984b2731e794be7e8705ab859c7303ca Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 28 Mar 2024 22:19:13 +0800 Subject: [PATCH 21/35] Revert changes around Variant --- .../src/System/Enum.CoreCLR.cs | 4 +- .../src/System/Variant.cs | 140 +-------- .../classlibnative/bcltype/variant.cpp | 266 ++++++++++++++++++ src/coreclr/classlibnative/bcltype/variant.h | 19 ++ src/coreclr/vm/ecalllist.h | 10 + 5 files changed, 311 insertions(+), 128 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs index 597da509513884..6f578ccad411fd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.cs @@ -15,7 +15,7 @@ public abstract partial class Enum private static partial void GetEnumValuesAndNames(QCallTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, Interop.BOOL getNames); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object InternalBoxEnum(RuntimeType enumType, long value); + private static extern object InternalBoxEnum(RuntimeType enumType, long value); [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe CorElementType InternalGetCorElementType(MethodTable* pMT); @@ -30,7 +30,7 @@ private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal unsafe CorElementType InternalGetCorElementType() + private unsafe CorElementType InternalGetCorElementType() { CorElementType elementType = InternalGetCorElementType(RuntimeHelpers.GetMethodTable(this)); GC.KeepAlive(this); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index b35c55178d8e72..3653b68e103918 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -12,7 +12,6 @@ using System.Diagnostics; using System.Globalization; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -22,9 +21,9 @@ internal partial struct Variant { // Do Not change the order of these fields. // They are mapped to the native VariantData * data structure. - internal object? _objref; - internal long _data; - internal int _flags; + private object? _objref; + private long _data; + private int _flags; // The following bits have been taken up as follows // bits 0-15 - Type code @@ -57,20 +56,10 @@ internal partial struct Variant internal const int CV_TIMESPAN = 0x11; internal const int CV_OBJECT = 0x12; internal const int CV_DECIMAL = 0x13; - internal const int CV_CURRENCY = 0x14; 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 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 const int TypeCodeBitMask = 0xffff; internal const int VTBitMask = unchecked((int)0xff000000); @@ -86,116 +75,11 @@ internal partial struct Variant [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); - internal unsafe void SetFieldsObject(object val) - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(val); - - if (!pMT->IsValueType) - { - _objref = val; - // Special cases already handled by caller - _flags = CV_OBJECT; - } - else if (IsSystemDrawingColor(val.GetType())) - { - // System.Drawing.Color is converted to UInt32 - object obj = val; - _data = ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref obj)); - _flags = CV_U4; - } - else - { - //If this is a primitive type, we need to unbox it, get the value and create a variant - //with just those values. - - _objref = null; - - //copy all of the data. - // Copies must be done based on the exact number of bytes to copy. - // We don't want to read garbage from other blocks of memory. - //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities - //If we don't find one of those ranges, we've found a value class - //of which we don't have inherent knowledge, so just slam that into an - //ObjectRef. - switch (val) - { - case byte u1: - this = new(u1); - break; - case sbyte i1: - this = new(i1); - break; - case bool boolean: - this = new(boolean); - break; - case short i2: - this = new(i2); - break; - case ushort u2: - this = new(u2); - break; - case char ch: - this = new(ch); - break; - case int i4: - this = new(i4); - break; - case uint u4: - this = new(u4); - break; - case float r4: - this = new(r4); - break; - // CV_DATETIME handled by caller - case TimeSpan ts: - _flags = CV_TIMESPAN; - _data = ts.Ticks; - break; - // CV_CURRENCY handled by caller - case long i8: - this = new(i8); - break; - case ulong u8: - this = new(u8); - break; - case double r8: - this = new(r8); - break; - case decimal d: - this = new(d); - break; - case Enum e: - _data = (long)Enum.ToUInt64(e); - _objref = e.GetType(); - _flags = e.InternalGetCorElementType() switch - { - CorElementType.ELEMENT_TYPE_I1 => CV_ENUM | EnumI1, - CorElementType.ELEMENT_TYPE_U1 => CV_ENUM | EnumU1, - CorElementType.ELEMENT_TYPE_I2 => CV_ENUM | EnumI2, - CorElementType.ELEMENT_TYPE_U2 => CV_ENUM | EnumU2, - CorElementType.ELEMENT_TYPE_I4 => CV_ENUM | EnumI4, - CorElementType.ELEMENT_TYPE_U4 => CV_ENUM | EnumU4, -#if TARGET_64BIT - CorElementType.ELEMENT_TYPE_I => CV_ENUM | EnumI8, - CorElementType.ELEMENT_TYPE_U => CV_ENUM | EnumU8, -#else - - CorElementType.ELEMENT_TYPE_I => CV_ENUM | EnumI4, - CorElementType.ELEMENT_TYPE_U => CV_ENUM | EnumU4, -#endif - CorElementType.ELEMENT_TYPE_I8 => CV_ENUM | EnumI8, - CorElementType.ELEMENT_TYPE_U8 => CV_ENUM | EnumU8, - _ => throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType) - }; - break; - default: - // Other boxed value classes get handled here. - _objref = val; - _flags = CV_OBJECT; - break; - } - } - } + // + // Native Methods + // + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern void SetFieldsObject(object val); // // Constructors @@ -327,7 +211,7 @@ public Variant(object? obj) return; } - if (obj == null || obj is System.Empty) + if (obj == null) { this = Empty; return; @@ -418,12 +302,16 @@ public Variant(object? obj) CV_R8 => BitConverter.Int64BitsToDouble(_data), CV_DATETIME => new DateTime(_data), CV_TIMESPAN => new TimeSpan(_data), - CV_ENUM => Enum.InternalBoxEnum((RuntimeType)_objref!, _data), + CV_ENUM => BoxEnum(), CV_MISSING => Type.Missing, CV_NULL => System.DBNull.Value, _ => _objref, // CV_DECIMAL, CV_STRING, CV_OBJECT }; + // This routine will return an boxed enum. + [MethodImpl(MethodImplOptions.InternalCall)] + private extern object BoxEnum(); + // Helper code for marshaling managed objects to VARIANT's (we use // managed variants as an intermediate type. internal static void MarshalHelperConvertObjectToVariant(object o, ref Variant v) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index 351f999d02d10f..eb798b4ec7c4ca 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -21,6 +21,272 @@ #include "variant.h" #include "string.h" +#include "field.h" + +// The following values are used to represent underlying +// type of the Enum.. +#define EnumI1 0x100000 +#define EnumU1 0x200000 +#define EnumI2 0x300000 +#define EnumU2 0x400000 +#define EnumI4 0x500000 +#define EnumU4 0x600000 +#define EnumI8 0x700000 +#define EnumU8 0x800000 +#define EnumMask 0xF00000 + + +/*===============================SetFieldsObject================================ +** +==============================================================================*/ +FCIMPL2(void, COMVariant::SetFieldsObject, VariantData* var, Object* vVal) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(var)); + PRECONDITION(CheckPointer(vVal)); + } + CONTRACTL_END; + + OBJECTREF val = ObjectToOBJECTREF(vVal); + + HELPER_METHOD_FRAME_BEGIN_1(val); + GCPROTECT_BEGININTERIOR(var) + + CVTypes cvt = CV_EMPTY; + TypeHandle typeHandle; + + MethodTable *valMT = val->GetMethodTable(); + + //If this isn't a value class, we should just skip out because we're not going + //to do anything special with it. + if (!valMT->IsValueType()) + { + var->SetObjRef(val); + typeHandle = TypeHandle(valMT); + + if (typeHandle==GetTypeHandleForCVType(CV_MISSING)) + { + var->SetType(CV_MISSING); + } + else if (typeHandle==GetTypeHandleForCVType(CV_NULL)) + { + var->SetType(CV_NULL); + } + else if (typeHandle==GetTypeHandleForCVType(CV_EMPTY)) + { + var->SetType(CV_EMPTY); + var->SetObjRef(NULL); + } + else + { + var->SetType(CV_OBJECT); + } + } + else if (IsTypeRefOrDef(g_ColorClassName, valMT->GetModule(), valMT->GetCl())) + { + // System.Drawing.Color is converted to UInt32 + var->SetDataAsUInt32(ConvertSystemColorToOleColor(&val)); + var->SetType(CV_U4); + } + else + { + //If this is a primitive type, we need to unbox it, get the value and create a variant + //with just those values. + void *UnboxData = val->UnBox(); + + ClearObjectReference(var->GetObjRefPtr()); + typeHandle = TypeHandle(valMT); + CorElementType cet = typeHandle.GetSignatureCorElementType(); + + if (cet>=ELEMENT_TYPE_BOOLEAN && cet<=ELEMENT_TYPE_STRING) + { + cvt = (CVTypes)cet; + } + else + { + cvt = GetCVTypeFromClass(valMT); + } + var->SetType(cvt); + + + //copy all of the data. + // Copies must be done based on the exact number of bytes to copy. + // We don't want to read garbage from other blocks of memory. + //CV_I8 --> CV_R8, CV_DATETIME, CV_TIMESPAN, & CV_CURRENCY are all of the 8 byte quantities + //If we don't find one of those ranges, we've found a value class + //of which we don't have inherent knowledge, so just slam that into an + //ObjectRef. + if (cvt>=CV_BOOLEAN && cvt<=CV_U1 && cvt != CV_CHAR) + { + var->SetDataAsInt64(*((UINT8 *)UnboxData)); + } + else if (cvt==CV_CHAR || cvt>=CV_I2 && cvt<=CV_U2) + { + var->SetDataAsInt64(*((UINT16 *)UnboxData)); + } + else if (cvt>=CV_I4 && cvt<=CV_U4 || cvt==CV_R4) + { + var->SetDataAsInt64(*((UINT32 *)UnboxData)); + } + else if ((cvt>=CV_I8 && cvt<=CV_R8) || (cvt==CV_DATETIME) || (cvt==CV_TIMESPAN) || (cvt==CV_CURRENCY)) + { + var->SetDataAsInt64(*((INT64 *)UnboxData)); + } + else if (cvt==CV_EMPTY || cvt==CV_NULL || cvt==CV_MISSING) + { + var->SetType(cvt); + } + else if (cvt==CV_ENUM) + { + var->SetDataAsInt64(*((INT32 *)UnboxData)); + var->SetObjRef(typeHandle.GetManagedClassObject()); + var->SetType(GetEnumFlags(typeHandle)); + } + else + { + // Decimals and other boxed value classes get handled here. + var->SetObjRef(val); + } + } + + GCPROTECT_END(); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + +FCIMPL1(Object*, COMVariant::BoxEnum, VariantData* var) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(var)); + PRECONDITION(var->GetObjRef() != NULL); + } + CONTRACTL_END; + + OBJECTREF retO = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(retO); + +#ifdef _DEBUG + CVTypes vType = (CVTypes) var->GetType(); +#endif + + _ASSERTE(vType == CV_ENUM); + + MethodTable* mt = ((REFLECTCLASSBASEREF) var->GetObjRef())->GetType().GetMethodTable(); + _ASSERTE(mt); + + retO = mt->Box(var->GetData()); + + HELPER_METHOD_FRAME_END(); + return OBJECTREFToObject(retO); +} +FCIMPLEND + + +/*===============================GetTypeFromClass=============================== +**Action: Takes an MethodTable * and returns the associated CVType. +**Arguments: MethodTable * -- a pointer to the class for which we want the CVType. +**Returns: The CVType associated with the MethodTable or CV_OBJECT if this can't be +** determined. +**Exceptions: None +==============================================================================*/ + +CVTypes COMVariant::GetCVTypeFromClass(TypeHandle th) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + if (th.IsNull()) + return CV_EMPTY; + + //We'll start looking from Variant. Empty and Void are handled below. + for (int i=CV_EMPTY; i check this approximation - we may be losing exact type information + ApproxFieldDescIterator fdIterator(th.GetMethodTable(), ApproxFieldDescIterator::INSTANCE_FIELDS); + FieldDesc* p = fdIterator.Next(); + if (NULL == p) + { + _ASSERTE(!"NULL FieldDesc returned"); + return 0; + } + +#ifdef _DEBUG + WORD fldCnt = th.GetMethodTable()->GetNumInstanceFields(); +#endif + + _ASSERTE(fldCnt == 1); + + CorElementType cet = p[0].GetFieldType(); + switch (cet) + { + case ELEMENT_TYPE_I1: + return (CV_ENUM | EnumI1); + + case ELEMENT_TYPE_U1: + return (CV_ENUM | EnumU1); + + case ELEMENT_TYPE_I2: + return (CV_ENUM | EnumI2); + + case ELEMENT_TYPE_U2: + return (CV_ENUM | EnumU2); + + IN_TARGET_32BIT(case ELEMENT_TYPE_I:) + case ELEMENT_TYPE_I4: + return (CV_ENUM | EnumI4); + + IN_TARGET_32BIT(case ELEMENT_TYPE_U:) + case ELEMENT_TYPE_U4: + return (CV_ENUM | EnumU4); + + IN_TARGET_64BIT(case ELEMENT_TYPE_I:) + case ELEMENT_TYPE_I8: + return (CV_ENUM | EnumI8); + + IN_TARGET_64BIT(case ELEMENT_TYPE_U:) + case ELEMENT_TYPE_U8: + return (CV_ENUM | EnumU8); + + default: + _ASSERTE(!"UNknown Type"); + return 0; + } +} + extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj) { QCALL_CONTRACT; diff --git a/src/coreclr/classlibnative/bcltype/variant.h b/src/coreclr/classlibnative/bcltype/variant.h index 037e598ebcca3b..12c5a3d36d53dd 100644 --- a/src/coreclr/classlibnative/bcltype/variant.h +++ b/src/coreclr/classlibnative/bcltype/variant.h @@ -21,6 +21,25 @@ #include "fcall.h" #include "olevariant.h" +class COMVariant +{ + friend class OleVariant; + +public: + // + // Helper Routines + // + + static FCDECL2(void, SetFieldsObject, VariantData* vThisRef, Object* vVal); + static FCDECL1(Object*, BoxEnum, VariantData* var); + +private: + // GetCVTypeFromClass + // This method will return the CVTypes from the Variant instance + static CVTypes GetCVTypeFromClass(TypeHandle th); + static int GetEnumFlags(TypeHandle th); +}; + extern "C" uint32_t QCALLTYPE Variant_ConvertSystemColorToOleColor(QCall::ObjectHandleOnStack obj); extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHandleOnStack objRet, uint32_t oleColor, MethodTable* pMT); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 4e461937988a3e..9d7e8f7125c39e 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -358,6 +358,13 @@ FCFuncStart(gWaitHandleFuncs) FCFuncElement("SignalAndWaitNative", WaitHandleNative::CorSignalAndWaitOneNative) FCFuncEnd() +#ifdef FEATURE_COMINTEROP +FCFuncStart(gVariantFuncs) + FCFuncElement("SetFieldsObject", COMVariant::SetFieldsObject) + FCFuncElement("BoxEnum", COMVariant::BoxEnum) +FCFuncEnd() +#endif // FEATURE_COMINTEROP + FCFuncStart(gCastHelpers) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) @@ -592,6 +599,9 @@ FCClassElement("Thread", "System.Threading", gThreadFuncs) FCClassElement("ThreadPool", "System.Threading", gThreadPoolFuncs) FCClassElement("Type", "System", gSystem_Type) FCClassElement("TypedReference", "System", gTypedReferenceFuncs) +#ifdef FEATURE_COMINTEROP +FCClassElement("Variant", "System", gVariantFuncs) +#endif FCClassElement("WaitHandle", "System.Threading", gWaitHandleFuncs) #undef FCFuncElement From de55154014b9503ccfcf0b4d483361cf26e5c4a1 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 28 Mar 2024 23:21:23 +0800 Subject: [PATCH 22/35] Move Color conversion to Variant --- .../src/Microsoft/Win32/OAVariantLib.cs | 5 +---- src/coreclr/System.Private.CoreLib/src/System/Variant.cs | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 187faf9fc6d10e..a549e6c792427b 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -84,7 +84,7 @@ internal static object ChangeType(object source, Type targetClass, short options { uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color - ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); + Variant.ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); Debug.Assert(result != null); return result; } @@ -204,9 +204,6 @@ private static object FromOAVariant(ComVariant input) => _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] - private static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value, IntPtr pMT); - #endregion } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 3653b68e103918..f32f2b99e788af 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -70,11 +70,14 @@ internal partial struct Variant internal static Variant Missing => new Variant(CV_MISSING, Type.Missing, 0); internal static Variant DBNull => new Variant(CV_NULL, System.DBNull.Value, 0); - internal static bool IsSystemDrawingColor(Type type) => type.Name == "System.Drawing.Color"; // Matches the behavior of IsTypeRefOrDef + internal static bool IsSystemDrawingColor(Type type) => type.FullName == "System.Drawing.Color"; // Matches the behavior of IsTypeRefOrDef [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertSystemColorToOleColor")] internal static partial uint ConvertSystemColorToOleColor(ObjectHandleOnStack obj); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Variant_ConvertOleColorToSystemColor")] + internal static partial void ConvertOleColorToSystemColor(ObjectHandleOnStack objret, uint value, IntPtr pMT); + // // Native Methods // From 1cfc19ba88710d72d7d520ab060257874677d4b0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 29 Mar 2024 00:55:49 +0800 Subject: [PATCH 23/35] Respect VTToCV mapping --- .../src/Microsoft/Win32/OAVariantLib.cs | 43 ++++++++++--------- .../src/System/OleAutBinder.cs | 2 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index a549e6c792427b..b7341e1c487380 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -56,7 +56,6 @@ internal static unsafe partial class OAVariantLib { typeof(DateTime), VarEnum.VT_DATE }, { typeof(object), VarEnum.VT_UNKNOWN }, { typeof(decimal), VarEnum.VT_DECIMAL }, - { typeof(DBNull), VarEnum.VT_NULL }, }; #endregion @@ -71,7 +70,7 @@ internal static unsafe partial class OAVariantLib * Variant and the types that CLR supports explicitly in the * CLR Variant class. */ - internal static object ChangeType(object source, Type targetClass, short options, CultureInfo culture) + internal static object? ChangeType(object source, Type targetClass, short options, CultureInfo culture) { ArgumentNullException.ThrowIfNull(targetClass); ArgumentNullException.ThrowIfNull(culture); @@ -107,7 +106,7 @@ internal static object ChangeType(object source, Type targetClass, short options result = FromOAVariant(ret); if (targetClass == typeof(char)) { - result = (char)(uint)result; + result = (char)(uint)result!; } } @@ -158,7 +157,7 @@ private static ComVariant ToOAVariant(object input) TimeSpan => throw new NotSupportedException(SR.NotSupported_ChangeType), Enum => throw new NotSupportedException(SR.NotSupported_ChangeType), null => default, - Missing missing => ComVariant.Create(missing), + Missing => throw new NotSupportedException(SR.NotSupported_ChangeType), DBNull => ComVariant.Null, UnknownWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), DispatchWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), @@ -181,26 +180,28 @@ private static ComVariant GetComIPFromObjectRef(object? obj) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_GetIUnknownOrIDispatchForObject")] private static partial IntPtr GetIUnknownOrIDispatchForObject(ObjectHandleOnStack o, [MarshalAs(UnmanagedType.Bool)] out bool isIDispatch); - private static object FromOAVariant(ComVariant input) => + private static object? FromOAVariant(ComVariant input) => input.VarType switch { - VarEnum.VT_BSTR => input.As()!, - VarEnum.VT_DATE => input.As()!, - VarEnum.VT_BOOL => input.As()!, - VarEnum.VT_DECIMAL => input.As()!, - VarEnum.VT_I1 => input.As()!, - VarEnum.VT_UI1 => input.As()!, - VarEnum.VT_I2 => input.As()!, - VarEnum.VT_UI2 => input.As()!, - VarEnum.VT_I4 => input.As()!, - VarEnum.VT_UI4 => input.As()!, - VarEnum.VT_I8 => input.As()!, - VarEnum.VT_UI8 => input.As()!, - VarEnum.VT_R4 => input.As()!, - VarEnum.VT_R8 => input.As()!, - VarEnum.VT_EMPTY => null!, + VarEnum.VT_BSTR => input.As(), + VarEnum.VT_DATE => input.As(), + VarEnum.VT_BOOL => input.As(), + VarEnum.VT_CY => null, + VarEnum.VT_DECIMAL => input.As(), + VarEnum.VT_I1 => input.As(), + VarEnum.VT_UI1 => input.As(), + VarEnum.VT_I2 => input.As(), + VarEnum.VT_UI2 => input.As(), + VarEnum.VT_I4 or VarEnum.VT_INT => input.As(), + VarEnum.VT_UI4 or VarEnum.VT_UINT => input.As(), + VarEnum.VT_I8 => input.As(), + VarEnum.VT_UI8 => input.As(), + VarEnum.VT_R4 => input.As(), + VarEnum.VT_R8 => input.As(), + VarEnum.VT_EMPTY => null, VarEnum.VT_NULL => DBNull.Value, - VarEnum.VT_UNKNOWN or VarEnum.VT_DISPATCH => Marshal.GetObjectForIUnknown(input.GetRawDataRef()), // Convert the IUnknown pointer to an OBJECTREF. + VarEnum.VT_UNKNOWN or VarEnum.VT_VARIANT => Marshal.GetObjectForIUnknown(input.GetRawDataRef()), + VarEnum.VT_VOID => null, _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; diff --git a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs index bfbf717112c721..32a69bb155f148 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs @@ -61,7 +61,7 @@ public override object ChangeType(object value, Type type, CultureInfo? cultureI #endif // Specify the LocalBool flag to have BOOL values converted to local language rather // than 0 or -1. - object RetObj = OAVariantLib.ChangeType(value, type, OAVariantLib.LocalBool, cultureInfo); + object RetObj = OAVariantLib.ChangeType(value, type, OAVariantLib.LocalBool, cultureInfo)!; #if DISPLAY_DEBUG_INFO Console.WriteLine("Object returned from ChangeType is of type: " + RetObj.GetType().Name); From 539040c86fa40356f85a815a8ed8885970c02ed8 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 29 Mar 2024 00:56:57 +0800 Subject: [PATCH 24/35] Update src/coreclr/vm/marshalnative.cpp Co-authored-by: Aaron Robinson --- src/coreclr/vm/marshalnative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index 46c6314482fce7..ac3fefa5eb5246 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -533,7 +533,7 @@ extern "C" void* QCALLTYPE MarshalNative_GetIUnknownOrIDispatchForObject(QCall:: OBJECTREF oref = o.Get(); GCPROTECT_BEGIN(oref); - ComIpType fetchedIpType = {}; + ComIpType fetchedIpType = ComIpType_None; retVal = GetComIPFromObjectRef(&oref, ComIpType_Both, &fetchedIpType); *isIDispatch = fetchedIpType == ComIpType_Dispatch; GCPROTECT_END(); From f989fce517cb881ad7a0706b4e8a04e358ad4394 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 29 Mar 2024 08:21:39 +0800 Subject: [PATCH 25/35] Update src/coreclr/classlibnative/bcltype/variant.cpp Co-authored-by: Aaron Robinson --- src/coreclr/classlibnative/bcltype/variant.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/variant.cpp b/src/coreclr/classlibnative/bcltype/variant.cpp index eb798b4ec7c4ca..1a3cf7e75bb441 100644 --- a/src/coreclr/classlibnative/bcltype/variant.cpp +++ b/src/coreclr/classlibnative/bcltype/variant.cpp @@ -311,9 +311,9 @@ extern "C" void QCALLTYPE Variant_ConvertOleColorToSystemColor(QCall::ObjectHand BEGIN_QCALL; GCX_COOP(); - SYSTEMCOLOR SystemColor = {}; - ConvertOleColorToSystemColor(oleColor, &SystemColor); - objRet.Set(pMT->Box(&SystemColor)); + SYSTEMCOLOR systemColor{}; + ConvertOleColorToSystemColor(oleColor, &systemColor); + objRet.Set(pMT->Box(&systemColor)); END_QCALL; } From 434fff66711ca348d05d4454582d0166346ef297 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 5 May 2024 15:50:08 +0800 Subject: [PATCH 26/35] Cleanup impossible types and unused constants --- .../src/Microsoft/Win32/OAVariantLib.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index b7341e1c487380..caa61d5ac20ce7 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -30,10 +30,6 @@ internal static unsafe partial class OAVariantLib #region Constants // Constants for VariantChangeType from OleAuto.h - public const int NoValueProp = 0x01; - public const int AlphaBool = 0x02; - public const int NoUserOverride = 0x04; - public const int CalendarHijri = 0x08; public const int LocalBool = 0x10; private static readonly Dictionary ClassTypes = new Dictionary @@ -140,7 +136,6 @@ private static ComVariant ToOAVariant(object input) return input switch { string str => ComVariant.Create(str), - char ch => ComVariant.Create(ch.ToString()), // We should override the VTtoVT default of VT_UI2 for this case. DateTime dateTime => ComVariant.Create(dateTime), bool b => ComVariant.Create(b), decimal d => ComVariant.Create(d), @@ -154,19 +149,9 @@ private static ComVariant ToOAVariant(object input) ulong u8 => ComVariant.Create(u8), float r4 => ComVariant.Create(r4), double r8 => ComVariant.Create(r8), - TimeSpan => throw new NotSupportedException(SR.NotSupported_ChangeType), - Enum => throw new NotSupportedException(SR.NotSupported_ChangeType), null => default, Missing => throw new NotSupportedException(SR.NotSupported_ChangeType), DBNull => ComVariant.Null, - UnknownWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), - DispatchWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), -#pragma warning disable 0618 // CurrencyWrapper is obsolete - CurrencyWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), -#pragma warning restore 0618 - BStrWrapper wrapper => GetComIPFromObjectRef(wrapper.WrappedObject), - _ when Variant.IsSystemDrawingColor(input.GetType()) - => ComVariant.Create(Variant.ConvertSystemColorToOleColor(ObjectHandleOnStack.Create(ref input))), _ => GetComIPFromObjectRef(input) // Convert the object to an IDispatch/IUnknown pointer. }; } @@ -186,7 +171,6 @@ private static ComVariant GetComIPFromObjectRef(object? obj) VarEnum.VT_BSTR => input.As(), VarEnum.VT_DATE => input.As(), VarEnum.VT_BOOL => input.As(), - VarEnum.VT_CY => null, VarEnum.VT_DECIMAL => input.As(), VarEnum.VT_I1 => input.As(), VarEnum.VT_UI1 => input.As(), @@ -199,7 +183,6 @@ private static ComVariant GetComIPFromObjectRef(object? obj) VarEnum.VT_R4 => input.As(), VarEnum.VT_R8 => input.As(), VarEnum.VT_EMPTY => null, - VarEnum.VT_NULL => DBNull.Value, VarEnum.VT_UNKNOWN or VarEnum.VT_VARIANT => Marshal.GetObjectForIUnknown(input.GetRawDataRef()), VarEnum.VT_VOID => null, _ => throw new NotSupportedException(SR.NotSupported_ChangeType), From 24bada7c2c8d3fe9b6aa5b80299dc71b41bea8db Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sun, 5 May 2024 21:30:18 +0800 Subject: [PATCH 27/35] Fix HResult --- .../src/Microsoft/Win32/OAVariantLib.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index caa61d5ac20ce7..ffe7ca6fd516ad 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -117,11 +117,11 @@ private static void OAFailed(int hr) throw new OutOfMemoryException(); case unchecked((int)0x80020008): // DISP_E_BADVARTYPE throw new NotSupportedException(SR.NotSupported_OleAutBadVarType); - case HResults.COR_E_DIVIDEBYZERO: + case unchecked((int)0x80020012): // DISP_E_DIVBYZERO throw new DivideByZeroException(); - case HResults.COR_E_OVERFLOW: + case unchecked((int)0x8002000A): // DISP_E_OVERFLOW throw new OverflowException(); - case HResults.TYPE_E_TYPEMISMATCH: + case unchecked((int)0x80020005): // DISP_E_TYPEMISMATCH throw new InvalidCastException(SR.InvalidCast_OATypeMismatch); case HResults.E_INVALIDARG: throw new ArgumentException(); From 9c1e96f3f82ff4e659ada0861752d7310a5743ea Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 6 May 2024 22:17:53 +0800 Subject: [PATCH 28/35] Use constants for HRESULT --- .../src/Microsoft/Win32/OAVariantLib.cs | 8 ++++---- src/libraries/Common/src/System/HResults.cs | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index ffe7ca6fd516ad..45229ab004a17c 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -115,13 +115,13 @@ private static void OAFailed(int hr) { case HResults.COR_E_OUTOFMEMORY: throw new OutOfMemoryException(); - case unchecked((int)0x80020008): // DISP_E_BADVARTYPE + case HResults.DISP_E_BADVARTYPE: throw new NotSupportedException(SR.NotSupported_OleAutBadVarType); - case unchecked((int)0x80020012): // DISP_E_DIVBYZERO + case HResults.DISP_E_DIVBYZERO: throw new DivideByZeroException(); - case unchecked((int)0x8002000A): // DISP_E_OVERFLOW + case HResults.DISP_E_OVERFLOW: throw new OverflowException(); - case unchecked((int)0x80020005): // DISP_E_TYPEMISMATCH + case HResults.DISP_E_TYPEMISMATCH: throw new InvalidCastException(SR.InvalidCast_OATypeMismatch); case HResults.E_INVALIDARG: throw new ArgumentException(); diff --git a/src/libraries/Common/src/System/HResults.cs b/src/libraries/Common/src/System/HResults.cs index bbc434b87289c9..b0de5f2c5fe35c 100644 --- a/src/libraries/Common/src/System/HResults.cs +++ b/src/libraries/Common/src/System/HResults.cs @@ -109,7 +109,10 @@ internal static partial class HResults internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D); internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C); internal const int CO_E_NOTINITIALIZED = unchecked((int)0x800401F0); + internal const int DISP_E_TYPEMISMATCH = unchecked((int)0x80020005); + internal const int DISP_E_BADVARTYPE = unchecked((int)0x80020008); internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int DISP_E_DIVBYZERO = unchecked((int)0x80020012); internal const int E_BOUNDS = unchecked((int)0x8000000B); internal const int E_CHANGED_STATE = unchecked((int)0x8000000C); internal const int E_FILENOTFOUND = unchecked((int)0x80070002); From 578b02898030ae3a27fe9c4018eb6dd52e98b351 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 May 2024 00:43:27 +0800 Subject: [PATCH 29/35] Improve test type coverage --- .../COM/NETClients/IDispatch/Program.cs | 54 +++++++++++++++---- .../COM/NativeServer/DispatchCoerceTesting.h | 25 ++++++--- .../COM/ServerContracts/Server.Contracts.cs | 12 +++-- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/tests/Interop/COM/NETClients/IDispatch/Program.cs b/src/tests/Interop/COM/NETClients/IDispatch/Program.cs index c57f8c5149f27e..c18306c3c7e9c1 100644 --- a/src/tests/Interop/COM/NETClients/IDispatch/Program.cs +++ b/src/tests/Interop/COM/NETClients/IDispatch/Program.cs @@ -245,11 +245,6 @@ static void Validate_ValueCoerce_ReturnToManaged() Assert.Equal(expected, result); } - // Invalid: Rejected before reaching coerce - Console.WriteLine("Invalid variant type should throw InvalidOleVariantTypeException."); - var variantException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged(0x7FFF)); - Assert.Equal(unchecked((int)0x80131531), variantException.HResult); - // Not supported source or destination type: COMException { HResult: 0x80020005 } // DISP_E_PARAMNOTFOUND: Converts to Missing @@ -257,6 +252,49 @@ static void Validate_ValueCoerce_ReturnToManaged() var comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged(unchecked((short)((short)VarEnum.VT_ERROR | 0x8000)))); Assert.Equal(unchecked((int)0x80020005), comException.HResult); + // Types rejected by OAVariantLib + VarEnum[] unsupportedTypes = + { + VarEnum.VT_ERROR | (VarEnum)0x8000, + }; + + // Types rejected by VariantChangeTypeEx + VarEnum[] invalidCastTypes = + { + VarEnum.VT_UNKNOWN, + VarEnum.VT_NULL, + }; + + foreach (var vt in invalidCastTypes) + { + Console.WriteLine($"Converting {vt} to int should fail from VariantChangeTypeEx."); + Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged((short)vt)); + } + + // Invalid: Rejected before reaching coerce + Console.WriteLine("Invalid variant type should throw InvalidOleVariantTypeException."); + var variantException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged(0x7FFF)); + Assert.Equal(unchecked((int)0x80131531), variantException.HResult); + + Console.WriteLine("Invoking void-returning method should not allocate return buffer."); + // E_POINTER translates to NullReferenceException + Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Void()); + + Console.WriteLine("Converting int to double should be supported."); + Assert.Equal(1234d, dispatchCoerceTesting.ReturnToManaged_Double()); + + Console.WriteLine("Converting int to string should be supported."); + Assert.Equal("1234", dispatchCoerceTesting.ReturnToManaged_String()); + + Console.WriteLine("Converting int to decimal should be supported."); + Assert.Equal(1234m, dispatchCoerceTesting.ReturnToManaged_Decimal()); + + Console.WriteLine("Converting int to DateTime should be supported."); + Assert.Equal(new DateTime(1903, 5, 18, 0, 0, 0), dispatchCoerceTesting.ReturnToManaged_DateTime()); + + Console.WriteLine("Converting int to System.Drawing.Color should be supported."); + Assert.Equal(System.Drawing.Color.FromArgb(210, 4, 0), dispatchCoerceTesting.ReturnToManaged_Color()); + Console.WriteLine("Converting int to VT_MISSING should be rejected."); comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Missing()); Assert.Equal(unchecked((int)0x80020005), comException.HResult); @@ -265,12 +303,6 @@ static void Validate_ValueCoerce_ReturnToManaged() comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_DBNull()); Assert.Equal(unchecked((int)0x80020005), comException.HResult); - // Rejected by VariantChangeTypeEx - Console.WriteLine("Converting VT_UNKNOWN to int should fail from VariantChangeTypeEx."); - Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged((short)VarEnum.VT_UNKNOWN)); - Console.WriteLine("Converting VT_NULL to int should fail from VariantChangeTypeEx."); - Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged((short)VarEnum.VT_NULL)); - // LOCAL_BOOL Console.WriteLine("VARIANT_BOOL should convert to non-numeric string."); Assert.Equal("True", dispatchCoerceTesting.BoolToString()); diff --git a/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h b/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h index 4e9bf3fa99a028..932cdb3f3c2796 100644 --- a/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h +++ b/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h @@ -85,15 +85,22 @@ class DispatchCoerceTesting : public UnknownImpl, public IDispatchCoerceTesting } case 3: { - return ReturnToManaged_Missing_Dispatch(pDispParams, pVarResult); + return BoolToString_Dispatch(pDispParams, pVarResult); } case 4: { - return ReturnToManaged_DBNull_Dispatch(pDispParams, pVarResult); + return ReturnToManaged_Void_Dispatch(pDispParams, pVarResult); } case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: { - return BoolToString_Dispatch(pDispParams, pVarResult); + return ReturnToManaged_Any_Dispatch(pDispParams, pVarResult); } } @@ -209,7 +216,7 @@ class DispatchCoerceTesting : public UnknownImpl, public IDispatchCoerceTesting return S_OK; } - HRESULT ReturnToManaged_Missing_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) + HRESULT ReturnToManaged_Void_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) { HRESULT hr; @@ -224,7 +231,7 @@ class DispatchCoerceTesting : public UnknownImpl, public IDispatchCoerceTesting return S_OK; } - HRESULT ReturnToManaged_DBNull_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) + HRESULT ReturnToManaged_Any_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) { HRESULT hr; @@ -270,9 +277,15 @@ const WCHAR * const DispatchCoerceTesting::Names[] = W("__RESERVED__"), W("ReturnToManaged"), W("ManagedArgument"), + W("BoolToString"), + W("ReturnToManaged_Void"), + W("ReturnToManaged_Double"), + W("ReturnToManaged_String"), + W("ReturnToManaged_Decimal"), + W("ReturnToManaged_DateTime"), + W("ReturnToManaged_Color"), W("ReturnToManaged_Missing"), W("ReturnToManaged_DBNull"), - W("BoolToString") }; const int DispatchCoerceTesting::NamesCount = ARRAY_SIZE(DispatchCoerceTesting::Names); diff --git a/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs b/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs index 27ee5b3bfc3db4..309e30bc90b992 100644 --- a/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs +++ b/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs @@ -325,14 +325,16 @@ public interface TestingEvents public interface IDispatchCoerceTesting { int ReturnToManaged(short vt); - int ManagedArgument(int arg); - + string BoolToString(); + void ReturnToManaged_Void(); + double ReturnToManaged_Double(); + string ReturnToManaged_String(); + decimal ReturnToManaged_Decimal(); + DateTime ReturnToManaged_DateTime(); + Color ReturnToManaged_Color(); System.Reflection.Missing ReturnToManaged_Missing(); - DBNull ReturnToManaged_DBNull(); - - string BoolToString(); } [ComVisible(true)] From 00a0a612b5732a66b6e8eee2663527d14051e20b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 May 2024 00:45:03 +0800 Subject: [PATCH 30/35] Remove more impossible types --- .../System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 45229ab004a17c..15045de71dfe3b 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -34,8 +34,6 @@ internal static unsafe partial class OAVariantLib private static readonly Dictionary ClassTypes = new Dictionary { - { typeof(Empty), VarEnum.VT_EMPTY }, - { typeof(void), VarEnum.VT_VOID }, { typeof(bool), VarEnum.VT_BOOL }, { typeof(char), VarEnum.VT_I2 }, { typeof(sbyte), VarEnum.VT_I1 }, @@ -50,7 +48,6 @@ internal static unsafe partial class OAVariantLib { typeof(double), VarEnum.VT_R8 }, { typeof(string), VarEnum.VT_BSTR }, { typeof(DateTime), VarEnum.VT_DATE }, - { typeof(object), VarEnum.VT_UNKNOWN }, { typeof(decimal), VarEnum.VT_DECIMAL }, }; From ca55c1786464c4a0e4d5307b6fd16596e0cc606a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 May 2024 17:07:26 +0800 Subject: [PATCH 31/35] Apply suggestions from code review Co-authored-by: Aaron Robinson --- .../src/Microsoft/Win32/OAVariantLib.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index 15045de71dfe3b..cb66aa7db01dea 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -72,9 +72,9 @@ internal static unsafe partial class OAVariantLib if (Variant.IsSystemDrawingColor(targetClass)) { - if (source.GetType() == typeof(int) || source.GetType() == typeof(uint)) + if (source is int || source is uint) { - uint sourceData = source.GetType() == typeof(int) ? (uint)(int)source : (uint)source; + uint sourceData = source is int ? (uint)(int)source : (uint)source; // Int32/UInt32 can be converted to System.Drawing.Color Variant.ConvertOleColorToSystemColor(ObjectHandleOnStack.Create(ref result), sourceData, targetClass.TypeHandle.Value); Debug.Assert(result != null); @@ -83,7 +83,9 @@ internal static unsafe partial class OAVariantLib } if (!ClassTypes.TryGetValue(targetClass, out VarEnum vt)) + { throw new NotSupportedException(SR.NotSupported_ChangeType); + } ComVariant vOp = ToOAVariant(source); ComVariant ret = default; @@ -94,7 +96,9 @@ internal static unsafe partial class OAVariantLib using (ret) { if (hr < 0) + { OAFailed(hr); + } result = FromOAVariant(ret); if (targetClass == typeof(char)) From 9d79d193713ec1869b9080e29a4169b08a435dee Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 May 2024 19:45:02 +0800 Subject: [PATCH 32/35] Don't test for types that were not requested --- .../src/Microsoft/Win32/OAVariantLib.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs index cb66aa7db01dea..a51ec6f0132e47 100644 --- a/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs +++ b/src/coreclr/System.Private.CoreLib/src/Microsoft/Win32/OAVariantLib.cs @@ -21,8 +21,6 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; -#pragma warning disable CA1416 // COM interop is only supported on Windows - namespace Microsoft.Win32 { internal static unsafe partial class OAVariantLib @@ -183,9 +181,6 @@ private static ComVariant GetComIPFromObjectRef(object? obj) VarEnum.VT_UI8 => input.As(), VarEnum.VT_R4 => input.As(), VarEnum.VT_R8 => input.As(), - VarEnum.VT_EMPTY => null, - VarEnum.VT_UNKNOWN or VarEnum.VT_VARIANT => Marshal.GetObjectForIUnknown(input.GetRawDataRef()), - VarEnum.VT_VOID => null, _ => throw new NotSupportedException(SR.NotSupported_ChangeType), }; From 4334c4003331034846b470ac8a4c86814e286627 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 8 May 2024 19:47:32 +0800 Subject: [PATCH 33/35] Fix compilation of NETServer --- .../COM/NETServer/DispatchCoerceTesting.cs | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs b/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs index 0367c605df4830..deb6338d4e7ca1 100644 --- a/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs +++ b/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs @@ -22,18 +22,48 @@ public int ManagedArgument(int arg) return arg; } - public System.Reflection.Missing ReturnToManaged_Missing() + public string BoolToString() { - return System.Reflection.Missing.Value; + throw new NotImplementedException(); } - public DBNull ReturnToManaged_DBNull() + public void ReturnToManaged_Void() { - return DBNull.Value; + throw new NotImplementedException(); } - public string BoolToString() + double ReturnToManaged_Double() { throw new NotImplementedException(); } + + string ReturnToManaged_String() + { + throw new NotImplementedException(); + } + + decimal ReturnToManaged_Decimal() + { + throw new NotImplementedException(); + } + + DateTime ReturnToManaged_DateTime() + { + throw new NotImplementedException(); + } + + System.Drawing.Color ReturnToManaged_Color() + { + throw new NotImplementedException(); + } + + public System.Reflection.Missing ReturnToManaged_Missing() + { + return System.Reflection.Missing.Value; + } + + public DBNull ReturnToManaged_DBNull() + { + return DBNull.Value; + } } \ No newline at end of file From e4c4994203a59378c43eb938f88e1f3447ef3267 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 13 May 2024 01:32:14 +0800 Subject: [PATCH 34/35] Test for values in ReturnToManaged --- .../COM/NETClients/IDispatch/Program.cs | 21 +++++++----- .../COM/NETServer/DispatchCoerceTesting.cs | 16 ++++----- .../COM/NativeServer/DispatchCoerceTesting.h | 33 ++++++++----------- .../COM/ServerContracts/Server.Contracts.cs | 16 ++++----- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/tests/Interop/COM/NETClients/IDispatch/Program.cs b/src/tests/Interop/COM/NETClients/IDispatch/Program.cs index d7b430cdbad4f9..69865c8f97b826 100644 --- a/src/tests/Interop/COM/NETClients/IDispatch/Program.cs +++ b/src/tests/Interop/COM/NETClients/IDispatch/Program.cs @@ -5,6 +5,7 @@ namespace NetClient { using System; + using System.Drawing; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; @@ -278,29 +279,33 @@ static void Validate_ValueCoerce_ReturnToManaged() Console.WriteLine("Invoking void-returning method should not allocate return buffer."); // E_POINTER translates to NullReferenceException - Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Void()); + Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Void(0)); Console.WriteLine("Converting int to double should be supported."); - Assert.Equal(1234d, dispatchCoerceTesting.ReturnToManaged_Double()); + Assert.Equal(1234d, dispatchCoerceTesting.ReturnToManaged_Double(1234)); Console.WriteLine("Converting int to string should be supported."); - Assert.Equal("1234", dispatchCoerceTesting.ReturnToManaged_String()); + Assert.Equal("1234", dispatchCoerceTesting.ReturnToManaged_String(1234)); Console.WriteLine("Converting int to decimal should be supported."); - Assert.Equal(1234m, dispatchCoerceTesting.ReturnToManaged_Decimal()); + Assert.Equal(1234m, dispatchCoerceTesting.ReturnToManaged_Decimal(1234)); Console.WriteLine("Converting int to DateTime should be supported."); - Assert.Equal(new DateTime(1903, 5, 18, 0, 0, 0), dispatchCoerceTesting.ReturnToManaged_DateTime()); + Assert.Equal(new DateTime(100, 1, 1), dispatchCoerceTesting.ReturnToManaged_DateTime(-657434)); + Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_DateTime(-657435)); + Assert.Equal(new DateTime(9999, 12, 31), dispatchCoerceTesting.ReturnToManaged_DateTime(2958465)); + Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_DateTime(2958466)); Console.WriteLine("Converting int to System.Drawing.Color should be supported."); - Assert.Equal(System.Drawing.Color.FromArgb(210, 4, 0), dispatchCoerceTesting.ReturnToManaged_Color()); + Assert.Equal(Color.FromKnownColor(KnownColor.ActiveBorder), dispatchCoerceTesting.ReturnToManaged_Color(unchecked((int)0x8000000A))); + Assert.Equal(ColorTranslator.FromOle(1234), dispatchCoerceTesting.ReturnToManaged_Color(1234)); Console.WriteLine("Converting int to VT_MISSING should be rejected."); - comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Missing()); + comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_Missing(0)); Assert.Equal(unchecked((int)0x80020005), comException.HResult); Console.WriteLine("Converting int to VT_NULL should be rejected."); - comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_DBNull()); + comException = Assert.Throws(() => dispatchCoerceTesting.ReturnToManaged_DBNull(0)); Assert.Equal(unchecked((int)0x80020005), comException.HResult); // LOCAL_BOOL diff --git a/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs b/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs index deb6338d4e7ca1..37330058801ebe 100644 --- a/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs +++ b/src/tests/Interop/COM/NETServer/DispatchCoerceTesting.cs @@ -27,42 +27,42 @@ public string BoolToString() throw new NotImplementedException(); } - public void ReturnToManaged_Void() + public void ReturnToManaged_Void(int value) { throw new NotImplementedException(); } - double ReturnToManaged_Double() + public double ReturnToManaged_Double(int value) { throw new NotImplementedException(); } - string ReturnToManaged_String() + public string ReturnToManaged_String(int value) { throw new NotImplementedException(); } - decimal ReturnToManaged_Decimal() + public decimal ReturnToManaged_Decimal(int value) { throw new NotImplementedException(); } - DateTime ReturnToManaged_DateTime() + public DateTime ReturnToManaged_DateTime(int value) { throw new NotImplementedException(); } - System.Drawing.Color ReturnToManaged_Color() + public System.Drawing.Color ReturnToManaged_Color(int value) { throw new NotImplementedException(); } - public System.Reflection.Missing ReturnToManaged_Missing() + public System.Reflection.Missing ReturnToManaged_Missing(int value) { return System.Reflection.Missing.Value; } - public DBNull ReturnToManaged_DBNull() + public DBNull ReturnToManaged_DBNull(int value) { return DBNull.Value; } diff --git a/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h b/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h index 7a9aad0b9548d6..f912f7d4bca6cc 100644 --- a/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h +++ b/src/tests/Interop/COM/NativeServer/DispatchCoerceTesting.h @@ -88,9 +88,6 @@ class DispatchCoerceTesting : public UnknownImpl, public IDispatchCoerceTesting return BoolToString_Dispatch(pDispParams, pVarResult); } case 4: - { - return ReturnToManaged_Void_Dispatch(pDispParams, pVarResult); - } case 5: case 6: case 7: @@ -216,33 +213,31 @@ class DispatchCoerceTesting : public UnknownImpl, public IDispatchCoerceTesting return S_OK; } - HRESULT ReturnToManaged_Void_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) + HRESULT ReturnToManaged_Any_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) { HRESULT hr; - size_t expectedArgCount = 0; + int *args[1]; + size_t expectedArgCount = 1; RETURN_IF_FAILED(VerifyValues(uint32_t(expectedArgCount), pDispParams->cArgs)); if (pVarResult == nullptr) return E_POINTER; - V_VT(pVarResult) = VT_I4; - V_I4(pVarResult) = 1234; - return S_OK; - } - - HRESULT ReturnToManaged_Any_Dispatch(_In_ DISPPARAMS *pDispParams, _Inout_ VARIANT *pVarResult) - { - HRESULT hr; - - size_t expectedArgCount = 0; - RETURN_IF_FAILED(VerifyValues(uint32_t(expectedArgCount), pDispParams->cArgs)); + VARENUM currType; + VARIANTARG *currArg; + size_t argIdx = expectedArgCount - 1; - if (pVarResult == nullptr) - return E_POINTER; + // Extract args + { + currType = VT_I4; + currArg = NextArg(pDispParams->rgvarg, argIdx); + RETURN_IF_FAILED(VerifyValues(VARENUM(currType), VARENUM(currArg->vt))); + args[0] = &currArg->intVal; + } V_VT(pVarResult) = VT_I4; - V_I4(pVarResult) = 1234; + V_I4(pVarResult) = *args[0]; return S_OK; } diff --git a/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs b/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs index 309e30bc90b992..476899377660d9 100644 --- a/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs +++ b/src/tests/Interop/COM/ServerContracts/Server.Contracts.cs @@ -327,14 +327,14 @@ public interface IDispatchCoerceTesting int ReturnToManaged(short vt); int ManagedArgument(int arg); string BoolToString(); - void ReturnToManaged_Void(); - double ReturnToManaged_Double(); - string ReturnToManaged_String(); - decimal ReturnToManaged_Decimal(); - DateTime ReturnToManaged_DateTime(); - Color ReturnToManaged_Color(); - System.Reflection.Missing ReturnToManaged_Missing(); - DBNull ReturnToManaged_DBNull(); + void ReturnToManaged_Void(int value); + double ReturnToManaged_Double(int value); + string ReturnToManaged_String(int value); + decimal ReturnToManaged_Decimal(int value); + DateTime ReturnToManaged_DateTime(int value); + Color ReturnToManaged_Color(int value); + System.Reflection.Missing ReturnToManaged_Missing(int value); + DBNull ReturnToManaged_DBNull(int value); } [ComVisible(true)] From ae0b4f880384e930572d43f6b908013009c960b2 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 13 May 2024 17:19:51 +0800 Subject: [PATCH 35/35] Add value test in managed arg --- .../COM/NativeClients/Dispatch/Client.cpp | 147 +++++++++++------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/src/tests/Interop/COM/NativeClients/Dispatch/Client.cpp b/src/tests/Interop/COM/NativeClients/Dispatch/Client.cpp index 4c509a3df16240..b2286e25b2d03a 100644 --- a/src/tests/Interop/COM/NativeClients/Dispatch/Client.cpp +++ b/src/tests/Interop/COM/NativeClients/Dispatch/Client.cpp @@ -462,57 +462,20 @@ void Validate_Enumerator() ValidateReturnedEnumerator(&result); } -void Validate_ParamCoerce_Type(ComSmartPtr& dispatchCoerceTesting, VARENUM type, int lcid, DISPID methodId) +void Validate_ParamCoerce_Success(ComSmartPtr& dispatchCoerceTesting, int lcid, DISPID methodId, VARIANT arg, int expected) { HRESULT hr; DISPPARAMS params; - VARIANTARG arg; + VARIANTARG args; params.cArgs = 1; - params.rgvarg = &arg; + params.rgvarg = &args; params.cNamedArgs = 0; params.rgdispidNamedArgs = nullptr; + args = arg; VARIANT result; - V_VT(&arg) = type; - - switch (type) - { - case VT_BSTR: - { - BSTR str = ::SysAllocString(L"123"); - V_BSTR(&arg) = str; - break; - } - case VT_R4: - { - V_R4(&arg) = 1.23f; - break; - } - case VT_DATE: - case VT_R8: - { - V_R8(&arg) = 1.23; - break; - } - case VT_CY: - { - VarCyFromI4(123, &V_CY(&arg)); - break; - } - case VT_DECIMAL: - { - VarDecFromI4(123, &V_DECIMAL(&arg)); - break; - } - default: - { - V_I1(&arg) = 123; - break; - } - } - THROW_IF_FAILED(dispatchCoerceTesting->Invoke( methodId, IID_NULL, @@ -524,7 +487,35 @@ void Validate_ParamCoerce_Type(ComSmartPtr& dispatchCoer nullptr )); - THROW_FAIL_IF_FALSE(V_I4(&result) != 0); + THROW_FAIL_IF_FALSE(V_I4(&result) == expected); +} + +void Validate_ParamCoerce_Exception(ComSmartPtr& dispatchCoerceTesting, int lcid, DISPID methodId, VARIANT arg, HRESULT expected) +{ + HRESULT hr; + + DISPPARAMS params; + VARIANTARG args; + params.cArgs = 1; + params.rgvarg = &args; + params.cNamedArgs = 0; + params.rgdispidNamedArgs = nullptr; + + args = arg; + VARIANT result; + + hr = dispatchCoerceTesting->Invoke( + methodId, + IID_NULL, + lcid, + DISPATCH_METHOD, + ¶ms, + &result, + nullptr, + nullptr + ); + + THROW_FAIL_IF_FALSE(hr == expected); } void Validate_ParamCoerce() @@ -548,24 +539,68 @@ void Validate_ParamCoerce() lcid, &methodId)); + VARIANT arg; + + ::wprintf(W("Validating VT_UI4\n")); + V_VT(&arg) = VT_UI4; + V_UI4(&arg) = 0x1234ABCD; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 0x1234ABCD); + ::wprintf(W("Validating VT_I2\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_I2, lcid, methodId); - ::wprintf(W("Validating VT_I4\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_I4, lcid, methodId); - ::wprintf(W("Validating VT_R4\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_R4, lcid, methodId); + V_VT(&arg) = VT_I2; + V_I2(&arg) = 123; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 123); + + ::wprintf(W("Validating VT_I8\n")); + V_VT(&arg) = VT_I8; + V_I8(&arg) = int64_t(1) << 32; + Validate_ParamCoerce_Exception(dispatchCoerceTesting, lcid, methodId, arg, DISP_E_OVERFLOW); + ::wprintf(W("Validating VT_R8\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_R8, lcid, methodId); + V_VT(&arg) = VT_R8; + V_R8(&arg) = 123.45; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 123); + ::wprintf(W("Validating VT_CY\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_CY, lcid, methodId); - ::wprintf(W("Validating VT_DATE\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_DATE, lcid, methodId); + V_VT(&arg) = VT_CY; + V_I8(&arg) = 123456; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 12); + ::wprintf(W("Validating VT_BSTR\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_BSTR, lcid, methodId); - ::wprintf(W("Validating VT_ERROR\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_ERROR, lcid, methodId); + V_VT(&arg) = VT_BSTR; + V_BSTR(&arg) = ::SysAllocString(L"123"); + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 123); + ::wprintf(W("Validating VT_BOOL\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_BOOL, lcid, methodId); + V_VT(&arg) = VT_BOOL; + V_BOOL(&arg) = VARIANT_TRUE; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, -1); + V_VT(&arg) = VT_BOOL; + V_I4(&arg) = 123; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, -1); + V_VT(&arg) = VT_BOOL; + V_BOOL(&arg) = VARIANT_FALSE; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 0); + + ::wprintf(W("Validating VT_DATE\n")); + V_VT(&arg) = VT_DATE; + V_R8(&arg) = -657434.0; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, -657434); + V_VT(&arg) = VT_DATE; + V_R8(&arg) = -657435.0; + Validate_ParamCoerce_Exception(dispatchCoerceTesting, lcid, methodId, arg, E_INVALIDARG); + V_VT(&arg) = VT_DATE; + V_R8(&arg) = 2958465.0; + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 2958465); + V_VT(&arg) = VT_DATE; + V_R8(&arg) = 2958466.0; + Validate_ParamCoerce_Exception(dispatchCoerceTesting, lcid, methodId, arg, E_INVALIDARG); + ::wprintf(W("Validating VT_DECIMAL\n")); - Validate_ParamCoerce_Type(dispatchCoerceTesting, VT_DECIMAL, lcid, methodId); + V_VT(&arg) = VT_DECIMAL; + VarDecFromI4(123, &V_DECIMAL(&arg)); + Validate_ParamCoerce_Success(dispatchCoerceTesting, lcid, methodId, arg, 123); + V_VT(&arg) = VT_DECIMAL; + VarDecFromI8(int64_t(1) << 32, &V_DECIMAL(&arg)); + Validate_ParamCoerce_Exception(dispatchCoerceTesting, lcid, methodId, arg, DISP_E_OVERFLOW); }