From 0f6260510a8cb8ced05f66db54ba47190090f62a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Mar 2026 18:15:52 -0700 Subject: [PATCH 01/13] Define REFERENCE_ASSEMBLY in WinRT.Runtime Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> --- src/WinRT.Runtime2/WinRT.Runtime.csproj | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/WinRT.Runtime2/WinRT.Runtime.csproj b/src/WinRT.Runtime2/WinRT.Runtime.csproj index 2b734d5c1d..11062b82d8 100644 --- a/src/WinRT.Runtime2/WinRT.Runtime.csproj +++ b/src/WinRT.Runtime2/WinRT.Runtime.csproj @@ -88,6 +88,25 @@ true key.snk + + + + $(DefineConstants);REFERENCE_ASSEMBLY + + + $(NoWarn);CS8597;IDE0005 + - $(NoWarn);CS8597;IDE0005 + $(NoWarn);CS8597;IDE0005;IDE0380;CS1574;CS1587 - $(NoWarn);CS8597;IDE0005;IDE0380;CS1574;CS1587 + $(NoWarn);CS8597;IDE0005;IDE0380 $(NoWarn);AD0001 - - $(NoWarn);CSWINRT3001 - true key.snk diff --git a/src/WinRT.Runtime2/Windows.Foundation/TrustLevel.cs b/src/WinRT.Runtime2/Windows.Foundation/TrustLevel.cs index 9afc9d2be4..2377a83bb8 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/TrustLevel.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/TrustLevel.cs @@ -2,9 +2,6 @@ // Licensed under the MIT License. #if !REFERENCE_ASSEMBLY -using System; -using System.ComponentModel; -using WindowsRuntime; namespace Windows.Foundation; @@ -15,10 +12,6 @@ namespace Windows.Foundation; /// This type is required for ABI projection of Windows Runtime types, but marshalling it is not supported. /// /// -[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] -[EditorBrowsable(EditorBrowsableState.Never)] public enum TrustLevel { /// diff --git a/src/WinRT.Runtime2/Windows.UI.Xaml.Interop/TypeKind.cs b/src/WinRT.Runtime2/Windows.UI.Xaml.Interop/TypeKind.cs index f326819079..2e0ca49491 100644 --- a/src/WinRT.Runtime2/Windows.UI.Xaml.Interop/TypeKind.cs +++ b/src/WinRT.Runtime2/Windows.UI.Xaml.Interop/TypeKind.cs @@ -3,8 +3,6 @@ #if !REFERENCE_ASSEMBLY using System; -using System.ComponentModel; -using WindowsRuntime; namespace Windows.UI.Xaml.Interop; @@ -15,10 +13,6 @@ namespace Windows.UI.Xaml.Interop; /// This type is required for ABI projection of the class, but marshalling it is not supported. /// /// -[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] -[EditorBrowsable(EditorBrowsableState.Never)] public enum TypeKind { /// diff --git a/src/WinRT.Runtime2/WindowsRuntimeInspectable.cs b/src/WinRT.Runtime2/WindowsRuntimeInspectable.cs index 1b7bdb36d3..b95c35c701 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeInspectable.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeInspectable.cs @@ -3,7 +3,6 @@ #if !REFERENCE_ASSEMBLY using System; -using System.ComponentModel; using WindowsRuntime.InteropServices; namespace WindowsRuntime; @@ -30,17 +29,9 @@ public WindowsRuntimeInspectable(WindowsRuntimeObjectReference nativeObjectRefer } /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected internal override bool HasUnwrappableNativeObjectReference => true; /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected override bool IsOverridableInterface(in Guid iid) { return false; diff --git a/src/WinRT.Runtime2/WindowsRuntimeObject.cs b/src/WinRT.Runtime2/WindowsRuntimeObject.cs index 65d2f5177a..defbbb31a1 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeObject.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeObject.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -51,10 +50,6 @@ public abstract unsafe class WindowsRuntimeObject : /// /// The inner Windows Runtime object reference to wrap in the current instance. /// Thrown if is . - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected WindowsRuntimeObject(WindowsRuntimeObjectReference nativeObjectReference) { ArgumentNullException.ThrowIfNull(nativeObjectReference); @@ -75,10 +70,6 @@ protected WindowsRuntimeObject(WindowsRuntimeObjectReference nativeObjectReferen /// /// This constructor should only be used when activating sealed types (both projected and user-defined types). /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected WindowsRuntimeObject( WindowsRuntimeActivationTypes.DerivedSealed _, WindowsRuntimeObjectReference activationFactoryObjectReference, @@ -121,10 +112,6 @@ protected WindowsRuntimeObject( /// /// This constructor should only be used when activating composable types (both projected and user-defined types). /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected WindowsRuntimeObject( WindowsRuntimeActivationTypes.DerivedComposed _, WindowsRuntimeObjectReference activationFactoryObjectReference, @@ -204,10 +191,6 @@ protected WindowsRuntimeObject( /// overload should be used instead, as that is more efficient in case the default signature is sufficient. /// /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected WindowsRuntimeObject( WindowsRuntimeActivationFactoryCallback.DerivedSealed activationFactoryCallback, in Guid iid, @@ -253,10 +236,6 @@ protected WindowsRuntimeObject( /// overload should be used instead, as that is more efficient in case the default signature is sufficient. /// /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected WindowsRuntimeObject( WindowsRuntimeActivationFactoryCallback.DerivedComposed activationFactoryCallback, in Guid iid, @@ -296,10 +275,6 @@ protected WindowsRuntimeObject( /// /// This object reference should point to an IInspectable native object. /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected internal WindowsRuntimeObjectReference NativeObjectReference { get; } /// @@ -309,10 +284,6 @@ protected WindowsRuntimeObject( /// This value is in aggregation scenarios, as the instance that should be marshalled /// to native is the derived managed type for the projected class, and not the inner object for the base type. /// - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected internal virtual bool HasUnwrappableNativeObjectReference => UnreachableException.Throw(); /// @@ -365,10 +336,6 @@ ConcurrentDictionary InitializeTypeHandleCache() /// /// The interface to check. /// Whether the interface represented by is an overridable interface for the current type. - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] protected virtual bool IsOverridableInterface(in Guid iid) { return UnreachableException.Throw(); @@ -380,10 +347,6 @@ protected virtual bool IsOverridableInterface(in Guid iid) /// The type handle for the interface to retrieve the object reference for. /// The resulting object. /// Thrown if the interface specified by is not implemented. - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] public WindowsRuntimeObjectReference GetObjectReferenceForInterface(RuntimeTypeHandle interfaceType) { // Throw an exception if we couldn't resolve the interface reference @@ -412,10 +375,6 @@ static void ThrowArgumentException(RuntimeTypeHandle interfaceType) /// The type handle for the interface to retrieve the object reference for. /// The resulting object, if the interface could be retrieved. /// Whether could be retrieved successfully. - [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] public bool TryGetObjectReferenceForInterface(RuntimeTypeHandle interfaceType, [NotNullWhen(true)] out WindowsRuntimeObjectReference? interfaceReference) { return TryGetCastResult( From 605eb0e3733253c1a93e67f8107436218ebd7cd4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:00:03 -0700 Subject: [PATCH 08/13] Delete CSWINRT3001 diagnostic documentation This diagnostic is no longer applicable now that the private implementation detail [Obsolete] attributes have been removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/diagnostics/cswinrt30001.md | 34 -------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 docs/diagnostics/cswinrt30001.md diff --git a/docs/diagnostics/cswinrt30001.md b/docs/diagnostics/cswinrt30001.md deleted file mode 100644 index c9877706fe..0000000000 --- a/docs/diagnostics/cswinrt30001.md +++ /dev/null @@ -1,34 +0,0 @@ -# CsWinRT warning CSWINRT3001 - -This type or method is a private implementation detail, and it's only meant to be consumed by generated projections (produced by 'cswinrt.exe') and by generated interop code (produced by 'cswinrtinteropgen.exe'). Private implementation detail types are not considered part of the versioned API surface, and they are ignored when determining the assembly version following semantic versioning. Types might be modified or removed across any version change for 'WinRT.Runtime.dll', and using them in user code is undefined behavior and not supported. - -For instance, the following sample generates CSWINRT3001: - -```csharp -using Microsoft.UI.Xaml; -using WindowsRuntime.InteropServices; - -namespace MyProgram; - -Window window = Window.Current; - -// CSWINRT3001: 'GetObjectReferenceForInterface' is a private implementation detail API -WindowsRuntimeObjectReference objectReference = window.GetObjectReferenceForInterface(typeof(object).TypeHandle); -``` - -Using any private implementation detail API is not supported, and should be considered undefined behavior. - -## Additional resources - -`CSWINRT30001` is emitted when user code tries to reference a type that is marked as a **private implementation detail** within `WinRT.Runtime.dll` or the generated `WinRT.Interop.dll`. These private implementation detail types exist solely to support the marshalling pipeline that CsWinRT and the .NET SDK generate at build time. They are not part of the public, versioned API surface, and consuming them from application code is unsupported. - -While all of these types are public (as they are used across assembglies), they are intentionally hidden from IntelliSense and decorated with `[Obsolete]` (with `CSWINRT3001` as the diagnostic id) to warn when they are referenced. Their names often include `Impl`, `Helpers`, or other internal wording, and their diagnostic message explicitly states that they are private implementation details. During a build, `cswinrt.exe` produces projections and `cswinrtinteropgen.exe` produces `WinRT.Interop.dll`. The generated code inside these tools uses private implementation detail types to perform marshalling work. See `docs/winrt-interop-dll-spec.md` for a description of the generated interop assembly. Because the tooling controls all references to these types, their shape can change whenever needed without breaking consumers. This flexibility is what allows performance and reliability improvements across releases. - -## Recommended action - -- Remove all references to any private implementation detail types. -- Look for supported alternatives in `WinRT.Runtime.dll`, the Windows SDK projections, or your own code. -- If you are authoring source generators or tooling, never take a dependency on private implementation detail types. -- When in doubt, file an issue describing the scenario so the CsWinRT team can help identify a stable API or consider exposing a supported helper. - -Keeping private implementation detail types exclusive to generated code is what allows CsWinRT to deliver fast, safe interop while evolving rapidly. Respecting the diagnostic ensures your applications remain stable across updates (and also avoids accidentally breaking builds when updating the CsWinRT version). From b2859ba68ff11b1d4a05d6e185f66e968ca6cb8d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:05:18 -0700 Subject: [PATCH 09/13] Update instructions for reference assembly approach Update copilot-instructions.md and interop-generator SKILL.md to reflect that private implementation detail types are now excluded from the reference assembly via #if !REFERENCE_ASSEMBLY, rather than being marked with [Obsolete] and [EditorBrowsable(Never)] attributes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 4 ++-- .github/skills/interop-generator/SKILL.md | 2 +- .github/skills/update-copilot-instructions/SKILL.md | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c1ed1cec76..6780aef125 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -167,6 +167,7 @@ The runtime library (`WinRT.Runtime.dll`) provides all common infrastructure for - **Warnings as errors**: release only. `EnforceCodeStyleInBuild` enabled, `AnalysisLevelStyle` = `latest-all`. - **Strong-name signed** with `key.snk` - **AOT compatible**: `IsAotCompatible = true` +- **Reference assembly**: the project is built twice for NuGet packaging. With `CsWinRTBuildReferenceAssembly=true`, it produces a reference assembly (for `ref\net10.0\` in the NuGet) that strips all private implementation detail types and members via `#if !REFERENCE_ASSEMBLY`. The normal build produces the full implementation assembly (for `lib\net10.0\`). This replaces the previous approach of marking implementation details with `[Obsolete]` and `[EditorBrowsable(Never)]` attributes. **Directory structure:** @@ -506,7 +507,7 @@ The MSBuild integration is orchestrated through several `.props` and `.targets` - **Compiler strict mode**: `strict` in all projects - **XML documentation**: generated for all projects - **`SkipLocalsInit`**: enabled in runtime and build tools for performance -- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes), `CSWINRT3001` (obsolete internal members) +- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes) - **Strong-name signing**: all assemblies signed with `src/WinRT.Runtime2/key.snk` ### C++ project (cswinrt) @@ -548,7 +549,6 @@ All three .NET build tools (`cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtin | Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0010`, `9999` | | Projection Generator | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` | | Interop Generator | `CSWINRTINTEROPGENxxxx` | Various, `9999` | -| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` | --- diff --git a/.github/skills/interop-generator/SKILL.md b/.github/skills/interop-generator/SKILL.md index 22a4ec56c9..25637701d2 100644 --- a/.github/skills/interop-generator/SKILL.md +++ b/.github/skills/interop-generator/SKILL.md @@ -32,7 +32,7 @@ The `WinRT.Interop.dll` assembly is produced by `cswinrtinteropgen`, which is a - If a project **does not** reference CsWinRT, then `cswinrtinteropgen` is loaded from the Windows SDK projections targeting pack. - If CsWinRT **is** referenced (directly or transitively), then `cswinrtinteropgen` is loaded from that package, but only if the `WinRT.Runtime.dll` binary from that package has a higher version than the one in the Windows SDK projections package being referenced. This correctly handles cases where a dependent project might have a reference to an outdated CsWinRT package. -This version matching is critical because `cswinrtinteropgen` relies on "implementation details only" APIs in `WinRT.Runtime.dll` — APIs which are public, hidden, and marked as `[Obsolete]`, and which are exclusively meant to be consumed by generated code produced by `cswinrtinteropgen`. These APIs might change at any time without following semantic versioning for CsWinRT. For instance, they are crucial to support marshalling generic Windows Runtime collection interfaces, and the code in `WinRT.Interop.dll` makes heavy use of them in this scenario. +This version matching is critical because `cswinrtinteropgen` relies on internal implementation detail APIs in `WinRT.Runtime.dll` — APIs which are public in the implementation assembly but excluded from the reference assembly (via `#if !REFERENCE_ASSEMBLY`), and which are exclusively meant to be consumed by generated code produced by `cswinrtinteropgen`. These APIs might change at any time without following semantic versioning for CsWinRT. For instance, they are crucial to support marshalling generic Windows Runtime collection interfaces, and the code in `WinRT.Interop.dll` makes heavy use of them in this scenario. ## Project settings diff --git a/.github/skills/update-copilot-instructions/SKILL.md b/.github/skills/update-copilot-instructions/SKILL.md index 210142edd0..86254ee632 100644 --- a/.github/skills/update-copilot-instructions/SKILL.md +++ b/.github/skills/update-copilot-instructions/SKILL.md @@ -26,6 +26,7 @@ Launch parallel explore agents for each of the 8 CsWinRT 3.0 projects listed in - Key types listed still exist and have the described purposes - T4 templates (`.tt` files) are accurately listed - Project settings (TFM, language version, nullable, unsafe, etc.) are current + - Reference assembly build setup (`CsWinRTBuildReferenceAssembly`, `#if !REFERENCE_ASSEMBLY`) is accurately described - Namespace organization matches 2. **WinRT.SourceGenerator2 (`src/Authoring/WinRT.SourceGenerator2/`)** From 907baddc418344d10a0645c9ab54b3f463b6ec5e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:08:37 -0700 Subject: [PATCH 10/13] Document private implementation detail APIs in interop generator skill Expand the version compatibility section to explain how the interop generator uses WinRT.Runtime's implementation assembly (not the reference assembly) to access private implementation detail APIs, with concrete examples of the type categories involved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/skills/interop-generator/SKILL.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/skills/interop-generator/SKILL.md b/.github/skills/interop-generator/SKILL.md index 25637701d2..bb9bb90365 100644 --- a/.github/skills/interop-generator/SKILL.md +++ b/.github/skills/interop-generator/SKILL.md @@ -34,6 +34,23 @@ The `WinRT.Interop.dll` assembly is produced by `cswinrtinteropgen`, which is a This version matching is critical because `cswinrtinteropgen` relies on internal implementation detail APIs in `WinRT.Runtime.dll` — APIs which are public in the implementation assembly but excluded from the reference assembly (via `#if !REFERENCE_ASSEMBLY`), and which are exclusively meant to be consumed by generated code produced by `cswinrtinteropgen`. These APIs might change at any time without following semantic versioning for CsWinRT. For instance, they are crucial to support marshalling generic Windows Runtime collection interfaces, and the code in `WinRT.Interop.dll` makes heavy use of them in this scenario. +### Private implementation detail APIs + +`WinRT.Runtime.dll` is shipped as two assemblies in the CsWinRT NuGet package: a **reference assembly** (in `ref\net10.0\`) that exposes only the versioned public API surface, and an **implementation assembly** (in `lib\net10.0\`) that contains the full set of types and members. Many types in the implementation assembly are "public" only because generated code in other assemblies (produced by `cswinrt.exe` and `cswinrtinteropgen`) needs to call them — they are not part of the stable API contract. These types are stripped from the reference assembly via `#if !REFERENCE_ASSEMBLY`, so consumers never see them. + +The interop generator receives a reference to the **implementation** `WinRT.Runtime.dll` (not the reference assembly) so that it can resolve and emit calls to these internal APIs. Examples of private implementation detail types that the interop generator uses heavily include: + +- **Vtable structs** (`IVectorVftbl`, `IMapVftbl`, `IIterableVftbl`, etc.) — the interop generator emits static readonly vtable fields for each projected interface, populated with function pointers to marshalling stubs. +- **Collection adapters and methods** (`IListAdapter`, `IDictionaryMethods`, `IEnumerableMethods`, etc.) — generated native object wrappers delegate collection operations to these adapter types. +- **Marshalling infrastructure** (`WindowsRuntimeObjectMarshaller`, `HStringMarshaller`, `WindowsRuntimeInterfaceMarshaller`, array marshallers, etc.) — generated marshalling stubs call these to convert between managed and native representations. +- **Object reference types** (`WindowsRuntimeObjectReference`, `WindowsRuntimeObjectReferenceValue`, `CreateObjectReferenceMarshalingType`) — generated code uses these for COM pointer lifecycle management. +- **ComWrappers callbacks** (`IWindowsRuntimeObjectComWrappersCallback`, `WindowsRuntimeComWrappersMarshal`) — generated CCW vtable entries and type map registrations use these. +- **Type map groups** (`WindowsRuntimeComWrappersTypeMapGroup`, `WindowsRuntimeMetadataTypeMapGroup`, `DynamicInterfaceCastableImplementationTypeMapGroup`) — generated type map registrations target these groups. +- **Projection implementations** (`IPropertyValueImpl`, `IStringableImpl`, `IMarshalImpl`, etc.) — generated code wires up these implementations in CCW vtables. +- **ABI types** (under `ABI.*` namespaces) — per-type marshalling definitions and event source types. + +All of these types are referenced extensively throughout the interop generator's builder and factory classes (in `Builders/` and `Factories/`). When modifying these types in `WinRT.Runtime`, the corresponding interop generator code must be updated in lockstep — which is why the version matching described above is essential. + ## Project settings - **Target**: `net10.0`, C# 14, `AllowUnsafeBlocks` From 5623ab9c8db7ca626a86788c41aa2e893fef9644 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:13:23 -0700 Subject: [PATCH 11/13] Remove extra blank lines before #endif in WindowsRuntimeObject.cs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/WinRT.Runtime2/WindowsRuntimeObject.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/WinRT.Runtime2/WindowsRuntimeObject.cs b/src/WinRT.Runtime2/WindowsRuntimeObject.cs index defbbb31a1..0cc038a0fe 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeObject.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeObject.cs @@ -486,7 +486,6 @@ internal bool TryGetObjectReferenceForIEnumerableInterfaceInstance([NotNullWhen( return false; } } - #endif /// @@ -919,7 +918,6 @@ private sealed class DynamicInterfaceCastableResult /// A dummy type to use for caching adaptive object references in . /// private static class IEnumerableInstance; - #endif } From bd1b696ce00b51fc2914c6f00155a77881095bda Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:16:06 -0700 Subject: [PATCH 12/13] Restore abstract members on WindowsRuntimeObject Revert HasUnwrappableNativeObjectReference and IsOverridableInterface back to abstract (from virtual with UnreachableException.Throw() body). These members are now inside #if !REFERENCE_ASSEMBLY, so the workaround from PR #2371 is no longer needed. Also remove the UnreachableException extension method that was added solely to support those virtual stubs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../WindowsRuntimeExceptionExtensions.cs | 15 --------------- src/WinRT.Runtime2/WindowsRuntimeObject.cs | 7 ++----- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs b/src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs index 56169cecdf..8374eccb8e 100644 --- a/src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs +++ b/src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs @@ -1481,19 +1481,4 @@ public static ArgumentNullException GetIOCompletionCallbackCannotProcessNullAsyn return new(paramName, WindowsRuntimeExceptionMessages.ArgumentNullReference_IOCompletionCallbackCannotProcessNullAsyncInfo); } } - - extension(UnreachableException) - { - /// - /// Throws an . - /// - /// This method never returns. - /// Always thrown. - [DoesNotReturn] - [StackTraceHidden] - public static bool Throw() - { - throw new UnreachableException(); - } - } } \ No newline at end of file diff --git a/src/WinRT.Runtime2/WindowsRuntimeObject.cs b/src/WinRT.Runtime2/WindowsRuntimeObject.cs index 0cc038a0fe..b0cbcae1a7 100644 --- a/src/WinRT.Runtime2/WindowsRuntimeObject.cs +++ b/src/WinRT.Runtime2/WindowsRuntimeObject.cs @@ -284,7 +284,7 @@ protected WindowsRuntimeObject( /// This value is in aggregation scenarios, as the instance that should be marshalled /// to native is the derived managed type for the projected class, and not the inner object for the base type. /// - protected internal virtual bool HasUnwrappableNativeObjectReference => UnreachableException.Throw(); + protected internal abstract bool HasUnwrappableNativeObjectReference { get; } /// /// Gets the lazy-loaded, cached object reference for IInspectable for the current object. @@ -336,10 +336,7 @@ ConcurrentDictionary InitializeTypeHandleCache() /// /// The interface to check. /// Whether the interface represented by is an overridable interface for the current type. - protected virtual bool IsOverridableInterface(in Guid iid) - { - return UnreachableException.Throw(); - } + protected abstract bool IsOverridableInterface(in Guid iid); /// /// Retrieves a object for the specified interface. From 2f20b23740048e4114bd4656e406f330cde29db9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Mar 2026 20:52:19 -0700 Subject: [PATCH 13/13] Disable ProduceReferenceAssembly for normal WinRT.Runtime builds WinRT.Runtime provides its own custom reference assembly (built with CsWinRTBuildReferenceAssembly=true) that strips private implementation detail types. Disable the SDK's default ProduceReferenceAssembly so that downstream ProjectReference consumers compile against the implementation assembly directly, where abstract members like HasUnwrappableNativeObject- Reference and IsOverridableInterface are visible and can be overridden. NuGet consumers continue to get the custom ref assembly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/WinRT.Runtime2/WinRT.Runtime.csproj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/WinRT.Runtime2/WinRT.Runtime.csproj b/src/WinRT.Runtime2/WinRT.Runtime.csproj index e332989fa8..18ba7a1b0d 100644 --- a/src/WinRT.Runtime2/WinRT.Runtime.csproj +++ b/src/WinRT.Runtime2/WinRT.Runtime.csproj @@ -86,6 +86,17 @@ key.snk + + + false + +