diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs
index 215b6a7cc..d70160642 100644
--- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs
+++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs
@@ -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");
}
///
diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs
new file mode 100644
index 000000000..593d5640a
--- /dev/null
+++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs
@@ -0,0 +1,236 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.Errors;
+using WindowsRuntime.InteropGenerator.Generation;
+using WindowsRuntime.InteropGenerator.Helpers;
+using WindowsRuntime.InteropGenerator.References;
+using WindowsRuntime.InteropGenerator.Visitors;
+
+namespace WindowsRuntime.InteropGenerator.Discovery;
+
+///
+internal partial class InteropTypeDiscovery
+{
+ ///
+ /// Tries to track a constructed generic type.
+ ///
+ /// The for the constructed type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ /// The instance to use.
+ /// The module currently being analyzed.
+ public static void TryTrackGenericTypeInstance(
+ GenericInstanceTypeSignature typeSignature,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState,
+ InteropReferences interopReferences,
+ ModuleDefinition module)
+ {
+ // Ignore types that should explicitly be excluded
+ if (TypeExclusions.IsExcluded(typeSignature, interopReferences))
+ {
+ return;
+ }
+
+ // Filter all constructed generic type signatures we have. We don't care about generic type
+ // definitions (eg. 'TypedEventHandler`1') for the purposes of marshalling code.
+ if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance))
+ {
+ return;
+ }
+
+ // Ignore types that are not fully resolvable (this likely means a .dll is missing)
+ if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition))
+ {
+ // Log a warning the first time we fail to resolve this generic instantiation in this module
+ if (discoveryState.TrackFailedResolutionType(typeSignature, module))
+ {
+ WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
+ }
+
+ return;
+ }
+
+ // If the current type signature represents a Windows Runtime type, track it
+ if (typeSignature.IsWindowsRuntimeType(interopReferences))
+ {
+ TryTrackWindowsRuntimeGenericTypeInstance(
+ typeDefinition,
+ typeSignature,
+ args,
+ discoveryState,
+ interopReferences,
+ module);
+ }
+ else
+ {
+ // Otherwise, try to track information for some constructed managed type
+ TryTrackManagedGenericTypeInstance(
+ typeDefinition,
+ typeSignature,
+ args,
+ discoveryState,
+ interopReferences);
+ }
+ }
+
+ ///
+ /// Tries to track an SZ array type.
+ ///
+ /// The for the SZ array type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ /// The instance to use.
+ /// The module currently being analyzed.
+ public static void TryTrackSzArrayType(
+ SzArrayTypeSignature typeSignature,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState,
+ InteropReferences interopReferences,
+ ModuleDefinition module)
+ {
+ // Ignore types that should explicitly be excluded
+ if (TypeExclusions.IsExcluded(typeSignature, interopReferences))
+ {
+ return;
+ }
+
+ // Filter all constructed generic type signatures we have. We don't care about
+ // generic type definitions (eg. '!0[]') for the purposes of marshalling code.
+ if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance))
+ {
+ return;
+ }
+
+ // Ignore types that are not fully resolvable (this likely means a .dll is missing)
+ if (!typeSignature.IsFullyResolvable(out _))
+ {
+ // Log a warning the first time we fail to resolve this SZ array in this module
+ if (discoveryState.TrackFailedResolutionType(typeSignature, module))
+ {
+ WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
+ }
+
+ return;
+ }
+
+ // Ignore array types that are not Windows Runtime types
+ if (!typeSignature.IsWindowsRuntimeType(interopReferences))
+ {
+ return;
+ }
+
+ // Track all SZ array types, as we'll need to emit marshalling code for them
+ discoveryState.TrackSzArrayType(typeSignature);
+ }
+
+ ///
+ /// Tries to track a constructed generic Windows Runtime type.
+ ///
+ /// The for the type to analyze.
+ /// The for the constructed type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ /// The instance to use.
+ /// The module currently being analyzed.
+ private static void TryTrackWindowsRuntimeGenericTypeInstance(
+ TypeDefinition typeDefinition,
+ GenericInstanceTypeSignature typeSignature,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState,
+ InteropReferences interopReferences,
+ ModuleDefinition module)
+ {
+ // Gather all 'KeyValuePair<,>' instances
+ if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences))
+ {
+ discoveryState.TrackKeyValuePairType(typeSignature);
+
+ return;
+ }
+
+ // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus
+ // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler').
+ // The filtering is already done above, so here we can rely the type will be of one of those kinds.
+ if (typeDefinition.IsDelegate)
+ {
+ discoveryState.TrackGenericDelegateType(typeSignature);
+
+ return;
+ }
+
+ // Track all projected Windows Runtime generic interfaces
+ if (typeDefinition.IsInterface)
+ {
+ discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences);
+
+ // We also want to crawl base interfaces
+ foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces())
+ {
+ // Filter out just constructed generic interfaces, since we only care about those here.
+ // The non-generic ones are only useful when gathering interfaces for user-defined types.
+ if (interfaceSignature is not GenericInstanceTypeSignature constructedSignature)
+ {
+ continue;
+ }
+
+ if (!interfaceSignature.IsFullyResolvable(out _))
+ {
+ // Also log a warning the first time we fail to resolve one of the recursively discovered generic
+ // instantiations from this module. The enumeration also yields back interfaces that couldn't be
+ // resolved, as that step is performed after yielding. This is done so we can have our own logic
+ // to log warnings or throw errors from here while we're processing interfaces in this module.
+ if (discoveryState.TrackFailedResolutionType(interfaceSignature, module))
+ {
+ WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
+ }
+
+ continue;
+ }
+
+ discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences);
+ }
+ }
+ }
+
+ ///
+ /// Tries to track a constructed generic user-defined type.
+ ///
+ /// The for the type to analyze.
+ /// The for the constructed type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ /// The instance to use.
+ private static void TryTrackManagedGenericTypeInstance(
+ TypeDefinition typeDefinition,
+ GenericInstanceTypeSignature typeSignature,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState,
+ InteropReferences interopReferences)
+ {
+ // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types.
+ // This is because "pass-array" and "fill-array" parameters are projected using spans, but
+ // those projections require the marshalling code produced when discovering SZ array types.
+ // So if we see any of these spans where the element type is a Windows Runtime type, we
+ // manually construct an SZ array type for it and add it to the set of tracked array types.
+ if (typeSignature.IsValueType &&
+ typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) &&
+ typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences))
+ {
+ discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType());
+
+ return;
+ }
+
+ // Otherwise, try to track a constructed user-defined type
+ TryTrackExposedUserDefinedType(
+ typeDefinition,
+ typeSignature,
+ args,
+ discoveryState,
+ interopReferences);
+ }
+}
\ No newline at end of file
diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs
new file mode 100644
index 000000000..3a6f8b9a9
--- /dev/null
+++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs
@@ -0,0 +1,206 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.Errors;
+using WindowsRuntime.InteropGenerator.Generation;
+using WindowsRuntime.InteropGenerator.Helpers;
+using WindowsRuntime.InteropGenerator.Models;
+using WindowsRuntime.InteropGenerator.References;
+
+namespace WindowsRuntime.InteropGenerator.Discovery;
+
+///
+/// A discovery helper type for interop types.
+///
+internal static partial class InteropTypeDiscovery
+{
+ ///
+ /// A thread-local instance that can be reused by discovery logic.
+ ///
+ [ThreadStatic]
+ private static TypeSignatureEquatableSet.Builder? TypeSignatures;
+
+ ///
+ /// Tries to track a given composable Windows Runtime type.
+ ///
+ /// The for the type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ public static void TryTrackTypeHierarchyType(
+ TypeDefinition typeDefinition,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState)
+ {
+ // We only care about projected Windows Runtime classes
+ if (!typeDefinition.IsProjectedWindowsRuntimeClassType)
+ {
+ return;
+ }
+
+ // Ignore types that don't have another base class
+ if (!typeDefinition.HasBaseType(out ITypeDefOrRef? baseType))
+ {
+ return;
+ }
+
+ // We need to resolve the base type to be able to look up attributes on it
+ if (!baseType.IsFullyResolvable(out _))
+ {
+ WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors);
+
+ return;
+ }
+
+ // If the base type is also a projected Windows Runtime type, track it
+ if (baseType.IsProjectedWindowsRuntimeType)
+ {
+ discoveryState.TrackTypeHierarchyEntry(typeDefinition.FullName, baseType.FullName);
+ }
+ }
+
+ ///
+ /// Tries to track an exposed user-defined type.
+ ///
+ /// The for the type to analyze.
+ /// The for the type to analyze.
+ /// The arguments for this invocation.
+ /// The discovery state for this invocation.
+ /// The instance to use.
+ ///
+ /// This method expects to either be non-generic, or
+ /// to have be a fully constructed signature for it.
+ ///
+ public static void TryTrackExposedUserDefinedType(
+ TypeDefinition typeDefinition,
+ TypeSignature typeSignature,
+ InteropGeneratorArgs args,
+ InteropGeneratorDiscoveryState discoveryState,
+ InteropReferences interopReferences)
+ {
+ // Ignore types that should explicitly be excluded
+ if (TypeExclusions.IsExcluded(typeDefinition, interopReferences))
+ {
+ return;
+ }
+
+ // Ignore all type definitions with generic parameters where we don't have constructed
+ // generic type signature. We can track these separately when we see them as instantiated.
+ if (typeDefinition.HasGenericParameters && typeSignature is not GenericInstanceTypeSignature)
+ {
+ return;
+ }
+
+ // We can skip all projected Windows Runtime types early, as they don't need CCW support
+ if (typeDefinition.IsProjectedWindowsRuntimeType)
+ {
+ return;
+ }
+
+ // We'll need to look up attributes and enumerate interfaces across the entire type
+ // hierarchy for this type, so make sure that we can resolve all types from it first.
+ if (!typeDefinition.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType))
+ {
+ WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors);
+
+ return;
+ }
+
+ // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime
+ if (!typeDefinition.IsPossiblyWindowsRuntimeExposedType || typeDefinition.IsWindowsRuntimeManagedOnlyType(interopReferences))
+ {
+ return;
+ }
+
+ // Reuse the thread-local builder to track all implemented interfaces for the current type
+ TypeSignatureEquatableSet.Builder interfaces = TypeSignatures ??= new TypeSignatureEquatableSet.Builder();
+
+ // Since we're reusing the builder for all types, make sure to clear it first
+ interfaces.Clear();
+
+ // We want to explicitly track whether the type implements any projected Windows Runtime
+ // interfaces, as we are only interested in such types. We want to also gather all
+ // implemented '[GeneratedComInterface]' interfaces, but if a type only implements
+ // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly.
+ bool hasAnyProjectedWindowsRuntimeInterfaces = false;
+
+ // Gather all implemented Windows Runtime interfaces for the current type
+ foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces())
+ {
+ // Make sure we can resolve the interface type fully, which we should always be able to do.
+ // This can really only fail for some constructed generics, for invalid type arguments.
+ if (!interfaceSignature.IsFullyResolvable(out TypeDefinition? interfaceDefinition))
+ {
+ WellKnownInteropExceptions.InterfaceImplementationTypeNotResolvedWarning(interfaceSignature, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors);
+
+ continue;
+ }
+
+ // Check for projected Windows Runtime interfaces first
+ if (interfaceSignature.IsWindowsRuntimeType(interopReferences))
+ {
+ hasAnyProjectedWindowsRuntimeInterfaces = true;
+
+ interfaces.Add(interfaceSignature);
+
+ // If the current interface is generic, also make sure that it's tracked. This is needed
+ // to fully cover all possible constructed generic interface types that might be needed.
+ // For instance, consider this case:
+ //
+ // class A : IEnumerable;
+ // class B : A;
+ //
+ // While processing 'B', we'll discover the constructed 'IEnumerable' interface.
+ // This interface would not have been discovered when processing 'A', as it's not
+ // in the 'TypeSpec' metadata table, and only appears as unconstructed on 'A'.
+ // So the discovery logic for generic instantiations below would otherwise miss it.
+ if (interfaceSignature is GenericInstanceTypeSignature constructedSignature)
+ {
+ discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences);
+ }
+ }
+ else if (interfaceDefinition.IsGeneratedComInterfaceType)
+ {
+ // We can only gather this type if we can find the generated 'InterfaceInformation' type.
+ // If we can't find it, we can't add the interface to the list of interface entries. We
+ // should warn if that's the (unlikely) case, so users can at least know that something
+ // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures.
+ if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _))
+ {
+ WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors);
+
+ continue;
+ }
+
+ // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time
+ // so we can check against some specific IID which might affect how we construct the COM
+ // interface entries. For instance, we need to check whether 'IMarshal' is implemented.
+ if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid))
+ {
+ WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors);
+
+ continue;
+ }
+
+ // Validate that the current interface isn't trying to implement a reserved interface.
+ // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'.
+ if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName))
+ {
+ throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, typeDefinition, iid, interfaceName);
+ }
+
+ // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below)
+ interfaces.Add(interfaceSignature);
+ }
+ }
+
+ // If the user-defined type implements at least a Windows Runtime interface, then it's considered exposed.
+ // We don't want to handle marshalling code for types with only '[GeneratedComInterface]' interfaces.
+ if (hasAnyProjectedWindowsRuntimeInterfaces)
+ {
+ discoveryState.TrackUserDefinedType(typeSignature, interfaces.ToEquatableSet());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs
index 42357863e..50d273efe 100644
--- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs
+++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs
@@ -430,11 +430,11 @@ public static WellKnownInteropException CustomMappedTypeComWrappersMarshallerAtt
}
///
- /// Failed to resolve a '[GeneratedComInterface]' type.
+ /// Failed to resolve the type of an implemented interface.
///
- public static WellKnownInteropWarning GeneratedComInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type)
+ public static WellKnownInteropWarning InterfaceImplementationTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type)
{
- return Warning(49, $"Failed to resolve the '[GeneratedComInterface]' type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries.");
+ return Warning(49, $"Failed to resolve interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries.");
}
///
@@ -534,11 +534,11 @@ public static WellKnownInteropWarning GeneratedComInterfaceGuidAttributeNotFound
}
///
- /// Failed to resolve a Windows Runtime interface type.
+ /// Failed to resolve a base type for a user-defined type.
///
- public static WellKnownInteropWarning WindowsRuntimeInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type)
+ public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type)
{
- return Warning(62, $"Failed to resolve the Windows Runtime interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries.");
+ return Warning(62, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated.");
}
///
@@ -573,14 +573,6 @@ public static WellKnownInteropWarning WindowsRuntimeClassTypeNotResolvedWarning(
return Warning(66, $"Failed to resolve the base type '{baseType}' for Windows Runtime class type '{classType}': runtime casts to the base type will not work if the type is trimmed.");
}
- ///
- /// Failed to resolve a base type for a user-defined type.
- ///
- public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type)
- {
- return Warning(67, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated.");
- }
-
///
/// Creates a new exception with the specified id and message.
///
diff --git a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs
index ac8ffeaac..aeee73acb 100644
--- a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs
+++ b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs
@@ -8,6 +8,7 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Signatures;
+using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables;
using WindowsRuntime.InteropGenerator.Helpers;
using WindowsRuntime.InteropGenerator.Visitors;
@@ -230,6 +231,58 @@ static IEnumerable EnumerateTypeSignatures(
yield return result;
}
}
+
+ IReadOnlyList instructions = specification.Method!.Resolve()?.CilMethodBody?.Instructions ?? (IReadOnlyList)[];
+
+ // Go through instruction to look for new objects
+ foreach (CilInstruction instruction in instructions)
+ {
+ // We only care for 'newobj' instructions
+ if (instruction.OpCode != CilOpCodes.Newobj)
+ {
+ continue;
+ }
+
+ // Check that we can retrieve the target object type
+ if (instruction.Operand is not IMethodDefOrRef { DeclaringType: ITypeDefOrRef objectType })
+ {
+ continue;
+ }
+
+ // Instantiate the object type and enumerate all signatures
+ foreach (TResult result in EnumerateTypeSignatures(
+ objectType.ToTypeSignature().InstantiateGenericTypes(genericContext),
+ results,
+ visitor))
+ {
+ yield return result;
+ }
+ }
+
+ // Go through instruction to look for new arrays
+ foreach (CilInstruction instruction in instructions)
+ {
+ // We only care for 'newarr' instructions
+ if (instruction.OpCode != CilOpCodes.Newarr)
+ {
+ continue;
+ }
+
+ // Check that we can retrieve the target object type
+ if (instruction.Operand is not ITypeDefOrRef arrayType)
+ {
+ continue;
+ }
+
+ // Instantiate the object type and enumerate all signatures
+ foreach (TResult result in EnumerateTypeSignatures(
+ arrayType.ToTypeSignature().InstantiateGenericTypes(genericContext),
+ results,
+ visitor))
+ {
+ yield return result;
+ }
+ }
}
}
diff --git a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs
index 515518250..e831a8e68 100644
--- a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs
+++ b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs
@@ -25,6 +25,17 @@ internal static class TypeDefinitionExtensions
///
public bool IsStatic => type.IsAbstract && type.IsSealed;
+ ///
+ /// Gets whether a given type has a base type other than .
+ ///
+ /// The resulting base type, if available.
+ public bool HasBaseType([NotNullWhen(true)] out ITypeDefOrRef? baseType)
+ {
+ baseType = type.BaseType;
+
+ return baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object });
+ }
+
///
/// Gets a value indicating whether a given 's type hierarchy can be fully resolved to type definitions.
///
@@ -32,18 +43,16 @@ internal static class TypeDefinitionExtensions
/// Whether the input 's type hierarchy can be fully resolved to type definitions.
public bool IsTypeHierarchyFullyResolvable([NotNullWhen(false)] out ITypeDefOrRef? failedResolutionBaseType)
{
- ITypeDefOrRef? baseType = type.BaseType;
+ TypeDefinition currentDefinition = type;
- while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object }))
+ while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType))
{
- if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition))
+ if (!baseType.IsFullyResolvable(out currentDefinition!))
{
failedResolutionBaseType = baseType;
return false;
}
-
- baseType = baseDefinition.BaseType;
}
failedResolutionBaseType = null;
@@ -224,38 +233,18 @@ public IEnumerable EnumerateBaseTypesAndSelf()
{
yield return type;
- ITypeDefOrRef? baseType = type.BaseType;
+ TypeDefinition currentDefinition = type;
- while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object }))
+ while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType))
{
// If we can't resolve the current base type, we have to stop.
// Callers should validate the type hierarchy before calling this.
- if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition))
+ if (!baseType.IsFullyResolvable(out currentDefinition!))
{
yield break;
}
- yield return baseDefinition;
-
- baseType = baseDefinition.BaseType;
- }
- }
-
- ///
- /// Enumerates all interface types implementation by the specified type, including those implemented by base types.
- ///
- /// The sequence of interface types implemented by the input type.
- ///
- /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy.
- ///
- public IEnumerable EnumerateAllInterfaces()
- {
- foreach (TypeDefinition currentType in type.EnumerateBaseTypesAndSelf())
- {
- foreach (InterfaceImplementation implementation in currentType.Interfaces)
- {
- yield return implementation;
- }
+ yield return currentDefinition;
}
}
diff --git a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs
index be76c72f1..cc0a7fbcc 100644
--- a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs
+++ b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
+using AsmResolver.PE.DotNet.Metadata.Tables;
namespace WindowsRuntime.InteropGenerator;
@@ -43,5 +45,65 @@ public bool IsFullyResolvable([NotNullWhen(true)] out TypeDefinition? definition
return true;
}
+
+ ///
+ /// Enumerates all interface types implementation by the specified type, including those implemented by base types.
+ ///
+ /// The sequence of interface types implemented by the input type.
+ ///
+ /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy.
+ ///
+ public IEnumerable EnumerateAllInterfaces()
+ {
+ TypeSignature currentSignature = signature;
+
+ while (currentSignature is not null)
+ {
+ // If we can't resolve the current type signature, we have to stop.
+ // Callers should validate the type hierarchy before calling this.
+ if (!currentSignature.IsFullyResolvable(out TypeDefinition? currentDefinition))
+ {
+ yield break;
+ }
+
+ GenericContext context = new(currentSignature as GenericInstanceTypeSignature, null);
+
+ // Go over all interfaces implemented on the current type. We don't need
+ // to recurse on them, as classes always declare the full transitive set.
+ foreach (InterfaceImplementation interfaceImplementation in currentDefinition.Interfaces)
+ {
+ // Ignore this interface if we can't actually retrieve the interface type.
+ // This should never happen for valid .NET assemblies, but just in case.
+ if (interfaceImplementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature)
+ {
+ continue;
+ }
+
+ // Return either the current non-generic interface, or the constructed generic one.
+ // We don't have to check: if the interface is not generic, this will be a no-op.
+ yield return interfaceSignature.InstantiateGenericTypes(context);
+
+ // Also recurse on the base interfaces (no need to instantiate the returned interface type
+ // signatures for base interfaces here: they will be already instantiate when returned).
+ foreach (TypeSignature baseInterface in interfaceSignature.EnumerateAllInterfaces())
+ {
+ yield return baseInterface;
+ }
+ }
+
+ ITypeDefOrRef? baseType = currentDefinition.BaseType;
+
+ // Stop if we have no available base type or if we reached 'object'
+ if (baseType is null or CorLibTypeSignature { ElementType: ElementType.Object })
+ {
+ yield break;
+ }
+
+ // Get the signature for the base type, adding back any generic context.
+ // Note that the base type will always be a reference type, even for
+ // struct types (in that case, the base type will be 'System.ValueType').
+ currentSignature = baseType.ToReferenceTypeSignature().InstantiateGenericTypes(context);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs
index a417b9d33..cd13d6761 100644
--- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs
+++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs
@@ -9,6 +9,7 @@
using System.Threading.Tasks;
using AsmResolver.DotNet;
using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.Discovery;
using WindowsRuntime.InteropGenerator.Errors;
using WindowsRuntime.InteropGenerator.Models;
using WindowsRuntime.InteropGenerator.References;
@@ -155,31 +156,7 @@ private static void DiscoverTypeHierarchyTypes(
{
args.Token.ThrowIfCancellationRequested();
- // We only care about projected Windows Runtime classes
- if (!type.IsProjectedWindowsRuntimeClassType)
- {
- continue;
- }
-
- // Ignore types that don't have another base class
- if (type.BaseType is null || SignatureComparer.IgnoreVersion.Equals(type.BaseType, module.CorLibTypeFactory.Object))
- {
- continue;
- }
-
- // We need to resolve the base type to be able to look up attributes on it
- if (!type.BaseType.IsFullyResolvable(out TypeDefinition? baseType))
- {
- WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(type.BaseType, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- // If the base type is also a projected Windows Runtime type, track it
- if (baseType.IsProjectedWindowsRuntimeType)
- {
- discoveryState.TrackTypeHierarchyEntry(type.FullName, baseType.FullName);
- }
+ InteropTypeDiscovery.TryTrackTypeHierarchyType(type, args, discoveryState);
}
}
catch (Exception e)
@@ -210,120 +187,13 @@ private static void DiscoverExposedUserDefinedTypes(
{
args.Token.ThrowIfCancellationRequested();
- // Ignore all type definitions with generic parameters, because they would be
- // unconstructed (by definition). We'll process instantiations that we can see
- // separately in the discovery phase, same as we do for constructed interfaces.
- if (type.HasGenericParameters)
- {
- continue;
- }
-
- // We can skip all projected Windows Runtime types early, as they don't need CCW support
- if (type.IsProjectedWindowsRuntimeType)
- {
- continue;
- }
-
- // We'll need to look up attributes and enumerate interfaces across the entire type
- // hierarchy for this type, so make sure that we can resolve all types from it first.
- if (!type.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType))
- {
- WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime
- if (!type.IsPossiblyWindowsRuntimeExposedType || type.IsWindowsRuntimeManagedOnlyType(interopReferences))
- {
- continue;
- }
-
- // Since we're reusing the builder for all types, make sure to clear it first
- interfaces.Clear();
-
- // We want to explicitly track whether the type implements any projected Windows Runtime
- // interfaces, as we are only interested in such types. We want to also gather all
- // implemented '[GeneratedComInterface]' interfaces, but if a type only implements
- // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly.
- bool hasAnyProjectedWindowsRuntimeInterfaces = false;
-
- // Gather all implemented Windows Runtime interfaces for the current type
- foreach (InterfaceImplementation implementation in type.EnumerateAllInterfaces())
- {
- // If the current implementation has no valid interface, skip it.
- // This should never really happen for valid .NET assemblies.
- if (implementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature)
- {
- continue;
- }
-
- // Check for projected Windows Runtime interfaces first
- if (interfaceSignature.IsWindowsRuntimeType(interopReferences))
- {
- // Make sure we can resolve the interface type fully, which we should always be able to do.
- // This can really only fail for some constructed generics, for invalid type arguments.
- if (!interfaceSignature.IsFullyResolvable(out _))
- {
- WellKnownInteropExceptions.WindowsRuntimeInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- hasAnyProjectedWindowsRuntimeInterfaces = true;
-
- interfaces.Add(interfaceSignature);
- }
- else if (implementation.Interface.IsGeneratedComInterfaceType)
- {
- // To properly track '[GeneratedComInterface]' implementations, we need to be able to resolve those interface types
- if (!implementation.Interface.IsFullyResolvable(out TypeDefinition? interfaceDefinition))
- {
- WellKnownInteropExceptions.GeneratedComInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- // We can only gather this type if we can find the generated 'InterfaceInformation' type.
- // If we can't find it, we can't add the interface to the list of interface entries. We
- // should warn if that's the (unlikely) case, so users can at least know that something
- // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures.
- if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _))
- {
- WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time
- // so we can check against some specific IID which might affect how we construct the COM
- // interface entries. For instance, we need to check whether 'IMarshal' is implemented.
- if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid))
- {
- WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors);
-
- continue;
- }
-
- // Validate that the current interface isn't trying to implement a reserved interface.
- // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'.
- if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName))
- {
- throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, type, iid, interfaceName);
- }
-
- // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below)
- interfaces.Add(interfaceSignature);
- }
- }
-
- // If the user-defined type doesn't implement any Windows Runtime interfaces, it's not considered exposed
- if (!hasAnyProjectedWindowsRuntimeInterfaces)
- {
- continue;
- }
-
- discoveryState.TrackUserDefinedType(type.ToTypeSignature(), interfaces.ToEquatableSet());
+ // Track the type (if it's not applicable, it will be a no-op)
+ InteropTypeDiscovery.TryTrackExposedUserDefinedType(
+ typeDefinition: type,
+ typeSignature: type.ToTypeSignature(),
+ args: args,
+ discoveryState: discoveryState,
+ interopReferences: interopReferences);
}
}
catch (Exception e)
@@ -358,85 +228,13 @@ private static void DiscoverGenericTypeInstantiations(
continue;
}
- // Ignore types that are not fully resolvable (this likely means a .dll is missing)
- if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition))
- {
- // Log a warning the first time we fail to resolve this generic instantiation in this module
- if (discoveryState.TrackFailedResolutionType(typeSignature, module))
- {
- WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
- }
-
- continue;
- }
-
- // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types.
- // This is because "pass-array" and "fill-array" parameters are projected using spans, but
- // those projections require the marshalling code produced when discovering SZ array types.
- // So if we see any of these spans where the element type is a Windows Runtime type, we
- // manually construct an SZ array type for it and add it to the set of tracked array types.
- if (typeSignature.IsValueType &&
- typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) &&
- typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences))
- {
- discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType());
-
- continue;
- }
-
- // Ignore generic instantiations that are not Windows Runtime types. That is, those that
- // have a generic type definition that's not a Windows Runtime type, or that have any type
- // arguments that are not Windows Runtime types.
- if (!typeSignature.IsWindowsRuntimeType(interopReferences))
- {
- continue;
- }
-
- // Gather all 'KeyValuePair<,>' instances
- if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences))
- {
- discoveryState.TrackKeyValuePairType(typeSignature);
-
- continue;
- }
-
- // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus
- // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler').
- // The filtering is already done above, so here we can rely the type will be of one of those kinds.
- if (typeDefinition.IsDelegate)
- {
- discoveryState.TrackGenericDelegateType(typeSignature);
-
- continue;
- }
-
- // Track all projected Windows Runtime generic interfaces
- if (typeDefinition.IsInterface)
- {
- discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences);
-
- // We also want to crawl base interfaces
- foreach (GenericInstanceTypeSignature interfaceSignature in typeDefinition.EnumerateGenericInstanceInterfaceSignatures(typeSignature))
- {
- if (!interfaceSignature.IsFullyResolvable(out _))
- {
- // Also log a warning the first time we fail to resolve one of the recursively discovered generic
- // instantiations from this module. The enumeration also yields back interfaces that couldn't be
- // resolved, as that step is performed after yielding. This is done so we can have our own logic
- // to log warnings or throw errors from here while we're processing interfaces in this module.
- if (discoveryState.TrackFailedResolutionType(interfaceSignature, module))
- {
- WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
- }
-
- continue;
- }
-
- discoveryState.TrackGenericInterfaceType(interfaceSignature, interopReferences);
- }
-
- continue;
- }
+ // Track the constructed generic type (if it's not applicable, it will be a no-op)
+ InteropTypeDiscovery.TryTrackGenericTypeInstance(
+ typeSignature: typeSignature,
+ args: args,
+ discoveryState: discoveryState,
+ interopReferences: interopReferences,
+ module: module);
}
}
catch (Exception e)
@@ -471,26 +269,13 @@ private static void DiscoverSzArrayTypes(
continue;
}
- // Ignore types that are not fully resolvable (this likely means a .dll is missing)
- if (!typeSignature.IsFullyResolvable(out _))
- {
- // Log a warning the first time we fail to resolve this SZ array in this module
- if (discoveryState.TrackFailedResolutionType(typeSignature, module))
- {
- WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors);
- }
-
- continue;
- }
-
- // Ignore array types that are not Windows Runtime types
- if (!typeSignature.IsWindowsRuntimeType(interopReferences))
- {
- continue;
- }
-
- // Track all SZ array types, as we'll need to emit marshalling code for them
- discoveryState.TrackSzArrayType(typeSignature);
+ // Track the SZ array type (if it's not applicable, it will be a no-op)
+ InteropTypeDiscovery.TryTrackSzArrayType(
+ typeSignature: typeSignature,
+ args: args,
+ discoveryState: discoveryState,
+ interopReferences: interopReferences,
+ module: module);
}
}
catch (Exception e)
diff --git a/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs b/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs
new file mode 100644
index 000000000..74b5ab799
--- /dev/null
+++ b/src/WinRT.Interop.Generator/Helpers/TypeExclusions.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.References;
+
+namespace WindowsRuntime.InteropGenerator.Helpers;
+
+///
+/// Contains logic to handle special-case types to exclude from the interop API surface.
+///
+internal static class TypeExclusions
+{
+ ///
+ /// Checks whether a given type should be excluded from the interop API surface.
+ ///
+ /// The type to check.
+ /// The instance to use.
+ /// Whether should be excluded from the interop API surface.
+ public static bool IsExcluded(ITypeDescriptor type, InteropReferences interopReferences)
+ {
+ // If we have a constructed generic type, extract the generic type definition
+ // and use that for checking. We don't have exclusion logic for type arguments.
+ if (type is GenericInstanceTypeSignature typeSignature)
+ {
+ return IsExcluded(typeSignature.GenericType, interopReferences);
+ }
+
+ // Also handle SZ arrays and check their element type
+ if (type is SzArrayTypeSignature arraySignature)
+ {
+ return IsExcluded(arraySignature.BaseType, interopReferences);
+ }
+
+ // This is the full set of types we want to always exclude from marshalling.
+ // We can't put this in a global variable as we need the 'InteropReferences'
+ // instance to actually retrieve the type references to enumerate. However
+ // by just using a 'ReadOnlySpan' here, the full list is stack-allocated.
+ ReadOnlySpan excludedTypes =
+ [
+ interopReferences.Task1,
+ interopReferences.ConditionalWeakTable2
+ ];
+
+ // Check if the input type matches any of our exclusions
+ return excludedTypes.Contains(type, SignatureComparer.IgnoreVersion);
+ }
+}
diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs
index 348103964..36bc0b02b 100644
--- a/src/WinRT.Interop.Generator/References/InteropReferences.cs
+++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs
@@ -193,6 +193,11 @@ public InteropReferences(
///
public GenericInstanceTypeSignature ReadOnlySpanInt32 => field ??= ReadOnlySpan1.MakeGenericValueType(_corLibTypeFactory.Int32);
+ ///
+ /// Gets the for .
+ ///
+ public TypeReference Task1 => field ??= _corLibTypeFactory.CorLibScope.CreateTypeReference("System.Threading.Tasks"u8, "Task`1"u8);
+
///
/// Gets the for .
///
diff --git a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncAction.cs b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncAction.cs
index 03cdcf9c8..b6720c55f 100644
--- a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncAction.cs
+++ b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncAction.cs
@@ -14,6 +14,7 @@ namespace WindowsRuntime;
/// The implementation of a native object for .
///
///
+[WindowsRuntimeManagedOnlyType]
internal sealed class WindowsRuntimeAsyncAction : WindowsRuntimeObject,
IAsyncAction,
IWindowsRuntimeInterface,
diff --git a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncActionWithProgress{TProgress}.cs b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncActionWithProgress{TProgress}.cs
index 31213b8ba..7f192a974 100644
--- a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncActionWithProgress{TProgress}.cs
+++ b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncActionWithProgress{TProgress}.cs
@@ -17,6 +17,7 @@ namespace WindowsRuntime;
/// The type of progress information.
/// The implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperationWithProgress{TResult, TProgress}.cs b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperationWithProgress{TResult, TProgress}.cs
index a9d1dd212..a24863977 100644
--- a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperationWithProgress{TResult, TProgress}.cs
+++ b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperationWithProgress{TResult, TProgress}.cs
@@ -18,6 +18,7 @@ namespace WindowsRuntime;
/// The type of progress information.
/// The implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperation{TResult}.cs b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperation{TResult}.cs
index 53113048a..86bdd28dc 100644
--- a/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperation{TResult}.cs
+++ b/src/WinRT.Runtime2/AsyncInfo/WindowsRuntimeAsyncOperation{TResult}.cs
@@ -17,6 +17,7 @@ namespace WindowsRuntime;
/// The result type.
/// The implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerable.cs b/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerable.cs
index ceae5ea36..c7f81f8e8 100644
--- a/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerable.cs
+++ b/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerable.cs
@@ -12,6 +12,7 @@ namespace WindowsRuntime;
/// The implementation of all projected Windows Runtime types.
///
///
+[WindowsRuntimeManagedOnlyType]
internal sealed class WindowsRuntimeEnumerable : WindowsRuntimeObject, IEnumerable, IWindowsRuntimeInterface
{
///
diff --git a/src/WinRT.Runtime2/Bindables/WindowsRuntimeIterator.cs b/src/WinRT.Runtime2/Bindables/WindowsRuntimeIterator.cs
index e2e6c0e87..09ab26209 100644
--- a/src/WinRT.Runtime2/Bindables/WindowsRuntimeIterator.cs
+++ b/src/WinRT.Runtime2/Bindables/WindowsRuntimeIterator.cs
@@ -15,6 +15,7 @@ namespace WindowsRuntime;
/// The implementation of all projected Windows Runtime types.
///
///
+[WindowsRuntimeManagedOnlyType]
internal sealed unsafe class WindowsRuntimeIterator : WindowsRuntimeObject, IEnumerator, IWindowsRuntimeInterface
{
///
diff --git a/src/WinRT.Runtime2/Bindables/WindowsRuntimeList.cs b/src/WinRT.Runtime2/Bindables/WindowsRuntimeList.cs
index 640eb5ee4..47b275fd7 100644
--- a/src/WinRT.Runtime2/Bindables/WindowsRuntimeList.cs
+++ b/src/WinRT.Runtime2/Bindables/WindowsRuntimeList.cs
@@ -14,6 +14,7 @@ namespace WindowsRuntime;
/// The implementation of all projected Windows Runtime types.
///
///
+[WindowsRuntimeManagedOnlyType]
internal sealed class WindowsRuntimeList : WindowsRuntimeObject,
IList,
IWindowsRuntimeInterface,
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs
index 17f4d7cf1..1f6a55a3b 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs
@@ -21,6 +21,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IIterable<T> implementation type.
/// The Windows.Foundation.Collections.IMap<K, V> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerable{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerable{T}.cs
index 0f83daeac..6b7159e39 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerable{T}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerable{T}.cs
@@ -17,6 +17,7 @@ namespace WindowsRuntime;
/// The type of objects to enumerate.
/// The Windows.Foundation.Collections.IIterable<T> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs
index dfddca617..637d08e82 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs
@@ -18,6 +18,7 @@ namespace WindowsRuntime;
/// The type of objects to enumerate.
/// The implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeList{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeList{T}.cs
index 67d624505..87a49a9ef 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeList{T}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeList{T}.cs
@@ -21,6 +21,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IIterable<T> implementation type.
/// The Windows.Foundation.Collections.IVector<T> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeMapChangedEventArgs{TKey}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeMapChangedEventArgs{TKey}.cs
index 09eccc3f3..fa97363bd 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeMapChangedEventArgs{TKey}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeMapChangedEventArgs{TKey}.cs
@@ -14,6 +14,7 @@ namespace WindowsRuntime;
/// The type of keys in the map.
/// The Windows.Foundation.Collections.IMapChangedEventArgs<K> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs
index 79c5570ef..efa2a4a95 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs
@@ -26,6 +26,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IMap<K, V> implementation type.
/// The Windows.Foundation.Collections.IObservableMap<K, V> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableVector{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableVector{T}.cs
index c2da94849..a1df93827 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableVector{T}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableVector{T}.cs
@@ -24,6 +24,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IVector<T> implementation type.
/// The Windows.Foundation.Collections.IObservableVector<T> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs
index e2e28038e..df74e42c4 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs
@@ -21,6 +21,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IIterable<T> implementation type.
/// The Windows.Foundation.Collections.IMapView<K, V> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyList{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyList{T}.cs
index 3c1ebf79a..bf171a5de 100644
--- a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyList{T}.cs
+++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyList{T}.cs
@@ -19,6 +19,7 @@ namespace WindowsRuntime;
/// The Windows.Foundation.Collections.IIterable<T> implementation type.
/// The Windows.Foundation.Collections.IVectorView<T> implementation type.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
diff --git a/src/WinRT.Runtime2/InteropServices/ObjectReference/WindowsRuntimeObjectReference.cs b/src/WinRT.Runtime2/InteropServices/ObjectReference/WindowsRuntimeObjectReference.cs
index 26bb13096..ee06ae965 100644
--- a/src/WinRT.Runtime2/InteropServices/ObjectReference/WindowsRuntimeObjectReference.cs
+++ b/src/WinRT.Runtime2/InteropServices/ObjectReference/WindowsRuntimeObjectReference.cs
@@ -10,6 +10,7 @@ namespace WindowsRuntime.InteropServices;
///
/// A managed, low-level wrapper for a native Windows Runtime object.
///
+[WindowsRuntimeManagedOnlyType]
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]