Skip to content

Commit

Permalink
Fallback for RoGetAgileReference when it isn't available.
Browse files Browse the repository at this point in the history
  • Loading branch information
manodasanW committed Jun 9, 2020
1 parent e2fefd8 commit 0a5a609
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
5 changes: 3 additions & 2 deletions UnitTest/TestComponentCSharp_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -1543,6 +1543,7 @@ public void AcquireObject()

// Call to proxy object acquired from MTA which should throw
Assert.ThrowsAny<System.Exception>(() => proxyObject.Commands.Count);
agileReference.Dispose();
}

public void CheckValue()
Expand Down
56 changes: 53 additions & 3 deletions WinRT.Runtime/AgileReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -21,13 +26,58 @@ public unsafe AgileReference(IObjectReference instance)
&agileReference));
_agileReference = ABI.WinRT.Interop.IAgileReference.FromAbi(agileReference).AsType<ABI.WinRT.Interop.IAgileReference>();
}
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<ABI.WinRT.Interop.IGlobalInterfaceTable>();
_cookie = _git.RegisterInterfaceInGlobal(instance, iid);

}
finally
{
MarshalInterface<IAgileReference>.DisposeAbi(agileReference);
MarshalInterface<IGlobalInterfaceTable>.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<T> : AgileReference
Expand Down
66 changes: 66 additions & 0 deletions WinRT.Runtime/Interop/IAgileReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -134,4 +143,61 @@ public IAgileObject(ObjectReference<Vftbl> 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<Vftbl> FromAbi(IntPtr thisPtr) => ObjectReference<Vftbl>.FromAbi(thisPtr);

public static implicit operator IGlobalInterfaceTable(IObjectReference obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null;
public static implicit operator IGlobalInterfaceTable(ObjectReference<Vftbl> obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null;
protected readonly ObjectReference<Vftbl> _obj;
public IntPtr ThisPtr => _obj.ThisPtr;
public ObjectReference<I> AsInterface<I>() => _obj.As<I>();
public A As<A>() => _obj.AsType<A>();

public IGlobalInterfaceTable(IObjectReference obj) : this(obj.As<Vftbl>()) { }
public IGlobalInterfaceTable(ObjectReference<Vftbl> 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);
}
}
}
}
3 changes: 3 additions & 0 deletions cswinrt/strings/WinRT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public static T AsDelegate<T>(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);

Expand Down

0 comments on commit 0a5a609

Please sign in to comment.