From 0a5a609940d313b2a50cb4b4e1f24c3a740188e2 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 9 Jun 2020 09:04:49 -0700 Subject: [PATCH] Fallback for RoGetAgileReference when it isn't available. --- UnitTest/TestComponentCSharp_Tests.cs | 5 +- WinRT.Runtime/AgileReference.cs | 56 ++++++++++++++++++-- WinRT.Runtime/Interop/IAgileReference.cs | 66 ++++++++++++++++++++++++ cswinrt/strings/WinRT.cs | 3 ++ 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/UnitTest/TestComponentCSharp_Tests.cs b/UnitTest/TestComponentCSharp_Tests.cs index b143a2561..d6f411c26 100644 --- a/UnitTest/TestComponentCSharp_Tests.cs +++ b/UnitTest/TestComponentCSharp_Tests.cs @@ -1514,12 +1514,12 @@ public void TestUnwrapInspectable() [Fact] public void TestManagedAgileObject() { - var testObjectAgileRef = TestObject.AsAgile(); + using var testObjectAgileRef = TestObject.AsAgile(); var agileTestObject = testObjectAgileRef.Get(); Assert.Equal(TestObject, agileTestObject); IProperties1 properties = new ManagedProperties(42); - var propertiesAgileRef = properties.AsAgile(); + using var propertiesAgileRef = properties.AsAgile(); var agileProperties = propertiesAgileRef.Get(); Assert.Equal(properties.ReadWriteProperty, agileProperties.ReadWriteProperty); @@ -1543,6 +1543,7 @@ public void AcquireObject() // Call to proxy object acquired from MTA which should throw Assert.ThrowsAny(() => proxyObject.Commands.Count); + agileReference.Dispose(); } public void CheckValue() diff --git a/WinRT.Runtime/AgileReference.cs b/WinRT.Runtime/AgileReference.cs index 7bf0249ba..ec5206b9b 100644 --- a/WinRT.Runtime/AgileReference.cs +++ b/WinRT.Runtime/AgileReference.cs @@ -4,14 +4,19 @@ namespace WinRT { - public class AgileReference + public class AgileReference : IDisposable { + private readonly static Guid CLSID_StdGlobalInterfaceTable = Guid.Parse("00000323-0000-0000-c000-000000000046"); private readonly IAgileReference _agileReference; + private readonly IGlobalInterfaceTable _git; + private readonly IntPtr _cookie; + private bool disposed; public unsafe AgileReference(IObjectReference instance) { + IntPtr agileReference = default; + IntPtr gitPtr = default; Guid iid = typeof(IUnknownVftbl).GUID; - IntPtr agileReference = IntPtr.Zero; try { Marshal.ThrowExceptionForHR(Platform.RoGetAgileReference( @@ -21,13 +26,58 @@ public unsafe AgileReference(IObjectReference instance) &agileReference)); _agileReference = ABI.WinRT.Interop.IAgileReference.FromAbi(agileReference).AsType(); } + catch(TypeLoadException) + { + Guid gitClsid = CLSID_StdGlobalInterfaceTable; + Guid gitIid = typeof(IGlobalInterfaceTable).GUID; + Marshal.ThrowExceptionForHR(Platform.CoCreateInstance( + ref gitClsid, + IntPtr.Zero, + 1 /*CLSCTX_INPROC_SERVER*/, + ref gitIid, + &gitPtr)); + _git = ABI.WinRT.Interop.IGlobalInterfaceTable.FromAbi(gitPtr).AsType(); + _cookie = _git.RegisterInterfaceInGlobal(instance, iid); + + } finally { MarshalInterface.DisposeAbi(agileReference); + MarshalInterface.DisposeAbi(gitPtr); + } + } + + public IObjectReference Get() => _agileReference?.Resolve(typeof(IUnknownVftbl).GUID) ?? _git?.GetInterfaceFromGlobal(_cookie, typeof(IUnknownVftbl).GUID); + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + try + { + if (_git != null && _cookie != IntPtr.Zero) + { + _git.RevokeInterfaceFromGlobal(_cookie); + } + } + catch(ObjectDisposedException) + { + // TODO: How to handle removing from git when it has already been disposed. + } + disposed = true; } } - public IObjectReference Get() => _agileReference?.Resolve(typeof(IUnknownVftbl).GUID); + ~AgileReference() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } } public sealed class AgileReference : AgileReference diff --git a/WinRT.Runtime/Interop/IAgileReference.cs b/WinRT.Runtime/Interop/IAgileReference.cs index 601c11d99..852d9fe1d 100644 --- a/WinRT.Runtime/Interop/IAgileReference.cs +++ b/WinRT.Runtime/Interop/IAgileReference.cs @@ -17,6 +17,15 @@ internal interface IAgileReference public interface IAgileObject { } + + [WindowsRuntimeType] + [Guid("00000146-0000-0000-C000-000000000046")] + internal interface IGlobalInterfaceTable + { + IntPtr RegisterInterfaceInGlobal(IObjectReference objRef, Guid riid); + void RevokeInterfaceFromGlobal(IntPtr cookie); + IObjectReference GetInterfaceFromGlobal(IntPtr cookie, Guid riid); + } } namespace ABI.WinRT.Interop @@ -134,4 +143,61 @@ public IAgileObject(ObjectReference obj) _obj = obj; } } + + [Guid("00000146-0000-0000-C000-000000000046")] + internal class IGlobalInterfaceTable : global::WinRT.Interop.IGlobalInterfaceTable + { + [Guid("00000146-0000-0000-C000-000000000046")] + public struct Vftbl + { + public delegate int _RegisterInterfaceInGlobal(IntPtr thisPtr, IntPtr objRef, ref Guid riid, out IntPtr cookie); + public delegate int _RevokeInterfaceFromGlobal(IntPtr thisPtr, IntPtr cookie); + public delegate int _GetInterfaceFromGlobal(IntPtr thisPtr, IntPtr cookie, ref Guid riid, out IntPtr objectReference); + + public global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public _RegisterInterfaceInGlobal RegisterInterfaceInGlobal; + public _RevokeInterfaceFromGlobal RevokeInterfaceFromGlobal; + public _GetInterfaceFromGlobal GetInterfaceFromGlobal; + } + + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator IGlobalInterfaceTable(IObjectReference obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null; + public static implicit operator IGlobalInterfaceTable(ObjectReference obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null; + protected readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + + public IGlobalInterfaceTable(IObjectReference obj) : this(obj.As()) { } + public IGlobalInterfaceTable(ObjectReference obj) + { + _obj = obj; + } + + public IntPtr RegisterInterfaceInGlobal(IObjectReference objRef, Guid riid) + { + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RegisterInterfaceInGlobal(ThisPtr, objRef.ThisPtr, ref riid, out IntPtr cookie)); + return cookie; + + } + + public void RevokeInterfaceFromGlobal(IntPtr cookie) + { + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RevokeInterfaceFromGlobal(ThisPtr, cookie)); + } + + public IObjectReference GetInterfaceFromGlobal(IntPtr cookie, Guid riid) + { + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInterfaceFromGlobal(ThisPtr, cookie, ref riid, out IntPtr ptr)); + try + { + return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + } + finally + { + MarshalInspectable.DisposeAbi(ptr); + } + } + } } diff --git a/cswinrt/strings/WinRT.cs b/cswinrt/strings/WinRT.cs index 340544821..23bf0f03c 100644 --- a/cswinrt/strings/WinRT.cs +++ b/cswinrt/strings/WinRT.cs @@ -36,6 +36,9 @@ public static T AsDelegate(this MulticastDelegate del) internal class Platform { + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + internal static extern unsafe int CoCreateInstance(ref Guid clsid, IntPtr outer, uint clsContext, ref Guid iid, IntPtr* instance); + [DllImport("api-ms-win-core-com-l1-1-0.dll")] internal static extern int CoDecrementMTAUsage(IntPtr cookie);