diff --git a/WinRT.Runtime/ComWrappersSupport.cs b/WinRT.Runtime/ComWrappersSupport.cs index 8cd8caef1..53242c133 100644 --- a/WinRT.Runtime/ComWrappersSupport.cs +++ b/WinRT.Runtime/ComWrappersSupport.cs @@ -272,6 +272,10 @@ internal static Func CreateTypedRcwFactory(string runtimeC { return (IInspectable obj) => new ABI.System.Nullable(obj.ObjRef); } + else if (runtimeClassName == "Windows.Foundation.IReference`1") + { + return (IInspectable obj) => new ABI.System.Nullable(obj.ObjRef); + } var (implementationType, _) = TypeNameSupport.FindTypeByName(runtimeClassName.AsSpan()); @@ -339,7 +343,7 @@ internal static Func CreateTypedRcwFactory(string runtimeC private static bool ShouldProvideIReference(object obj) { - return obj.GetType().IsValueType || obj is string; + return obj.GetType().IsValueType || obj is string || obj is Type; } @@ -634,12 +638,12 @@ private static ComInterfaceEntry ProvideIReferenceArray(object obj) Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } - if (obj is global::System.Type) + if (obj is Type) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), - Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } return new ComInterfaceEntry diff --git a/WinRT.Runtime/ComWrappersSupport.net5.cs b/WinRT.Runtime/ComWrappersSupport.net5.cs index ec0cf9496..ca46c0057 100644 --- a/WinRT.Runtime/ComWrappersSupport.net5.cs +++ b/WinRT.Runtime/ComWrappersSupport.net5.cs @@ -46,7 +46,14 @@ public static object CreateRcwForComObject(IntPtr ptr) // This ensures that ComWrappers never sees the same managed object for two different // native pointers. We unwrap here to ensure that the user-experience is expected // and consumers get a string object for a Windows.Foundation.IReference. - return rcw is ABI.System.Nullable ns ? ns.Value : rcw; + // We need to do the same thing for System.Type because there can be multiple WUX.Interop.TypeName's + // for a single System.Type. + return rcw switch + { + ABI.System.Nullable ns => ns.Value, + ABI.System.Nullable nt => nt.Value, + _ => rcw + }; } public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) => TryRegisterObjectForInterface(obj, thisPtr); diff --git a/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs b/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs index 45084f223..afd019fd4 100644 --- a/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs +++ b/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs @@ -57,7 +57,14 @@ public static object CreateRcwForComObject(IntPtr ptr) // This ensures that our cache never sees the same managed object for two different // native pointers. We unwrap here to ensure that the user-experience is expected // and consumers get a string object for a Windows.Foundation.IReference. - return rcw is ABI.System.Nullable ns ? ns.Value : rcw; + // We need to do the same thing for System.Type because there can be multiple MUX.Interop.TypeName's + // for a single System.Type. + return rcw switch + { + ABI.System.Nullable ns => ns.Value, + ABI.System.Nullable nt => nt.Value, + _ => rcw + }; } public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) diff --git a/WinRT.Runtime/Projections/IPropertyValue.cs b/WinRT.Runtime/Projections/IPropertyValue.cs index 709518305..75f1c92e5 100644 --- a/WinRT.Runtime/Projections/IPropertyValue.cs +++ b/WinRT.Runtime/Projections/IPropertyValue.cs @@ -272,6 +272,19 @@ private static T[] UnboxArray(object value) return coercedArray; } + private static bool IsCoercable(object value) + { + // String <--> Guid is allowed + // Converting from an object to a string, Guid, or numeric scalar is allowed. + if (value.GetType() == typeof(string) || value.GetType() == typeof(Guid) || value.GetType() != typeof(object)) + { + return true; + } + + // All numeric scalars can also be coerced + return NumericScalarTypes.TryGetValue(value.GetType(), out _); + } + /// /// Coerce the managd object to an object of type . /// @@ -285,42 +298,6 @@ private static T CoerceValue(object value) return u; } - // TODO: Check if the value can be coerced. - - try - { - if (value is string str && typeof(T) == typeof(Guid)) - { - return (T)(object)Guid.Parse(str); - } - else if (value is Guid guid && typeof(T) == typeof(string)) - { - return (T)(object)guid.ToString("D", global::System.Globalization.CultureInfo.InvariantCulture); - } - else - { - if (NumericScalarTypes.TryGetValue(typeof(T), out _)) - { - return (T)Convert.ChangeType(value, typeof(T), global::System.Globalization.CultureInfo.InvariantCulture); - } - } - } - catch (FormatException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); - throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); - } - catch (InvalidCastException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); - throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); - } - catch (OverflowException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, value.GetType(), value, typeof(T).Name), DISP_E_OVERFLOW); - throw new InvalidCastException("", DISP_E_OVERFLOW); - } - if (value is global::Windows.Foundation.IPropertyValue ipv) { if (typeof(T) == typeof(byte)) @@ -361,6 +338,45 @@ private static T CoerceValue(object value) } } + if (!IsCoercable(value)) + { + throw new InvalidCastException(); + } + + try + { + if (value is string str && typeof(T) == typeof(Guid)) + { + return (T)(object)Guid.Parse(str); + } + else if (value is Guid guid && typeof(T) == typeof(string)) + { + return (T)(object)guid.ToString("D", global::System.Globalization.CultureInfo.InvariantCulture); + } + else + { + if (NumericScalarTypes.TryGetValue(typeof(T), out _)) + { + return (T)Convert.ChangeType(value, typeof(T), global::System.Globalization.CultureInfo.InvariantCulture); + } + } + } + catch (FormatException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + catch (InvalidCastException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + catch (OverflowException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, value.GetType(), value, typeof(T).Name), DISP_E_OVERFLOW); + throw new InvalidCastException("", DISP_E_OVERFLOW); + } + throw new InvalidCastException(); } @@ -582,7 +598,7 @@ private static unsafe int Do_Abi_GetDateTime_15(IntPtr thisPtr, out global::ABI. value = default; try { - value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + value = ABI.System.DateTimeOffset.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); } catch (global::System.Exception __exception__) { @@ -596,7 +612,7 @@ private static unsafe int Do_Abi_GetTimeSpan_16(IntPtr thisPtr, out global::ABI. value = default; try { - value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + value = ABI.System.TimeSpan.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); } catch (global::System.Exception __exception__) { @@ -1035,76 +1051,84 @@ private static unsafe int Do_Abi_get_Type_0(IntPtr thisPtr, out global::Windows. value = default; try { - global::System.Type managedType = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetType(); - bool isArray = managedType.IsArray; - if (isArray) + value = GetPropertyTypeOfObject(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe global::Windows.Foundation.PropertyType GetPropertyTypeOfObject(object obj) + { + global::Windows.Foundation.PropertyType value; + global::System.Type managedType = obj.GetType(); + bool isArray = managedType.IsArray; + if (isArray) + { + managedType = managedType.GetElementType(); + } + if (!NumericScalarTypes.TryGetValue(managedType, out value)) + { + if (managedType == typeof(string)) { - managedType = managedType.GetElementType(); + value = global::Windows.Foundation.PropertyType.String; } - if (!NumericScalarTypes.TryGetValue(managedType, out value)) + else if (managedType == typeof(char)) { - if (managedType == typeof(string)) - { - value = global::Windows.Foundation.PropertyType.String; - } - else if (managedType == typeof(char)) - { - value = global::Windows.Foundation.PropertyType.Char16; - } - else if (managedType == typeof(bool)) - { - value = global::Windows.Foundation.PropertyType.Boolean; - } - else if (managedType == typeof(global::System.DateTimeOffset)) - { - value = global::Windows.Foundation.PropertyType.DateTime; - } - else if (managedType == typeof(global::System.TimeSpan)) - { - value = global::Windows.Foundation.PropertyType.TimeSpan; - } - else if (managedType == typeof(global::System.Guid)) - { - value = global::Windows.Foundation.PropertyType.Guid; - } - else if (managedType.FullName == "Windows.Foundation.Point") - { - value = global::Windows.Foundation.PropertyType.Point; - } - else if (managedType.FullName == "Windows.Foundation.Rect") - { - value = global::Windows.Foundation.PropertyType.Rect; - } - else if (managedType.FullName == "Windows.Foundation.Size") - { - value = global::Windows.Foundation.PropertyType.Size; - } - else if (managedType == typeof(object)) - { - value = global::Windows.Foundation.PropertyType.Inspectable; - } - else if (!managedType.IsValueType && isArray) - { - // Treat arrays of interfaces as though they are arrays of object. - value = global::Windows.Foundation.PropertyType.Inspectable; - } - else - { - value = global::Windows.Foundation.PropertyType.OtherType; - } + value = global::Windows.Foundation.PropertyType.Char16; } - if (isArray) + else if (managedType == typeof(bool)) { - // The array values for Windows.Foundation.PropertyType are all 1024 above their scalar equivalents - value = (global::Windows.Foundation.PropertyType)((int)value + 1024); + value = global::Windows.Foundation.PropertyType.Boolean; + } + else if (managedType == typeof(global::System.DateTimeOffset)) + { + value = global::Windows.Foundation.PropertyType.DateTime; + } + else if (managedType == typeof(global::System.TimeSpan)) + { + value = global::Windows.Foundation.PropertyType.TimeSpan; + } + else if (managedType == typeof(global::System.Guid)) + { + value = global::Windows.Foundation.PropertyType.Guid; + } + else if (managedType.FullName == "Windows.Foundation.Point") + { + value = global::Windows.Foundation.PropertyType.Point; + } + else if (managedType.FullName == "Windows.Foundation.Rect") + { + value = global::Windows.Foundation.PropertyType.Rect; + } + else if (managedType.FullName == "Windows.Foundation.Size") + { + value = global::Windows.Foundation.PropertyType.Size; + } + else if (managedType == typeof(object)) + { + value = global::Windows.Foundation.PropertyType.Inspectable; + } + else if (!managedType.IsValueType && managedType != typeof(Type) && isArray) + { + // Treat arrays of interfaces as though they are arrays of object. + value = global::Windows.Foundation.PropertyType.Inspectable; + } + else + { + value = global::Windows.Foundation.PropertyType.OtherType; } } - catch (global::System.Exception __exception__) + if (isArray) { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + // The array values for Windows.Foundation.PropertyType are all 1024 above their scalar equivalents + value = (global::Windows.Foundation.PropertyType)((int)value + 1024); } - return 0; + + return value; } }