Skip to content

Extend full CCW support to non-WinRT SZ array types in 'cswinrtgen'#2228

Open
Sergio0694 wants to merge 25 commits intostaging/3.0from
user/sergiopedri/sz-array-ccw
Open

Extend full CCW support to non-WinRT SZ array types in 'cswinrtgen'#2228
Sergio0694 wants to merge 25 commits intostaging/3.0from
user/sergiopedri/sz-array-ccw

Conversation

@Sergio0694
Copy link
Member

Title. Also updates the CCW for WinRT SZ array types to support covariance.

Update TypeSignature enumeration APIs to accept InteropReferences and properly handle SZ arrays. EnumerateAllInterfaces and EnumerateBaseTypes now take an InteropReferences parameter and special-case SzArrayTypeSignature to yield runtime-implemented interfaces (IList, ICollection, IEnumerable and their generic variants) and return System.Array as the array base type. Call sites in InteropTypeDiscovery.* and WindowsRuntimeTypeAnalyzer were updated to pass interopReferences. Also added an ICollection TypeReference to InteropReferences and the required using directive to enable these changes. These updates ensure array interfaces and base types are discovered correctly for WinRT interop.
Treat SZ array (single-dim zero-based) types specially during interop discovery: use the TypeSignature when checking exclusions and reporting unresolved interface implementations, and add TryTrackExposedSzArrayType to enumerate and track interfaces for exposed SZ arrays. The new method performs resolution checks, respects managed-only exclusions, protects against recursion, collects covariant interfaces (including constructed generics), and records user-defined array types for later generation. Added a new warning (id 79) for unresolved array element base types and updated the discovery comment to clarify that SZ arrays are tracked for both WinRT and user-defined scenarios.
Use a dedicated marking path for single-dimension SZ array types during interop discovery to prevent recursion. Add a backing ConcurrentDictionary for SZ arrays and a TryMarkSzArrayType method, and update the recursion check to call it. Also narrow the TryMarkWindowsRuntimeGenericInterfaceTypeInstance parameter to GenericInstanceTypeSignature for stronger typing.
Centralize and improve handling of single-dimensional (SZ) array types during interop discovery. Replace ad-hoc interface discovery with a single TryTrackExposedSzArrayType call (InteropTypeDiscovery.Generics.cs), use the resolved TypeDefinition when available, and only special-case arrays as WinRT arrays when their element type is a WinRT type (InteropTypeDiscovery.cs). Change discovery state storage to track SZ arrays with their vtable TypeSignatureEquatableSet (instead of a simple byte set), add a deduplicating _szArrayVtableTypes cache, and update TrackSzArrayType to accept and cache vtable sets (InteropGeneratorDiscoveryState.cs). These changes prepare for correct marshalling/vtable handling of exposed array types and avoid duplicate allocations of equivalent vtable sets.
Change the backing dictionary for _szArrayTypes from ConcurrentDictionary<TypeSignature, ...> to ConcurrentDictionary<SzArrayTypeSignature, ...> in InteropGeneratorDiscoveryState.cs. This narrows the key type to represent single-dimension zero-based array signatures explicitly and improves type correctness when tracking sz-array types.
Use typeSignature.IsWindowsRuntimeType instead of checking BaseType when determining if an array is a Windows Runtime type. Also update the comment to clarify that the array itself (not its element's base type) must be considered for specialized marshalling. This ensures SzArray types are correctly tracked for interop marshalling via TrackSzArrayType.
Reorder and add accessors for SZ array discovery state: reintroduces SzArrayTypes, and adds SzArrayAndVtableTypes (IReadOnlyDictionary<SzArrayTypeSignature, TypeSignatureEquatableSet>) and SzArrayVtableTypes (IReadOnlyCollection<TypeSignatureEquatableSet>). These properties expose SZ array types, their associated vtable mappings, and the collection of vtable types for use by the interop generator discovery logic.
Add pooling and duplicate-IID detection for GeneratedComInterface processing. Introduces a ConcurrentBag<HashSet<Guid>> (IidHashSetPool) in InteropTypeDiscovery, acquires/clears a HashSet per type, checks for duplicate IIDs and logs or throws a well-known warning when duplicates are found, and returns the set to the pool. Also adds a new warning factory GeneratedComInterfaceDuplicateIidWarning (id 80) in WellKnownInteropExceptions to produce the diagnostic message.
Track an explicitly implemented IMarshal entry instead of a boolean flag and use it when building the interface entries list (add the user-provided IMarshal entry if present; otherwise add the default). In the WellKnownTypeDefinitionFactory, split the total entry count into dynamic entries and six default entries, validate the dynamic count, emit fields for dynamic entries in a loop, then append the six fixed fields (IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, IUnknown). This ensures correct struct layout and allows user-defined types to override IMarshal.
Introduce InteropInterfaceEntryInfo base type and InteropInterfaceEntriesResolver to enumerate CCW interface entries for managed types. EnumerateInterfaceEntries handles generic instances, custom-mapped/manually projected types, generated COM interfaces, and projected WinRT interfaces, producing concrete entry implementations (WindowsRuntimeInterfaceEntryInfo and ComInterfaceEntryInfo). Concrete entries implement loading of IID and vtable into a CIL instruction stream and detect IMarshal via WellKnownInterfaceIIDs. Uses AsmResolver and InteropReferences for imports and type resolution.
Refactor and split interface-entry resolution into metadata vs native flows. Renamed EnumerateInterfaceEntries to EnumerateMetadataInterfaceEntries and added EnumerateNativeInterfaceEntries to emit the built-in native interface implementations in a defined order (IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, IUnknown). Introduced TryGetUserDefinedIMarshalInterfaceImplementation to prefer a user-defined IMarshal implementation when present and special-case skipping of IMarshal during metadata enumeration. Removed the IsIMarshalInterface abstract member from InteropInterfaceEntryInfo and adjusted WindowsRuntimeInterfaceEntryInfo/ComInterfaceEntryInfo constructors and usage accordingly. Minor additions: System.Diagnostics.CodeAnalysis import and small formatting/refactor cleanup.
Delegate construction of COM/WinRT interface entries to InteropInterfaceEntriesResolver. This removes the inlined logic that assembled WindowsRuntime/Com entry types and the nested InterfaceEntryInfo/WindowsRuntimeInterfaceEntryInfo/ComInterfaceEntryInfo classes, switching to a thread-static List<InteropInterfaceEntryInfo>. Added a useWindowsUIXamlProjections parameter to InterfaceEntriesImpl and updated call sites to pass args.UseWindowsUIXamlProjections. Also adjusted signatures/types (implTypes spans, resolver method parameters) to match the new resolver-based flow and simplify entry enumeration.
Add a runtime check that implTypes.Length equals entriesFieldType.Fields.Count using ArgumentOutOfRangeException.ThrowIfNotEqual. This enforces that all interface entries are initialized before declaring the implementation type and fails fast if there's a mismatch.
Introduce a single constant for the default native COM interface entries (InteropInterfaceEntriesResolver.NumberOfNativeComInterfaceEntries = 6) and replace hard-coded uses. Change UserDefinedType.InterfaceEntriesImpl to return the shared interfaceEntriesType (reusing interopDefinitions.UserDefinedInterfaceEntries(entriesList.Count)) and pass that type into ComWrappersMarshallerAttribute instead of recomputing based on vtableSizes. Update the emitter to capture and forward the new out parameter and adjust WellKnownTypeDefinitionFactory to use the centralized constant. This removes duplication, avoids magic numbers, and ensures cached/reused interface entry types are used consistently.
Introduce generation and emission of COM interface entry types for SZ (single-dimensional zero-based) arrays with WinRT element types. Adds WellKnownTypeDefinitionFactory.SzArrayInterfaceEntriesType to build a ValueType containing a special IReferenceArray entry, a configurable set of dynamic interface entries, and the default COM entries (IPropertyValue, IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, IUnknown), with argument validation for the dynamic entry count. Adds storage and accessors in InteropDefinitions (_szArrayInterfaceEntries, SzArrayInterfaceEntries, EnumerateSzArrayInterfaceEntriesTypes) and emits those generated types in InteropGenerator.Emit alongside user-defined interface entries. This mirrors existing user-defined-type behavior so SZ arrays are handled consistently during interop generation.
Add a new generic extension method OrderByFullyQualifiedTypeName that lets callers sort an IEnumerable<T> by a selected ITypeDescriptor key using TypeDescriptorComparer.Create<TKey>(). Also add a missing using System; and normalize the XML docs to reference ArgumentNullException (instead of the fully-qualified name). Changes live in src/WinRT.Interop.Generator/Extensions/ITypeDescriptorExtensions.cs and provide a deferred-execution ordering overload for descriptor key selectors.
Replace the previous OrderBy call that used TypeDescriptorComparer with a call to OrderByFullyQualifiedTypeName to sort discoveryState.UserDefinedAndVtableTypes by fully qualified type name. This ensures a deterministic, clearer ordering for emitting proxy types and simplifies the comparer usage.
Introduce a static Create(IMethodDefOrRef get_IID, IMethodDefOrRef get_Vtable) on InteropInterfaceEntriesResolver that returns a WindowsRuntimeInterfaceEntryInfo constructed from the provided methods. Adds XML documentation for the method, its parameters, and return value to clarify its purpose as a convenience factory for creating InteropInterfaceEntryInfo instances.
Reuse a thread-static List<InteropInterfaceEntryInfo> to build COM interface entries for SZ array marshallers to reduce allocations and centralize entry construction. InterfaceEntriesImpl now accepts vtableTypes and outputs an interfaceEntriesType; it clears and populates the cached list with the IReferenceArray entry, metadata interface entries (from vtableTypes), IPropertyValue, and native interface entries, then passes the entries to the underlying generator via CollectionsMarshal.AsSpan. ComWrappersMarshallerAttribute was updated to take the resulting interface entries type and use its field count. InteropGenerator.Emit now iterates SzArrayAndVtableTypes, forwards vtableTypes, and captures the interfaceEntriesType to pass into the marshaller attribute. Added/updated using directives for required types and introduced a ThreadStatic entriesList field.
Delete the ReferenceArrayInterfaceEntriesType factory and its InteropDefinitions property, and remove the _szArrayVtableTypes deduplication cache and SzArrayVtableTypes exposure. Update SzArray registration to store the provided vtableTypes directly in _szArrayTypes. Also clean up a couple of XML comments (use IReference<T> in comments). These changes simplify handling of SZ-array vtable types by removing an extra layer of deduplication and remove the unused/refactored IReference array interface entries type.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends COM Callable Wrapper (CCW) generation in cswinrtgen to better support SZ array types (including non-WinRT element types), and updates the CCW interface-entry generation for WinRT SZ arrays to account for covariance when enumerating implemented interfaces.

Changes:

  • Introduced a shared abstraction (InteropInterfaceEntryInfo) and a central resolver (InteropInterfaceEntriesResolver) to build COM interface entry initialization logic consistently across user-defined types and SZ arrays.
  • Updated discovery to track SZ arrays with their computed vtable interface sets, and expanded interface enumeration helpers to correctly model SZ array base types/interfaces.
  • Added new generated COM interface-entries type factories/caches for user-defined types and SZ arrays, and updated emit paths to include these dynamically generated types.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntryInfo.cs Adds a shared abstraction for emitting IID/vtable loads into static constructors.
src/WinRT.Interop.Generator/Resolvers/InteropInterfaceEntriesResolver.cs Centralizes enumeration of metadata/native COM interface entries (incl. user-defined IMarshal override).
src/WinRT.Interop.Generator/References/InteropReferences.cs Adds System.Collections.ICollection reference used for SZ array interface enumeration.
src/WinRT.Interop.Generator/References/InteropDefinitions.cs Adds SZ-array interface-entries type cache and enumeration; reshapes interface-entries type emission.
src/WinRT.Interop.Generator/Helpers/WindowsRuntimeTypeAnalyzer.cs Routes interface/base-type enumeration through updated helpers requiring InteropReferences.
src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs Tracks SZ arrays alongside their vtable interface sets; adjusts marking APIs and exposes new map.
src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs Emits SZ array types using tracked vtable sets; emits both user-defined and SZ-array interface-entries types.
src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs Updates SZ array discovery comment to reflect broader tracking scope.
src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs Refactors interface-entries type generation: user-defined vs SZ array layouts and dynamic-entry computation.
src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs Fixes SZ array interface/base-type enumeration (arrays implement fixed interfaces; base type is System.Array).
src/WinRT.Interop.Generator/Extensions/ITypeDescriptorExtensions.cs Adds a keyed OrderByFullyQualifiedTypeName helper used for stable ordering of pair sequences.
src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs Adds warnings for array element type resolution failures and duplicate IIDs on generated COM interfaces.
src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs Adds SZ array exposure tracking path; adds duplicate-IID validation for [GeneratedComInterface] interfaces.
src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs Routes SZ array discovery into the new exposed-array tracking flow; updates generic interface crawl.
src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs Switches interface-entry initialization to the new InteropInterfaceEntryInfo model and enforces entry/field count match.
src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.UserDefinedType.cs Uses the shared resolver to build interface entry lists and reuses interface-entries struct types by computed count.
src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs Reworks SZ array interface entry generation to be dynamic and include metadata/native entries and IPropertyValue.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sergio0694 and others added 5 commits February 6, 2026 11:39
Correct a grammar issue and normalize XML comment spacing: change the summary in InteropInterfaceEntriesResolver.cs from "to exposed" to "to be exposed", and adjust whitespace on a <param> XML comment in InteropTypeDefinitionBuilder.SzArray.cs for consistent formatting.
Call SzArrayInterfaceEntries instead of UserDefinedInterfaceEntries when creating interface entries for SZ array types so entries are reused by SZ-array semantics. Also update XML doc comments in InteropGeneratorDiscoveryState to reflect the combined "...AndVtableTypes" naming and remove the redundant SzArrayTypes property to keep the discovery API consistent.
Compute whether the current interface is a Windows Runtime type and use that to include overridable interfaces during covariant expansion. The condition now accepts covariant signatures that match the original interface when the original is a WinRT type, ensuring overridable interfaces are present in CCW interface entries while still filtering out other '[exclusiveto]' interfaces from the expansion. Also clarifies in a comment that SZ arrays cannot have overridable interfaces so the simpler exclusiveto check is sufficient there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants