Skip to content

Add opt-in opaque RCW marshalling feature switch - #2642

Merged
Sergio Pedri (Sergio0694) merged 2 commits into
staging/3.0from
user/sergiopedri/opaque-rcw-marshalling
Nov 5, 2026
Merged

Sergio Pedri (Sergio0694) merged 2 commits into
staging/3.0from
user/sergiopedri/opaque-rcw-marshalling

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Add an opt-in (off by default) feature switch that lets projected runtime class RCWs without a native-exposed proxy be marshalled to native code as opaque CCWs, instead of throwing NotSupportedException. It is controlled by the new CsWinRTEnableOpaqueRcwMarshalling MSBuild property, which maps to the new CSWINRT_ENABLE_OPAQUE_RCW_MARSHALLING runtime switch.

Motivation

On some Windows versions, native XAML asks the .NET reference tracker host to wrap a projected RCW (for instance a Frame or a Grid set as Window.Content) through ComWrappers.GetOrCreateComInterfaceForObject. In CsWinRT 3.0 this reaches WindowsRuntimeMarshallingInfo.GetVtableInfo(), which throws for projected classes that don't have a native-exposed proxy, and XAML then fails fast.

With the switch enabled, those requests get an opaque CCW that exposes only the standard interfaces (IInspectable, IStringable, IWeakReferenceSource, IMarshal, IAgileObject, and so on). The RCW itself forwards any other QueryInterface request to its native object. Native-exposed types keep their own vtables, and the new behavior only applies to projected runtime classes.

The feature is pay-for-play: both fallbacks sit on paths that would otherwise throw, and are removed by ILLink and ILC when the switch is disabled, so there is no overhead by default.

Changes

  • src/WinRT.Runtime2/Properties/WindowsRuntimeFeatureSwitches.cs: new [FeatureSwitchDefinition] property EnableOpaqueRcwMarshalling, backed by CSWINRT_ENABLE_OPAQUE_RCW_MARSHALLING (default false).
  • nuget/Microsoft.Windows.CsWinRT.targets: new CsWinRTEnableOpaqueRcwMarshalling property (default false) and a trimmable RuntimeHostConfigurationOption.
  • src/WinRT.Runtime2/InteropServices/TypeMapInfo/WindowsRuntimeMarshallingInfo.cs: when the switch is enabled, GetVtableInfo() reuses the shared opaque object vtables for projected classes, and GetRuntimeClassName() falls back to the full type name. New private IsProjectedClass helper.
  • src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeNativeExposedTypeAttribute.cs: remarks now point to the new property as an alternative for opaque-only exposure.
  • src/Tests/FunctionalTests/OpaqueRcwMarshalling/: new functional test for the enabled case (managed identity, standard and forwarded QueryInterface, GetIids, runtime class name, unwrapping through regular marshalling, and a native-exposed JsonArray keeping its own vtables).
  • src/Tests/FunctionalTests/NativeExposedType/Program.cs: new check that the default (switch disabled) still fails with NotSupportedException.
  • src/cswinrt.slnx and build/AzurePipelineTemplates/CsWinRT-FunctionalTest-Steps.yml: wire up the new functional test.
  • docs/aot-trimming.md and docs/interop.md: document the property and the opaque CCW semantics and limitations.

Validation

  • OpaqueRcwMarshalling passes (exit code 100) on x64 Native AOT and x86 trimmed JIT, and fails with the expected NotSupportedException when the switch is forced off.
  • NativeExposedType passes (exit code 100), including the new disabled-default check.
  • WinRT.Runtime builds clean in Release (warnings as errors), both the implementation and reference assembly variants.

Limitations

  • The end-to-end packaged WinUI/XAML scenario was not validated here.
  • Covariant cases (for example DependencyObjectCollection as an items source) still need [WindowsRuntimeNativeExposedType].
  • Only ComWrappers-driven paths and projected runtime classes are affected.

Add a new [FeatureSwitchDefinition]-based feature switch to control whether projected runtime class instances (RCWs) that are not native-exposed can be marshalled to native code as opaque CCWs, instead of reporting NotSupportedException. Defaults to false. Configurable via the CsWinRTEnableOpaqueRcwMarshalling MSBuild property.

On some Windows versions, native XAML asks the reference tracker host to wrap a projected RCW (for instance a Frame or a Grid set as Window.Content) through ComWrappers. That request reaches WindowsRuntimeMarshallingInfo.GetVtableInfo(), which throws for projected classes without a native-exposed proxy, and XAML then fails fast. When the switch is enabled, GetVtableInfo() reuses the shared opaque object vtables for those classes, and GetRuntimeClassName() falls back to the full name of the projected type. Native-exposed types keep their own vtables, and the RCW forwards any other QueryInterface request to its native object. Covariant scenarios still require [WindowsRuntimeNativeExposedType].

Both fallbacks only run on paths that would otherwise throw, and are removed by ILLink and ILC when the switch is disabled, so there is no overhead by default.

Add an OpaqueRcwMarshalling functional test for the enabled case, extend NativeExposedType to check the disabled default, and document the property in aot-trimming.md and interop.md.

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

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@Sergio0694
Sergio Pedri (Sergio0694) merged commit 3e12dfb into staging/3.0 Nov 5, 2026
20 checks passed
@Sergio0694
Sergio Pedri (Sergio0694) deleted the user/sergiopedri/opaque-rcw-marshalling branch November 5, 2026 03:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CsWinRT 3.0 enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants