Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
04c4d91
Add method to enumerate all implemented interfaces
Sergio0694 Dec 13, 2025
5309677
Avoid redundant instantiation of base interfaces
Sergio0694 Dec 13, 2025
e025f07
Track type definition in IObservableVector1 builder
Sergio0694 Dec 14, 2025
9ea6e95
Refactor and clarify interface resolution warnings
Sergio0694 Dec 14, 2025
eaaa460
Remove EnumerateAllInterfaces method from TypeDefinitionExtensions
Sergio0694 Dec 14, 2025
51bb30e
Refactor base type checks in type hierarchy utilities
Sergio0694 Dec 15, 2025
6e6efb4
Refactor user-defined type discovery logic
Sergio0694 Dec 15, 2025
635f5f5
Refactor generic type tracking into separate file
Sergio0694 Dec 15, 2025
dff7615
Handle newobj and newarr instructions in signature enumeration
Sergio0694 Dec 15, 2025
8a4f942
Refactor type tracking logic into helper methods
Sergio0694 Dec 15, 2025
5b2ab04
Refactor tracking methods to return void instead of bool
Sergio0694 Dec 15, 2025
0eb2784
Filter unconstructed generic types during discovery
Sergio0694 Dec 15, 2025
f11c140
Fix typos
Sergio0694 Dec 15, 2025
74ef05d
Add tests for generic type activation and interface QI
Sergio0694 Dec 16, 2025
05a314b
Add tests for AsyncInfo.Run with generic async operations
Sergio0694 Dec 16, 2025
10baca1
Remove constructed generic type checks in discovery
Sergio0694 Dec 18, 2025
f71488c
Improve IID cast error handling in ComHelpers
Sergio0694 Jan 15, 2026
999dff0
Update GUID for IEnumerable interface
Sergio0694 Jan 15, 2026
f13ffc5
Update IClosable GUID in test program
Sergio0694 Jan 15, 2026
0b46da1
Update IAsyncInfo GUIDs in test program
Sergio0694 Jan 15, 2026
07f328b
Refactor MakeAsyncOperation to inline AsyncInfo.Run
Sergio0694 Jan 15, 2026
0239a3e
Fix method signatures for async interop and marshalling
Sergio0694 Jan 15, 2026
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
236 changes: 224 additions & 12 deletions src/Tests/FunctionalTests/ClassActivation/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Windows.Input;
using TestComponent;
using TestComponentCSharp;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Foundation.Tasks;
using WindowsRuntime.InteropServices;

CustomDisposableTest customDisposableTest = new();
Expand Down Expand Up @@ -38,8 +47,6 @@
{
void* testMixedComClassUnknownPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(testMixedComClass);
void* classicComActionPtr = null;
void* closablePtr = null;
void* inspectablePtr = null;

try
{
Expand All @@ -53,21 +60,101 @@
Marshal.ThrowExceptionForHR(((delegate* unmanaged[MemberFunction]<void*, int>)(*(void***)classicComActionPtr)[3])(classicComActionPtr));

// Sanity check: we should still also be able to 'QueryInterface' for other interfaces
Marshal.ThrowExceptionForHR(Marshal.QueryInterface(
pUnk: (nint)testMixedComClassUnknownPtr,
iid: new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"),
ppv: out *(nint*)&closablePtr));
Marshal.ThrowExceptionForHR(Marshal.QueryInterface(
pUnk: (nint)testMixedComClassUnknownPtr,
iid: new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90"),
ppv: out *(nint*)&inspectablePtr));
ComHelpers.EnsureQueryInterface(
unknownPtr: testMixedComClassUnknownPtr,
iids: [
new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"),
new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90")]);
}
finally
{
WindowsRuntimeMarshal.Free(testMixedComClassUnknownPtr);
WindowsRuntimeMarshal.Free(classicComActionPtr);
WindowsRuntimeMarshal.Free(closablePtr);
WindowsRuntimeMarshal.Free(inspectablePtr);
}
}

ConstructedDerivedType constructedDerivedType = new();

unsafe
{
void* constructedDerivedTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(constructedDerivedType);

try
{
ComHelpers.EnsureQueryInterface(
unknownPtr: constructedDerivedTypePtr,
iids: [
new Guid("E2FCC7C1-3BFC-5A0B-B2B0-72E769D1CB7E"), // 'IEnumerable<string>'
new Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F"), // 'IEnumerable'
new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), // 'IClosable'
new Guid("8311ED02-4F46-5CAF-BF01-2AE354C04BF5")]); // 'IMapChangedEventArgs<IEnumerable>'
}
finally
{
WindowsRuntimeMarshal.Free(constructedDerivedTypePtr);
}
}

object genericType = GenericFactory.Make();

unsafe
{
void* genericTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(genericType);

try
{
ComHelpers.EnsureQueryInterface(
unknownPtr: genericTypePtr,
iids: [
new Guid("30160817-1D7D-54E9-99DB-D7636266A476"), // 'IEnumerable<bool>'
new Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F"), // 'IEnumerable'
new Guid("907661AB-C065-5A14-9AC3-2FEB0D164DDA"), // 'IReadOnlyDictionary<bool, float>'
new Guid("3631E370-2F65-5F4A-8364-1619C536DB12"), // 'IEnumerable<KeyValuePair<bool, float>>'
new Guid("F61E8483-D7A0-5840-9DCF-40423CCC97D0")]); // 'IMapChangedEventArgs<float>'
}
finally
{
WindowsRuntimeMarshal.Free(genericTypePtr);
}
}

IAsyncActionWithProgress<int> asyncActionWithProgress = GenericFactory.MakeAsyncActionWithProgress();

unsafe
{
void* asyncActionWithProgressPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncActionWithProgress);

try
{
ComHelpers.EnsureQueryInterface(
unknownPtr: asyncActionWithProgressPtr,
iids: [
new Guid("0EDE398F-0090-574E-AD30-E152B433BF6A"), // 'IAsyncActionWithProgress<int>'
new Guid("00000036-0000-0000-C000-000000000046")]); // 'IAsyncInfo'
}
finally
{
WindowsRuntimeMarshal.Free(asyncActionWithProgressPtr);
}
}

