Skip to content

Commit

Permalink
revert binary breaking change for generic MarshalInspectable for 0.8.…
Browse files Browse the repository at this point in the history
…0 release
  • Loading branch information
Scottj1s committed Oct 12, 2020
1 parent ee9038a commit 2fe1845
Show file tree
Hide file tree
Showing 33 changed files with 294 additions and 365 deletions.
8 changes: 4 additions & 4 deletions Projections/Test/TestHost.ProbeByHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private static unsafe int Do_Abi_ActivateInstance_0(IntPtr thisPtr, out IntPtr i
try
{
__instance = global::WinRT.ComWrappersSupport.FindObject<global::Windows.Foundation.IActivationFactory>(thisPtr).ActivateInstance();
instance = MarshalInspectable<object>.FromManaged(__instance);
instance = MarshalInspectable.FromManaged(__instance);
}
catch (Exception __exception__)
{
Expand Down Expand Up @@ -84,11 +84,11 @@ public unsafe object ActivateInstance()
try
{
global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ActivateInstance_0(ThisPtr, out __retval));
return MarshalInspectable<object>.FromAbi(__retval);
return MarshalInspectable.FromAbi(__retval);
}
finally
{
MarshalInspectable<object>.DisposeAbi(__retval);
MarshalInspectable.DisposeAbi(__retval);
}
}
}
Expand Down Expand Up @@ -123,7 +123,7 @@ public static unsafe IntPtr GetActivationFactory(String runtimeClassId)
if (ctor != null)
{
var factory = new WinRT.Host.ActivationFactory(ctor);
return MarshalInspectable<WinRT.Host.ActivationFactory>.FromManaged(factory);
return MarshalInspectable.FromManaged(factory);
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private static unsafe int Do_Abi_ActivateInstance_0(IntPtr thisPtr, out IntPtr i
try
{
__instance = global::WinRT.ComWrappersSupport.FindObject<global::Windows.Foundation.IActivationFactory>(thisPtr).ActivateInstance();
instance = MarshalInspectable<object>.FromManaged(__instance);
instance = MarshalInspectable.FromManaged(__instance);
}
catch (Exception __exception__)
{
Expand Down Expand Up @@ -84,11 +84,11 @@ public unsafe object ActivateInstance()
try
{
global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ActivateInstance_0(ThisPtr, out __retval));
return MarshalInspectable<object>.FromAbi(__retval);
return MarshalInspectable.FromAbi(__retval);
}
finally
{
MarshalInspectable<object>.DisposeAbi(__retval);
MarshalInspectable.DisposeAbi(__retval);
}
}
}
Expand Down Expand Up @@ -123,7 +123,7 @@ public static unsafe IntPtr GetActivationFactory(String runtimeClassId)
if (ctor != null)
{
var factory = new WinRT.Host.ActivationFactory(ctor);
return MarshalInspectable<WinRT.Host.ActivationFactory>.FromManaged(factory);
return MarshalInspectable.FromManaged(factory);
}
}
}
Expand Down
25 changes: 2 additions & 23 deletions UnitTest/TestComponentCSharp_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
using WinRT.Interop;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;

namespace UnitTest
{
Expand Down Expand Up @@ -89,13 +87,9 @@ public void TestUri()
(object sender, Uri value) => Assert.Equal(managedUri, value);
TestObject.RaiseUriChanged();

var uri2 = MarshalInspectable<System.Uri>.FromAbi(ABI.System.Uri.FromManaged(managedUri));
var uri2 = MarshalInspectable.FromAbi(ABI.System.Uri.FromManaged(managedUri));
var str2 = uri2.ToString();
Assert.Equal(full_uri, str2);

var uri3 = MarshalInspectable<object>.FromAbi(ABI.System.Uri.FromManaged(managedUri));
var str3 = uri3.ToString();
Assert.Equal(full_uri, str3);
}

