Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ parameters:
- DerivedClassAsBaseClass
- CCW
- NativeExposedType
- OpaqueRcwMarshalling
- TypeMarshaling
- ImplementWinMDTypes
- UserDefinedTypeNames
Expand Down
26 changes: 26 additions & 0 deletions docs/aot-trimming.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Feature switches are set as MSBuild properties in your project file:
| `CsWinRTEnableMarshalingTypeValidation` | `false` | Validates the `MarshalingType` to determine whether the metadata is the same as the implementation. |
| `CsWinRTEnableDefaultCustomPropertyProviderSupport` | `true` | Adds a default, type-only `ICustomPropertyProvider` to generated managed CCWs and the runtime's shared opaque-object CCW, without replacing explicit providers. |
| `CsWinRTSuppressCustomPropertyNotSupportedException` | `false` | Returns null instead of reporting `NotSupportedException` when the default provider cannot supply a property or indexer. |
| `CsWinRTEnableOpaqueRcwMarshalling` | `false` | Allows projected runtime class instances (RCWs) that are not native-exposed to be marshalled to native code as opaque CCWs, instead of reporting `NotSupportedException`. |

## Selecting dynamic casting for exclusive interfaces

Expand All @@ -40,6 +41,31 @@ Reference projection packages preserve the exact effective selection as generato

Opting out avoids the selected interface's IDIC shim and type-map association, but does not remove ABI helpers or CCW implementations still needed by public/default/overridable interfaces. In the single-method exclusive-interface regression fixture, opting in adds exactly one shim type, one DIM method, and one assembly type-map association. Final binary size depends on trimming and reachability.

## Marshalling projected runtime classes as opaque CCWs