IAsyncOperation<TimeSpan> asyncOperation = GenericFactory.MakeAsyncOperation();

unsafe
{
void* asyncOperationPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncOperation);

try
{
ComHelpers.EnsureQueryInterface(
unknownPtr: asyncOperationPtr,
iids: [
new Guid("1AE01209-1ACA-51D3-A080-8B1214E0A39E"), // 'IAsyncOperation<TimeSpan>'
new Guid("00000036-0000-0000-C000-000000000046")]); // 'IAsyncInfo'
}
finally
{
WindowsRuntimeMarshal.Free(asyncOperationPtr);
}
}

Expand All @@ -86,13 +173,138 @@ public void Dispose()
}
}

class GenericBaseType<T> : IEnumerable<T>, IDisposable
{
public void Dispose()
{
throw new NotImplementedException();
}

public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

// This type is extending a generic type. The base type is a 'TypeSpec' with a constructed
// generic type. We use this to validate that the base constructed interfaces are seen.
class ConstructedDerivedType : GenericBaseType<string>, IMapChangedEventArgs<IEnumerable>
{
public CollectionChange CollectionChange => throw new NotImplementedException();

public IEnumerable Key => throw new NotImplementedException();
}

class GenericType<T1, T2> : IEnumerable<T1>, IReadOnlyDictionary<T1, T2>, IMapChangedEventArgs<T2>
{
public T2 this[T1 key] => throw new NotImplementedException();

public IEnumerable<T1> Keys => throw new NotImplementedException();

public IEnumerable<T2> Values => throw new NotImplementedException();

public int Count => throw new NotImplementedException();

public CollectionChange CollectionChange => throw new NotImplementedException();

public T2 Key => throw new NotImplementedException();

public bool ContainsKey(T1 key)
{
throw new NotImplementedException();
}

public IEnumerator<T1> GetEnumerator()
{
throw new NotImplementedException();
}

public bool TryGetValue(T1 key, [MaybeNullWhen(false)] out T2 value)
{
throw new NotImplementedException();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

IEnumerator<KeyValuePair<T1, T2>> IEnumerable<KeyValuePair<T1, T2>>.GetEnumerator()
{
throw new NotImplementedException();
}
}

class GenericFactory
{
// This method is caling a generic one, which then constructs a generic type.
// The 'GenericType<T, int>' instantiation doesn't appear anywhere in the .dll,
// but we should be able to still find it, because:
// - We can see 'Make<bool>' in the 'MethodSpec' table.
// - We can inspect all instructions in that constructed method
// - We can see a 'newobj' instruction with a 'MemberRef' to 'GenericType<T, int>.ctor'
// From there, we should be able to gather info on that constructed generic type.
public static object Make() => Make<bool>();

private static object Make<T>() => new GenericType<T, float>();

// Specific test for 'AsyncInfo.Run' with explicit type arguments
[SupportedOSPlatform("windows10.0.10240.0")]
public static IAsyncActionWithProgress<int> MakeAsyncActionWithProgress()
{
return AsyncInfo.Run<int>((token, progress) => Task.CompletedTask);
}

// Specific test for 'AsyncInfo.Run' with transitive type arguments.
// Note: transitive type arguments aren't currently supported for this.
[SupportedOSPlatform("windows10.0.10240.0")]
public static IAsyncOperation<TimeSpan> MakeAsyncOperation()
{
return AsyncInfo.Run(token => Task.FromResult(default(TimeSpan)));
}
}

[Guid("3C832AA5-5F7E-46EE-B1BF-7FE03AE866AF")]
[GeneratedComInterface]
partial interface IClassicComAction
{
void Invoke();
}

file static class ComHelpers
{
[SupportedOSPlatform("windows6.3")]
public static unsafe void EnsureQueryInterface(void* unknownPtr, params ReadOnlySpan<Guid> iids)
{
foreach (Guid iid in iids)
{
int hresult = Marshal.QueryInterface(
pUnk: (nint)unknownPtr,
iid: iid,
ppv: out nint interfacePtr);

WindowsRuntimeMarshal.Free((void*)interfacePtr);

const int E_NOINTERFACE = unchecked((int)0x80004002);

// If we failed due to 'E_NOINTERFACE', we want a custom message with the IID, to help debugging
if (hresult == E_NOINTERFACE)
{
throw new InvalidCastException($"Specified cast is not valid (IID: '{iid.ToString().ToUpperInvariant()}').");
}
else
{
Marshal.ThrowExceptionForHR(hresult);
}
}
}
}

/*
// new RCW / Factory activation
var instance = new Class();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ public static void ImplType(
implType.Methods.Add(makeVectorChangedMethod);
implType.Methods.Add(get_VectorChangedTableMethod);
implType.Properties.Add(vectorChangedTableProperty);

// Track the type (it may be needed by COM interface entries for user-defined types)
emitState.TrackTypeDefinition(implType, vectorType, "Impl");
}

/// <summary>
Expand Down
Loading