[Fact]
Expand Down Expand Up @@ -1621,13 +1615,9 @@ public void WeakReferenceOfNativeObjectRehydratedAfterWrapperIsCollected()
[Fact]
public void TestUnwrapInspectable()
{
using var objRef = MarshalInspectable<object>.CreateMarshaler(TestObject);
using var objRef = MarshalInspectable.CreateMarshaler(TestObject);
var inspectable = IInspectable.FromAbi(objRef.ThisPtr);
Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable, out _));

using var objRef2 = MarshalInspectable<Class>.CreateMarshaler(TestObject);
var inspectable2 = IInspectable.FromAbi(objRef2.ThisPtr);
Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable2, out _));
}

[Fact]
Expand Down Expand Up @@ -1783,16 +1773,5 @@ public void TestInterfaceObjectMarshalling()

Assert.Equal(TestObject.ReadWriteProperty, nativeProperties.ReadWriteProperty);
}

// Test scenario where type reported by runtimeclass name is not a valid type (i.e. internal type).
[Fact]
public void TestNonProjectedRuntimeClass()
{
string key = ".....";
IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
MacAlgorithmProvider mac = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1);
CryptographicKey cryptoKey = mac.CreateKey(keyMaterial);
Assert.NotNull(cryptoKey);
}
}
}
2 changes: 1 addition & 1 deletion WinRT.Runtime/AgileReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public unsafe AgileReference(IObjectReference instance)
public new T Get()
{
using var objRef = base.Get();
return ComWrappersSupport.CreateRcwForComObject<T>(objRef?.ThisPtr ?? IntPtr.Zero);
return (T) ComWrappersSupport.CreateRcwForComObject(objRef?.ThisPtr ?? IntPtr.Zero);
}
}
}
2 changes: 1 addition & 1 deletion WinRT.Runtime/CastExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static TInterface As<TInterface>(this object value)
{
using (objRef)
{
return ComWrappersSupport.CreateRcwForComObject<TInterface>(objRef.ThisPtr);
return (TInterface)ComWrappersSupport.CreateRcwForComObject(objRef.ThisPtr);
}
}
}
Expand Down
72 changes: 10 additions & 62 deletions WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public static partial class ComWrappersSupport
{
private readonly static ConcurrentDictionary<string, Func<IInspectable, object>> TypedObjectFactoryCache = new ConcurrentDictionary<string, Func<IInspectable, object>>();
private readonly static ConditionalWeakTable<object, object> CCWTable = new ConditionalWeakTable<object, object>();
private readonly static ConcurrentDictionary<Type, MemberInfo> TypeObjectRefFieldCache = new ConcurrentDictionary<Type, MemberInfo>();

public static TReturn MarshalDelegateInvoke<TDelegate, TReturn>(IntPtr thisPtr, Func<TDelegate, TReturn> invoke)
where TDelegate : class, Delegate
Expand Down Expand Up @@ -69,33 +68,20 @@ public static bool TryUnwrapObject(object o, out IObjectReference objRef)
}

Type type = o.GetType();

