Skip to content

Commit

Permalink
IPropertyValue various fixes (#210)
Browse files Browse the repository at this point in the history
* Provide IReference<> and IPropertyValue for System.Type objects.

* Add more coercion checks. Fix DateTimeOffset and TimeSpan boxing.
  • Loading branch information
jkoritzinsky authored Apr 24, 2020
1 parent e44df1b commit 230b31b
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 105 deletions.
12 changes: 8 additions & 4 deletions WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ internal static Func<IInspectable, object> CreateTypedRcwFactory(string runtimeC
{
return (IInspectable obj) => new ABI.System.Nullable<String>(obj.ObjRef);
}
else if (runtimeClassName == "Windows.Foundation.IReference`1<Windows.UI.Xaml.Interop.TypeName>")
{
return (IInspectable obj) => new ABI.System.Nullable<Type>(obj.ObjRef);
}

var (implementationType, _) = TypeNameSupport.FindTypeByName(runtimeClassName.AsSpan());

Expand Down Expand Up @@ -339,7 +343,7 @@ internal static Func<IInspectable, object> 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;
}


Expand Down Expand Up @@ -634,12 +638,12 @@ private static ComInterfaceEntry ProvideIReferenceArray(object obj)
Vtable = BoxedArrayIReferenceArrayImpl<object>.AbiToProjectionVftablePtr
};
}
if (obj is global::System.Type)
if (obj is Type)
{
return new ComInterfaceEntry
{
IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray<global::System.Type>)),
Vtable = BoxedArrayIReferenceArrayImpl<global::System.Type>.AbiToProjectionVftablePtr
IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray<Type>)),
Vtable = BoxedArrayIReferenceArrayImpl<Type>.AbiToProjectionVftablePtr
};
}
return new ComInterfaceEntry
Expand Down
9 changes: 8 additions & 1 deletion WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>.
return rcw is ABI.System.Nullable<string> 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<string> ns => ns.Value,
ABI.System.Nullable<Type> nt => nt.Value,
_ => rcw
};
}

public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) => TryRegisterObjectForInterface(obj, thisPtr);
Expand Down
9 changes: 8 additions & 1 deletion WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>.
return rcw is ABI.System.Nullable<string> 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<string> ns => ns.Value,
ABI.System.Nullable<Type> nt => nt.Value,
_ => rcw
};
}

public static void RegisterObjectForInterface(object obj, IntPtr thisPtr)
Expand Down
222 changes: 123 additions & 99 deletions WinRT.Runtime/Projections/IPropertyValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,19 @@ private static T[] UnboxArray<T>(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 _);
}

/// <summary>
/// Coerce the managd object to an object of type <typeparamref name="T"/>.
/// </summary>
Expand All @@ -285,42 +298,6 @@ private static T CoerceValue<T>(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))
Expand Down Expand Up @@ -361,6 +338,45 @@ private static T CoerceValue<T>(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();
}

Expand Down Expand Up @@ -582,7 +598,7 @@ private static unsafe int Do_Abi_GetDateTime_15(IntPtr thisPtr, out global::ABI.
value = default;
try
{
value = CoerceValue<System.DateTimeOffset>(global::WinRT.ComWrappersSupport.FindObject<object>(thisPtr));
value = ABI.System.DateTimeOffset.FromManaged(CoerceValue<global::System.DateTimeOffset>(global::WinRT.ComWrappersSupport.FindObject<object>(thisPtr)));
}
catch (global::System.Exception __exception__)
{
Expand All @@ -596,7 +612,7 @@ private static unsafe int Do_Abi_GetTimeSpan_16(IntPtr thisPtr, out global::ABI.
value = default;
try
{
value = CoerceValue<System.TimeSpan>(global::WinRT.ComWrappersSupport.FindObject<object>(thisPtr));
value = ABI.System.TimeSpan.FromManaged(CoerceValue<global::System.TimeSpan>(global::WinRT.ComWrappersSupport.FindObject<object>(thisPtr)));
}
catch (global::System.Exception __exception__)
{
Expand Down Expand Up @@ -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<object>(thisPtr).GetType();
bool isArray = managedType.IsArray;
if (isArray)
value = GetPropertyTypeOfObject(global::WinRT.ComWrappersSupport.FindObject<object>(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;
}
}

Expand Down

0 comments on commit 230b31b

Please sign in to comment.