Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions src/tests/Interop/COM/ComWrappers/API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ static TestComWrappers()
fpWrappedQueryInterface = MockReferenceTrackerRuntime.WrapQueryInterface(fpQueryInterface);
}

public bool UseManualReleaseITestObjectWrapper { get; init; }

protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
{
ComInterfaceEntry* entryRaw = null;
Expand Down Expand Up @@ -133,7 +135,14 @@ protected override object CreateObject(IntPtr externalComObject, CreateObjectFla
hr = Marshal.QueryInterface(externalComObject, typeof(ITest).GUID, out iTest);
if (hr == 0)
{
return new ITestObjectWrapper(iTest);
if (UseManualReleaseITestObjectWrapper)
{
return new ManualReleaseITestObjectWrapper(iTest);
}
else
{
return new ITestObjectWrapper(iTest);
}
}

Assert.Fail("The COM object should support ITrackerObject or ITest for all tests in this test suite.");
Expand Down Expand Up @@ -418,9 +427,9 @@ unsafe static void CallSetValue(TestComWrappers wrappers, Test testInstance, int
[MethodImpl(MethodImplOptions.NoInlining)]
[ActiveIssue("Not supported on Mono", TestRuntimes.Mono)]
[Fact]
public void ValidateResurrection()
public void ValidateManagedObjectWrapperResurrection()
{
Console.WriteLine($"Running {nameof(ValidateResurrection)}...");
Console.WriteLine($"Running {nameof(ValidateManagedObjectWrapperResurrection)}...");

var wrappers = new TestComWrappers();

Expand Down Expand Up @@ -621,6 +630,72 @@ public void ValidateMappingAPIs()
Marshal.Release(unmanagedObjIUnknown);
}

class Resurrecter()
{
public ManualReleaseITestObjectWrapper? UnmanagedWrapper;

~Resurrecter()
{
if (UnmanagedWrapper != null)
{
GC.ReRegisterForFinalize(this);
}
}
}


[MethodImpl(MethodImplOptions.NoInlining)]
[ActiveIssue("Not supported on Mono", TestRuntimes.Mono)]
[Fact]
public void ValidateNativeObjectWrapperResurrection()
{
Console.WriteLine($"Running {nameof(ValidateNativeObjectWrapperResurrection)}...");

var cw = new TestComWrappers()
{
UseManualReleaseITestObjectWrapper = true,
};

WeakGCHandle<Resurrecter> resurrecter;
nint unmanagedObj = AllocateWrapper(cw, out resurrecter);
Assert.Equal(0, Marshal.QueryInterface(unmanagedObj, IUnknownVtbl.IID_IUnknown, out IntPtr unmanagedObjIUnknown));
ForceGC();
AssertNativeObjectWrapperAlive(cw, resurrecter, unmanagedObjIUnknown);

resurrecter.Dispose();
Marshal.Release(unmanagedObjIUnknown);
Assert.Equal(0, Marshal.Release(unmanagedObj));

[MethodImpl(MethodImplOptions.NoInlining)]
static nint AllocateWrapper(ComWrappers cw, out WeakGCHandle<Resurrecter> handle)
{
Test test = new();
nint comWrapper = cw.GetOrCreateComInterfaceForObject(test, CreateComInterfaceFlags.None);
Assert.NotEqual(IntPtr.Zero, comWrapper);

var unmanagedWrapper = (ManualReleaseITestObjectWrapper)cw.GetOrCreateObjectForComInstance(comWrapper, CreateObjectFlags.UniqueInstance);
Resurrecter resurrecter = new()
{
UnmanagedWrapper = unmanagedWrapper,
};
handle = new WeakGCHandle<Resurrecter>(resurrecter, true);
return comWrapper;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void AssertNativeObjectWrapperAlive(ComWrappers cw, WeakGCHandle<Resurrecter> handle, IntPtr unmanagedObj)
{
Assert.True(handle.TryGetTarget(out Resurrecter resurrecter));
ManualReleaseITestObjectWrapper? unmanagedWrapper = resurrecter.UnmanagedWrapper;
Assert.NotNull(resurrecter);
Assert.True(ComWrappers.TryGetComInstance(unmanagedWrapper, out IntPtr unmanagedObjOther));
Assert.Equal(unmanagedObj, unmanagedObjOther);
resurrecter.UnmanagedWrapper = null;
Marshal.Release(unmanagedObjOther);
unmanagedWrapper.FinalRelease();
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
[ActiveIssue("Not supported on Mono", TestRuntimes.Mono)]
[Fact]
Expand Down
28 changes: 22 additions & 6 deletions src/tests/Interop/COM/ComWrappers/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ public static int SetValueInternal(IntPtr dispatchPtr, int i)
}
}

public class ITestObjectWrapper : ITest
public class ITestObjectWrapperBase : ITest
{
private readonly ITestVtbl._SetValue _setValue;
private readonly IntPtr _ptr;
private bool _released;
protected readonly IntPtr _ptr;
protected bool _released;

public ITestObjectWrapper(IntPtr ptr)
public ITestObjectWrapperBase(IntPtr ptr)
{
_ptr = ptr;
VtblPtr inst = Marshal.PtrToStructure<VtblPtr>(ptr);
Expand All @@ -117,15 +117,31 @@ public int FinalRelease()
return count;
}

public void SetValue(int i) => _setValue(_ptr, i);
}

public class ManualReleaseITestObjectWrapper : ITestObjectWrapperBase
{
public ManualReleaseITestObjectWrapper(IntPtr ptr)
: base(ptr)
{
}
}

public class ITestObjectWrapper : ITestObjectWrapperBase
{
public ITestObjectWrapper(IntPtr ptr)
: base(ptr)
{
}

~ITestObjectWrapper()
{
if (_ptr != IntPtr.Zero && !_released)
{
Marshal.Release(_ptr);
}
}

public void SetValue(int i) => _setValue(_ptr, i);
}

//
Expand Down
Loading