var objRefField = TypeObjectRefFieldCache.GetOrAdd(type, (type) =>
{
ObjectReferenceWrapperAttribute objRefWrapper = type.GetCustomAttribute<ObjectReferenceWrapperAttribute>();
if (objRefWrapper is object)
{
return type.GetField(objRefWrapper.ObjectReferenceField, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
ProjectedRuntimeClassAttribute projectedClass = type.GetCustomAttribute<ProjectedRuntimeClassAttribute>();
if (projectedClass is object && projectedClass.DefaultInterfaceProperty != null)
{
return type.GetProperty(projectedClass.DefaultInterfaceProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
return null;
});


if(objRefField is FieldInfo field)
ObjectReferenceWrapperAttribute objRefWrapper = type.GetCustomAttribute<ObjectReferenceWrapperAttribute>();
if (objRefWrapper is object)
{
objRef = (IObjectReference)field.GetValue(o);
objRef = (IObjectReference)type.GetField(objRefWrapper.ObjectReferenceField, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).GetValue(o);
return true;
}
else if(objRefField is PropertyInfo defaultProperty)

ProjectedRuntimeClassAttribute projectedClass = type.GetCustomAttribute<ProjectedRuntimeClassAttribute>();

if (projectedClass is object && projectedClass.DefaultInterfaceProperty != null)
{
return TryUnwrapObject(defaultProperty.GetValue(o), out objRef);
return TryUnwrapObject(
type.GetProperty(projectedClass.DefaultInterfaceProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).GetValue(o),
out objRef);
}

objRef = null;
Expand Down Expand Up @@ -342,44 +328,6 @@ internal static Func<IInspectable, object> CreateTypedRcwFactory(string runtimeC
return CreateFactoryForImplementationType(runtimeClassName, implementationType);
}

internal static string GetRuntimeClassForTypeCreation(IInspectable inspectable, Type staticallyDeterminedType)
{
string runtimeClassName = inspectable.GetRuntimeClassName(noThrow: true);
if (staticallyDeterminedType != null && staticallyDeterminedType != typeof(object))
{
// We have a static type which we can use to construct the object. But, we can't just use it for all scenarios
// and primarily use it for tear off scenarios and for scenarios where runtimeclass isn't accurate.
// For instance if the static type is an interface, we return an IInspectable to represent the interface.
// But it isn't convertable back to the class via the as operator which would be possible if we use runtimeclass.
// Similarly for composable types, they can be statically retrieved using the parent class, but can then no longer
// be cast to the sub class via as operator even if it is really an instance of it per rutimeclass.
// To handle these scenarios, we use the runtimeclass if we find it is assignable to the statically determined type.
// If it isn't, we use the statically determined type as it is a tear off.

Type implementationType = null;
if (runtimeClassName != null)
{
try
{
(implementationType, _) = TypeNameSupport.FindTypeByName(runtimeClassName.AsSpan());
}
catch (TypeLoadException)
{
}
}

if (!(implementationType != null &&
(staticallyDeterminedType == implementationType ||
staticallyDeterminedType.IsAssignableFrom(implementationType) ||
staticallyDeterminedType.IsGenericType && implementationType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == staticallyDeterminedType.GetGenericTypeDefinition()))))
{
runtimeClassName = TypeNameSupport.GetNameForType(staticallyDeterminedType, TypeNameGenerationFlags.GenerateBoxedName);
}
}

return runtimeClassName;
}

private static bool ShouldProvideIReference(object obj)
{
return obj.GetType().IsValueType || obj is string || obj is Type || obj is Delegate;
Expand Down
49 changes: 20 additions & 29 deletions WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using WinRT.Interop;
using static System.Runtime.InteropServices.ComWrappers;

namespace WinRT
{
public static partial class ComWrappersSupport
{
internal static readonly ConditionalWeakTable<object, InspectableInfo> InspectableInfoTable = new ConditionalWeakTable<object, InspectableInfo>();
internal static readonly ThreadLocal<Type> CreateRCWType = new ThreadLocal<Type>();
internal static readonly ConditionalWeakTable<object, InspectableInfo> InspectableInfoTable = new ConditionalWeakTable<object, InspectableInfo>();

private static ComWrappers _comWrappers;
private static object _comWrappersLock = new object();
Expand Down Expand Up @@ -52,33 +50,26 @@ internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis)
return InspectableInfoTable.GetValue(_this, o => PregenerateNativeTypeInformation(o).inspectableInfo);
}

public static T CreateRcwForComObject<T>(IntPtr ptr)
public static object CreateRcwForComObject(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
{
return default;
return null;
}

// CreateRCWType is a thread local which is set here to communicate the statically known type
// when we are called by the ComWrappers API to create the object. We can't pass this through the
// ComWrappers API surface, so we are achieving it via a thread local. We unset it after in case
// there is other calls to it via other means.
CreateRCWType.Value = typeof(T);
var rcw = ComWrappers.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.TrackerObject);
CreateRCWType.Value = null;
// Because .NET will de-duplicate strings and WinRT doesn't,
// our RCW factory returns a wrapper of our string instance.
// 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>.
// We need to do the same thing for System.Type because there can be multiple WUX.Interop.TypeName's
// for a single System.Type.

// Because .NET will de-duplicate strings and WinRT doesn't,
// our RCW factory returns a wrapper of our string instance.
// 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>.
// 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 => (T)(object) ns.Value,
ABI.System.Nullable<Type> nt => (T)(object) nt.Value,
_ => (T) rcw
ABI.System.Nullable<string> ns => ns.Value,
ABI.System.Nullable<Type> nt => nt.Value,
_ => rcw
};
}

Expand Down Expand Up @@ -220,13 +211,13 @@ protected override object CreateObject(IntPtr externalComObject, CreateObjectFla
{
IInspectable inspectable = new IInspectable(inspectableRef);

string runtimeClassName = ComWrappersSupport.GetRuntimeClassForTypeCreation(inspectable, ComWrappersSupport.CreateRCWType.Value);
if (runtimeClassName == null)
{
// If the external IInspectable has not implemented GetRuntimeClassName,
// we use the Inspectable wrapper directly.
return inspectable;
}
string runtimeClassName = inspectable.GetRuntimeClassName(noThrow: true);
if (runtimeClassName == null)
{
// If the external IInspectable has not implemented GetRuntimeClassName,
// we use the Inspectable wrapper directly.
return inspectable;
}
return ComWrappersSupport.GetTypedRcwFactory(runtimeClassName)(inspectable);
}
else if (objRef.TryAs<ABI.WinRT.Interop.IWeakReference.Vftbl>(out var weakRef) == 0)
Expand Down
34 changes: 17 additions & 17 deletions WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public static partial class ComWrappersSupport

internal static InspectableInfo GetInspectableInfo(IntPtr pThis) => UnmanagedObject.FindObject<ComCallableWrapper>(pThis).InspectableInfo;

public static T CreateRcwForComObject<T>(IntPtr ptr)
public static object CreateRcwForComObject(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
{
return default;
return null;
}

IObjectReference identity = GetObjectReferenceForInterface(ptr).As<IUnknownVftbl>();
Expand All @@ -34,8 +34,8 @@ public static T CreateRcwForComObject<T>(IntPtr ptr)
object runtimeWrapper = null;
if (identity.TryAs<IInspectable.Vftbl>(out var inspectableRef) == 0)
{
var inspectable = new IInspectable(identity);
string runtimeClassName = GetRuntimeClassForTypeCreation(inspectable, typeof(T));
var inspectable = new IInspectable(identity);
string runtimeClassName = inspectable.GetRuntimeClassName();
runtimeWrapper = TypedObjectFactoryCache.GetOrAdd(runtimeClassName, className => CreateTypedRcwFactory(className))(inspectable);
}
else if (identity.TryAs<ABI.WinRT.Interop.IWeakReference.Vftbl>(out var weakRef) == 0)
Expand All @@ -60,21 +60,21 @@ public static T CreateRcwForComObject<T>(IntPtr ptr)
return oldValue;
}).TryGetTarget(out object rcw);

GC.KeepAlive(keepAliveSentinel);

// Because .NET will de-duplicate strings and WinRT doesn't,
// our RCW factory returns a wrapper of our string instance.
// 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>.
// We need to do the same thing for System.Type because there can be multiple MUX.Interop.TypeName's
// for a single System.Type.
GC.KeepAlive(keepAliveSentinel);

// Because .NET will de-duplicate strings and WinRT doesn't,
// our RCW factory returns a wrapper of our string instance.
// 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>.
// 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 => (T)(object)ns.Value,
ABI.System.Nullable<Type> nt => (T)(object)nt.Value,
_ => (T)rcw
};
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
Loading

0 comments on commit 2fe1845

Please sign in to comment.