Projected runtime classes are backed by native objects, so passing one to native code unwraps the native object and no CCW is involved. Native code can still ask the runtime for a CCW around the managed wrapper (RCW) through `ComWrappers`. For example, on some Windows versions XAML asks the reference tracker host to wrap a projected object it was handed, such as a `Frame` or a `Grid` set as `Window.Content`. A projected class has no CCW vtables unless it is [native-exposed](interop.md#create-ccw), so by default this request is reported as a `NotSupportedException`, and the native caller can fail fast.

To expose these objects to native code as opaque CCWs instead, opt in by setting the following property in the project file:

```xml
<PropertyGroup>
<CsWinRTEnableOpaqueRcwMarshalling>true</CsWinRTEnableOpaqueRcwMarshalling>
</PropertyGroup>
```

This defaults to `false` and sets the `CSWINRT_ENABLE_OPAQUE_RCW_MARSHALLING` runtime feature switch. The CCW is then the runtime's shared opaque-object CCW, the same one used for plain managed objects, so the generated marshalling code does not change and no type needs to be annotated. The behavior is as follows:

- The CCW implements `IInspectable`, `IStringable`, `IWeakReferenceSource`, `IMarshal` and `IAgileObject`, plus `ICustomPropertyProvider` when `CsWinRTEnableDefaultCustomPropertyProviderSupport` is enabled. `GetIids` reports only these interfaces and `IUnknown`.
- Queries for any other interface, including the interfaces of the projected type, are forwarded by the RCW to the native object, so native code can still reach the interfaces that the native object implements. They fail if the native object does not implement them.
- `GetRuntimeClassName` reports the full name of the projected type, such as `Windows.Data.Json.JsonObject`. A plain opaque object reports `Object`.
- Native-exposed projected types are not affected. They keep the CCW vtables from their own `[WindowsRuntimeNativeExposedType]` annotation.
- Only `ComWrappers` requests are affected. Ordinary marshalling, such as passing the RCW to a projected method, still unwraps the native object and keeps the same object identity.
- Only projected runtime classes are covered, not interfaces, delegates or other types.

The opaque CCW does not implement the interfaces of the projected type, and only forwards queries for them to the native object. Scenarios that need the CCW itself to implement additional interfaces still require `[assembly: WindowsRuntimeNativeExposedType(typeof(...))]`. This is the case for the covariant collection interfaces that XAML queries when a `DependencyObjectCollection` is used as an items source, as described in [Create CCW](interop.md#create-ccw).

The switch is pay-for-play: when it is disabled, the trimmer and Native AOT remove the code behind it.

## ICustomPropertyProvider support for XAML binding

Non source-generated WinUI and UWP XAML binding scenarios (i.e., not `x:Bind`) such as `DisplayMemberPath` make use of `ICustomPropertyProvider`. CsWinRT 3.0 provides an AOT-safe source-generated implementation via the `[GeneratedCustomPropertyProvider]` attribute.
Expand Down
2 changes: 2 additions & 0 deletions docs/interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ Native code can also need a CCW **around a projected native object's managed wra

This supplies the wrapper's CCW vtables without replacing its RCW marshaller. Ordinary marshalling still unwraps the original native object, and native-to-managed conversion still creates the projected type. Collection adapters can wrap native enumerators when a required bindable or covariant iterator interface is unavailable; ordinary interface marshalling remains strict.

Projected classes that are not annotated this way have no CCW vtables, so a CCW request for one of them fails with a `NotSupportedException` by default. If native code only needs such objects as opaque references, set `CsWinRTEnableOpaqueRcwMarshalling` to `true` to expose them through the shared opaque-object CCW instead, as described in [Marshalling projected runtime classes as opaque CCWs](aot-trimming.md#marshalling-projected-runtime-classes-as-opaque-ccws). Annotated classes keep their own vtables, and scenarios that need additional interfaces, such as the covariant collection interfaces above, still need the annotation.

### Release native pointer

To release a native COM pointer. Unlike `Marshal.Release`, this method is null-safe:
Expand Down
6 changes: 6 additions & 0 deletions nuget/Microsoft.Windows.CsWinRT.targets
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<CsWinRTEnableMarshalingTypeMetadataSupport Condition="'$(CsWinRTEnableMarshalingTypeMetadataSupport)' == ''">true</CsWinRTEnableMarshalingTypeMetadataSupport>
<CsWinRTEnableDefaultCustomPropertyProviderSupport Condition="'$(CsWinRTEnableDefaultCustomPropertyProviderSupport)' == ''">true</CsWinRTEnableDefaultCustomPropertyProviderSupport>
<CsWinRTSuppressCustomPropertyNotSupportedException Condition="'$(CsWinRTSuppressCustomPropertyNotSupportedException)' == ''">false</CsWinRTSuppressCustomPropertyNotSupportedException>
<CsWinRTEnableOpaqueRcwMarshalling Condition="'$(CsWinRTEnableOpaqueRcwMarshalling)' == ''">false</CsWinRTEnableOpaqueRcwMarshalling>

<!--
Note: the 'CsWinRTUseWindowsUIXamlProjections' property and associated 'CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS' feature
Expand Down Expand Up @@ -709,6 +710,11 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<RuntimeHostConfigurationOption Include="CSWINRT_SUPPRESS_CUSTOM_PROPERTY_NOT_SUPPORTED_EXCEPTION"
Value="$(CsWinRTSuppressCustomPropertyNotSupportedException)"
Trim="true" />

<!-- CSWINRT_ENABLE_OPAQUE_RCW_MARSHALLING switch -->
<RuntimeHostConfigurationOption Include="CSWINRT_ENABLE_OPAQUE_RCW_MARSHALLING"
Value="$(CsWinRTEnableOpaqueRcwMarshalling)"
Trim="true" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions src/Tests/FunctionalTests/NativeExposedType/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ private static int Main(string[] args)
return 102;
}

// Without a proxy, a CCW can't be created for a 'JsonObject' either, unless the opt-in opaque RCW marshalling
// feature is enabled. This project doesn't enable it, so the request must fail.
if (!IsOpaqueRcwMarshallingDisabled())
{
return 103;
}

CollectionTests.Run(rcwFirst: args is ["--rcw-first"]);

if (args is ["--xaml"])
Expand All @@ -57,6 +64,21 @@ private static int Main(string[] args)
return 100;
}

// Checks that a CCW is not created for an RCW of a projected type that is not native-exposed by default
private static unsafe bool IsOpaqueRcwMarshallingDisabled()
{
try
{
WindowsRuntimeMarshal.Free(WindowsRuntimeComWrappersMarshal.GetOrCreateComInterfaceForObject(new JsonObject(), CreateComInterfaceFlags.TrackerSupport));

return false;
}
catch (NotSupportedException)
{
return true;
}
}

// Retrieves the proxy type map used by CsWinRT to resolve CCW marshalling info for managed objects
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The proxy type map is always preserved by the interop generator.")]
private static IReadOnlyDictionary<Type, Type> GetComWrappersProxyTypeMapping()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>$(FunctionalTestsBuildTFMs)</TargetFrameworks>
<Platforms>x86;x64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<PublishProfileFullPath>$(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml</PublishProfileFullPath>
<IsTrimmable>true</IsTrimmable>
<CsWinRTEnableOpaqueRcwMarshalling>true</CsWinRTEnableOpaqueRcwMarshalling>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Authoring\WinRT.SourceGenerator2\WinRT.SourceGenerator2.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetPlatform="Platform=AnyCPU" />
<ProjectReference Include="..\..\..\Projections\Windows\Windows.csproj" />
</ItemGroup>

</Project>
Loading
Loading