Skip to content

Add opt-in CCW marshalling for projected Windows Runtime types#2481

Draft
Sergio0694 wants to merge 8 commits into
staging/3.0from
user/sergiopedri/native-exposed-type-attribute
Draft

Add opt-in CCW marshalling for projected Windows Runtime types#2481
Sergio0694 wants to merge 8 commits into
staging/3.0from
user/sergiopedri/native-exposed-type-attribute

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Introduce a new WindowsRuntimeNativeExposedTypeAttribute that lets a projected Windows Runtime class be explicitly opted into CCW (COM Callable Wrapper) marshalling code generation, which the interop generator normally skips for projected types. The PR adds interop generator support, three analyzer diagnostics, unit and functional (JIT and Native AOT) test coverage, and improved marshalling failure diagnostics.

Motivation

The interop generator automatically generates CCW marshalling code for all managed types that implement one or more Windows Runtime interfaces. Projected Windows Runtime types are deliberately skipped, because they are backed by native objects and are marshalled by unwrapping the underlying native object directly, so they never normally require a CCW. There are, however, niche scenarios where a CCW is required for a projected type, and there was previously no way to request one.

The motivating scenario is using a projected collection type as the items source for a XAML control. Consider a DependencyObjectCollection assigned as the items source of an ItemsControl. The control queries the assigned object (marshalled as an IInspectable) for IBindableIterable and IIterable<IInspectable>. This succeeds for most collection types, but DependencyObjectCollection implements neither of those interfaces: it implements IIterable<DependencyObject>, and Windows Runtime generic interfaces are not covariant on the native side (each generic instantiation has a completely different IID). To handle this, XAML controls fall back to obtaining a CCW from the source object and querying that instead. The .NET runtime creates an RCW for the native object and uses ComWrappers to obtain a CCW proxy over it; because the RCW implements IEnumerable<DependencyObject>, which is covariant in C#, the query for IIterable<IInspectable> then succeeds through the proxy. This requires a CCW vtable to exist for the projected type, which the interop generator would not normally generate. The new attribute makes it possible to request exactly that.

Changes

  • src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeNativeExposedTypeAttribute.cs: new public, assembly-level attribute (AllowMultiple) that opts a projected Windows Runtime class into CCW marshalling code generation, with detailed documentation of the motivating scenario.
  • src/WinRT.Interop.Generator/ (Discovery/InteropTypeDiscovery.cs, Generation/InteropGenerator.Discover.cs, References/InteropReferences.cs, Errors/WellKnownInteropExceptions.cs): discover [assembly: WindowsRuntimeNativeExposedType(typeof(X))] applications and route the referenced projected types through the user-defined-type CCW pipeline, bypassing the check that normally skips projected types while keeping all other discovery filters.
  • src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs: add the missing IStringable case to get_IID. Every projected class implements the manually-projected IStringable, so resolving its interface entry is required when CCW code is generated for a projected type; this path was previously unreachable and threw CSWINRTINTEROPGEN0052.
  • src/Authoring/WinRT.SourceGenerator2/Diagnostics/ (Analyzers/WindowsRuntimeNativeExposedTypeAnalyzer.cs, DiagnosticDescriptors.cs, AnalyzerReleases.Shipped.md): new analyzer emitting CSWINRT2018 (target type cannot be instantiated), CSWINRT2019 (target type is not a projected class, so CCW code is already generated automatically), and CSWINRT2020 (the same type is targeted by more than one application in the assembly). Applications in generated code are excluded, so a duplicate spanning user code and generated code only warns on the user application.
  • Tests (src/Tests/SourceGenerator2Test/Test_WindowsRuntimeNativeExposedTypeAnalyzer.cs, src/Tests/SourceGenerator2Test/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs, src/Tests/UnitTest/NativeExposedTypeTests.cs, src/Tests/FunctionalTests/NativeExposedType/, src/cswinrt.slnx, src/build.cmd): analyzer unit tests (including a test-harness helper for injecting generated code to validate the generated-code exclusion), plus a self-contained NativeExposedType functional test that runs in both JIT and Native AOT and a unit test. Both verify that the interop generator registers a proxy type map association for an opted-in projected type and not for a projected type that was not opted in.
  • src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComWrappersMarshallerAttribute.cs and src/WinRT.Runtime2/InteropServices/TypeMapInfo/WindowsRuntimeMarshallingInfo.cs: improve marshalling failure diagnostics. The NotSupportedException messages now include the concrete marshaller type, and ComputeVtables returns null when not overridden so WindowsRuntimeMarshallingInfo can throw a detailed exception that names the marshaller and metadata provider types and points to WindowsRuntimeNativeExposedTypeAttribute as the remediation.

Sergio0694 and others added 7 commits July 2, 2026 15:06
Add a public assembly-level attribute in the WindowsRuntime.InteropServices namespace that lets developers explicitly opt a projected Windows Runtime class type into CCW marshalling code generation, which the interop generator normally skips for projected types.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… generator

Discover '[assembly: WindowsRuntimeNativeExposedType(typeof(X))]' attributes and route the referenced types through the user-defined type CCW pipeline, bypassing the check that normally skips projected Windows Runtime types. All other discovery filters still apply, so opting in a type that does not need CCW support is a safe no-op.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add three warning diagnostics (CSWINRT2018, CSWINRT2019, CSWINRT2020) reported by a new 'WindowsRuntimeNativeExposedTypeAnalyzer'. CSWINRT2018 warns when the target type cannot be instantiated (interfaces, abstract or static classes, and generic type definitions), CSWINRT2019 warns when the target type is not a projected Windows Runtime class (for which CCW marshalling code is already generated automatically), and CSWINRT2020 warns when the same type is targeted by more than one application of the attribute in the assembly. Applications in generated code are excluded from the diagnostics, so that a duplicate spanning user code and generated code only warns on the user application.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add unit tests covering all three diagnostics (CSWINRT2018/2019/2020) for the
'WindowsRuntimeNativeExposedTypeAnalyzer', including valid projected class usage,
non-instantiable and non-projected-class targets, duplicate applications, and
suppression of diagnostics for applications that appear in generated code.

Extend the analyzer test helper to allow adding an additional generated source
file, so the generated-code suppression behavior can be validated.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ted types

When the interop generator generates CCW marshalling code for a projected type opted in via '[WindowsRuntimeNativeExposedType]', the type's vtable set includes the manually-projected 'IStringable' interface, which every projected Windows Runtime class implements. Resolving its interface entry routes through 'WellKnownInterfaceIIDs.get_IID', which was missing a case for 'IStringable' and so threw CSWINRTINTEROPGEN0052. This path was never reached before, because managed user-defined types use the built-in 'IStringable' native entry instead of carrying the projected interface in their vtable set. Add the missing case so that CCW code can be generated for projected types.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add a 'NativeExposedType' functional test and a 'NativeExposedTypeTests' unit test that opt a projected type into CCW marshalling code generation via '[WindowsRuntimeNativeExposedType]' and assert that the interop generator registered a proxy type map association for it, while a projected type that was not opted in has no such association. The functional test is a self-contained executable that runs in both JIT and Native AOT, and is wired into 'cswinrt.slnx' and the functional test list in 'build.cmd'.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Enhance debugging info and error handling for WindowsRuntimeComWrappers marshalling.

- Include the runtime attribute type in NotSupportedException messages for GetOrCreateComInterfaceForObject and CreateObject to make failures easier to diagnose.
- Change ComputeVtables to return null (with count=0) when not overridden; WindowsRuntimeMarshallingInfo now detects null vtable entries and throws a detailed NotSupportedException that includes the marshaller type and metadata provider type, and guidance for enabling marshalling or filing an issue.
- Add pragma to suppress IDE0046 alongside IDE0008.

This improves developer diagnostics when custom marshallers are incomplete.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Sergio0694 Sergio0694 added enhancement New feature or request debugging Improvements to debugging experience tooling CsWinRT 3.0 labels Jul 3, 2026
@Sergio0694 Sergio0694 requested a review from manodasanW July 3, 2026 01:52
Refactor WindowsRuntimeNativeExposedTypeAnalyzer: initialize validTargetTypes correctly, enumerate assembly attributes via context.Compilation, simplify Classify return logic (explicit guard + valid return), and relax CountOccurrences to accept IEnumerable<ITypeSymbol>. Also remove IDE0046 from WindowsRuntimeMarshallingInfo pragma disables. These edits improve clarity, correctness of attribute enumeration, and reduce unnecessary suppressed warnings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CsWinRT 3.0 debugging Improvements to debugging experience enhancement New feature or request tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant