Skip to content

Build and ship a reference assembly for WinRT.Runtime#2453

Open
Sergio0694 wants to merge 9 commits into
staging/3.0from
staging/winrt-runtime-ref-assembly
Open

Build and ship a reference assembly for WinRT.Runtime#2453
Sergio0694 wants to merge 9 commits into
staging/3.0from
staging/winrt-runtime-ref-assembly

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Introduce a dedicated reference assembly for WinRT.Runtime, built alongside the implementation assembly with every implementation-only type and member stripped out, and ship it in the Microsoft.Windows.CsWinRT NuGet package under ref\net10.0\. A new [WindowsRuntimeImplementationOnlyMember] attribute and a banned-API analyzer make the implementation-only surface explicit and prevent it from ever leaking into the public API.

Motivation

WinRT.Runtime exposes a large set of public-but-private-implementation-detail APIs (marshallers, vtables, native object wrappers, collection adapters, ABI types, and so on) that exist solely to be consumed by generated code: the CsWinRT projections and the interop generator (WinRT.Interop.dll). Previously these were marked [Obsolete] + [EditorBrowsable(Never)] to hide them from IntelliSense, but they were still part of the public surface that consumers compiled against. That cluttered the API, risked accidental use, and meant the implementation-only surface was effectively versioned like public API.

Building a separate reference assembly that strips these APIs out entirely gives consumers a clean public surface to compile against, lets the implementation-only APIs evolve freely without semantic-versioning concerns, and makes the rules enforceable: deriving from WindowsRuntimeObject is now blocked at compile time (CSWINRT3001) because its constructor exists only in the reference assembly, and the banned-API analyzer fails the build if any implementation-only type ever leaks into the reference surface. This is the runtime-side foundation that complements the reference-projection model used by the rest of CsWinRT 3.0.

Changes

  • [WindowsRuntimeImplementationOnlyMember] attribute (Use a dedicated attribute to mark implementation-only members #2434) — new internal sealed, [Conditional("WINDOWS_RUNTIME_REFERENCE_ASSEMBLY")] marker in src/WinRT.Runtime2/Attributes/ that replaces the previous [Obsolete] + [EditorBrowsable(Never)] combination used to mark private implementation-detail types and members. Applied throughout ABI/, InteropServices/, NativeObjects/, Windows.Foundation/, and the marker attributes under Attributes/. Removes the old WindowsRuntimeConstants private-implementation-detail messaging and the corresponding warning suppression.
  • WindowsRuntimeObject partial split (Split WindowsRuntimeObject into a partial implementation file #2446) — splits the base type into WindowsRuntimeObject.cs (shared/reference surface, including the explicit interface implementations as throw null stubs) and WindowsRuntimeObject.Impl.cs (implementation-only half), so the implementation can be excluded from the reference assembly.
  • Dual implementation/reference assembly build (Set up dual reference assembly build for WinRT.Runtime #2444) — src/WinRT.Runtime2/WinRT.Runtime.csproj now builds twice: the implementation assembly by default and a stripped reference assembly when CsWinRTBuildReferenceAssembly=true. Adds the WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY / WINDOWS_RUNTIME_REFERENCE_ASSEMBLY compilation symbols, sets ProduceReferenceAssembly=false on the implementation build, and adds a RemoveWindowsRuntimeImplementationOnlyFiles target that excludes implementation-only sources (files defining WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE, plus whole folders such as ABI/, NativeObjects/, and most of InteropServices/) from the reference build.
  • Banned-API analyzer guard (Add banned API analyzer to guard the reference assembly surface #2447) — the reference build references Microsoft.CodeAnalysis.BannedApiAnalyzers (src/Directory.Packages.props) and lists WindowsRuntimeImplementationOnlyMemberAttribute in src/WinRT.Runtime2/BannedSymbols.txt with RS0030 promoted to an error, so the build fails if any implementation-only type ever leaks into the reference surface.
  • Reference-assembly-only constructor and CSWINRT3001 (Add a reference-assembly-only constructor for WindowsRuntimeObject #2448) — a parameterless protected WindowsRuntimeObject() constructor exists only in the reference assembly, marked [Obsolete(..., DiagnosticId = "CSWINRT3001")]. User code that derives from WindowsRuntimeObject now gets the CSWINRT3001 warning and, because the constructor is absent from the implementation assembly, a MissingMethodException at runtime. Only CsWinRT-generated projections may derive from it. Adds docs/diagnostics/cswinrt3001.md.
  • Packaging (Package the WinRT.Runtime reference assembly #2451) — the Microsoft.Windows.CsWinRT NuGet package now ships the implementation assembly under lib\net10.0\ and the reference assembly (with its XML documentation trimmed to the reference surface) under ref\net10.0\. The dual build and staging are wired through nuget/Microsoft.Windows.CsWinRT.nuspec, src/build.cmd, and the Azure Pipelines build/publish steps.
  • Documentation (Document the WinRT.Runtime reference assembly and impl-only APIs #2452) — updates the Copilot instructions (.github/copilot-instructions.md) and the interop generator skill docs (.github/skills/...) to describe the reference assembly, the [WindowsRuntimeImplementationOnlyMember] attribute, the CSWINRT3001 diagnostic, and the implementation-only API surface consumed by the interop generator.

Sergio0694 and others added 9 commits June 14, 2026 11:35
* Add WindowsRuntimeImplementationOnlyMemberAttribute

Introduce an internal sealed attribute to mark implementation-only members for WinRT runtime. The attribute targets all members, is non-inherited, and is conditional on WINDOWS_RUNTIME_REFERENCE_ASSEMBLY so it is emitted only in reference assemblies; this makes it explicit which members are implementation-only and allows them to be stripped from the runtime DLL. Added at src/WinRT.Runtime2/Attributes/WindowsRuntimeImplementationOnlyMemberAttribute.cs.

* Use [WindowsRuntimeImplementationOnlyMember] for implementation-only members

Replace the combination of [EditorBrowsable(Never)] and [Obsolete(...)] with the single [WindowsRuntimeImplementationOnlyMember] attribute on implementation-only members across WinRT.Runtime.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove WindowsRuntimeConstants.cs

Delete WindowsRuntimeConstants.cs which contained constants for WinRT private implementation detail messaging, the PrivateImplementationDetailObsoleteDiagnosticId (CSWINRT3001), and the CsWinRT diagnostics URL format. These constants have been removed from the project.

* Remove CSWINRT3001 NoWarn suppression

Delete the NoWarn entry and its comment for CSWINRT3001 from src/WinRT.Runtime2/WinRT.Runtime.csproj so warnings about '[Obsolete]' private implementation details are no longer suppressed. This change surfaces those warnings for visibility during builds.

* 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>

* Use [WindowsRuntimeImplementationOnlyMember] in IActivationFactoryImpl

This file was missed in the bulk conversion to the new attribute; it still referenced the now-deleted WindowsRuntimeConstants type, which would break the build.

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

* Remove CSWINRT3001 warning suppressions

Remove pragma suppressions and documentation references for the CSWINRT3001 diagnostic. Deleted several "#pragma warning disable CSWINRT3001" (and matching restores) from source files and code-generation templates, and removed CSWINRT3001 from the suppressed warnings list and obsolete-marker table in docs. Affected files include .github/copilot-instructions.md, docs/interop.md, various tests and shim sources, and cswinrt code/template headers.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the bulk of WindowsRuntimeObject's implementation into a new partial file (WindowsRuntimeObject.Impl.cs) and update WindowsRuntimeObject.cs to be a partial declaration. The change relocates constructors, caching fields, dynamic cast/QueryInterface logic, virtual method table helpers, and exception stubs into the new file while removing now-unneeded usings from the original file. This refactor preserves existing behavior but improves code organization and maintainability by separating surface declaration from detailed implementation.
* Adjust reference assembly build for WinRT.Runtime

Prevent the SDK-produced reference assembly from leaking implementation-only types by customizing the ref-assembly build. When not building the explicit CsWinRT reference assembly, ProduceReferenceAssembly is disabled and WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY is defined to simplify #if usage. When CsWinRTBuildReferenceAssembly is true, WINDOWS_RUNTIME_REFERENCE_ASSEMBLY is defined and expected warnings (CS8597, IDE0005, IDE0380) are suppressed. A BeforeTargets CoreCompile target removes files that opt out of reference assemblies (via WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE), ABI sources, and several implementation-only folders so the packaged reference assembly omits private implementation details.

* Guard Windows metadata by assembly type

Wrap Windows runtime metadata and platform/contract attributes with build-time symbols to differentiate reference vs implementation assemblies. Many files now conditionally include Windows.Foundation.Metadata usings and apply [ContractVersion]/[SupportedOSPlatform] for WINDOWS_RUNTIME_REFERENCE_ASSEMBLY and [WindowsRuntimeMetadata]/[WindowsRuntimeClassName]/marshalling attributes for WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY. Removed unconditional SupportedOSPlatform/usings where appropriate and added #if guards across foundation, collections, streams, async adapters and task/asyncinfo helpers to allow building both reference and implementation variants.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

* Disable CS1574 in WindowsRuntimeFeatureSwitches

Suppress CS1574 (XML doc 'cref' not found) warnings in WindowsRuntimeFeatureSwitches.cs by adding `#pragma warning disable CS1574` before the namespace declaration. This prevents spurious documentation warnings during the build, likely caused by cref references to internal or conditional types.

* Move WindowsRuntimeInspectable to NativeObjects

Rename/move WindowsRuntimeInspectable.cs from src/WinRT.Runtime2/ to src/WinRT.Runtime2/NativeObjects/ to reorganize project structure. File content unchanged (100% similarity).

* Split WindowsRuntimeObject into partial file

Move the core implementation into a new partial file (src/WinRT.Runtime2/WindowsRuntimeObject.Impl.cs) and make WindowsRuntimeObject partial. The impl file contains caching, dynamic cast lookup, generated COM vtable handling, QueryInterface logic, activation constructors and an exceptions helper; redundant usings/implementation were removed from WindowsRuntimeObject.cs to keep the public surface unchanged.

* Add build guards for reference vs implementation

Introduce compile-time guards to separate reference and implementation builds. Many files were marked as implementation-only by adding a WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE define; APIs and method bodies were wrapped with WINDOWS_RUNTIME_REFERENCE_ASSEMBLY / WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY conditionals (throw null in reference builds, real code in implementation builds). Adjustments touch attributes, interop/activation, marshalling, async helpers, buffer/stream utilities, and WindowsRuntimeMarshal/RestrictedErrorInfo/RestrictedErrorInfoExceptionMarshaller. Also updated the project file to exclude the Marshalling folder from the reference build. This enables producing lightweight reference assemblies while keeping full implementations in the implementation assembly.

* Use explicit #elif WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY in WinRT.Runtime

Replace the plain #else branch following #if WINDOWS_RUNTIME_REFERENCE_ASSEMBLY
with an explicit #elif WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY for clarity. The
two constants are mutually exclusive and exhaustive, so the behavior is
unchanged.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add banned API analyzer and ref-assembly config

Add Microsoft.CodeAnalysis.BannedApiAnalyzers to central package versions and include it as a private analyzer when building the reference assembly. Add a BannedSymbols.txt listing the WindowsRuntimeImplementationOnlyMemberAttribute to prevent implementation-only types from being exposed in the published ref assembly. Rearrange and annotate the WinRT.Runtime.csproj property groups for implementation vs reference assembly builds, set RS0030 as an error to fail on leaked private APIs, and wire BannedSymbols.txt as an AdditionalFiles input for the analyzer.

* Mark implementation-only sources; adjust csproj

Add #define WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE to several implementation-only sources (Activation/WindowsRuntimeActivationArgsReference.cs, WindowsRuntimeActivationFactoryCallback.cs, WindowsRuntimeActivationTypes.cs, Windows.Foundation/TrustLevel.cs) so they can be excluded from reference-assembly builds. Tidy WinRT.Runtime.csproj Compile Remove entries (self-closing tags / spacing) and add exclusion for Windows.UI.Xaml.Interop to ensure implementation-only folders are removed from reference builds.
…2448)

* Bring back WindowsRuntimeConstants for the WindowsRuntimeObject constructor

Re-add the type removed in #2434, now holding the dedicated obsolete message, diagnostic id (CSWINRT3001) and URL format used to flag the reference-assembly-only WindowsRuntimeObject constructor.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

* Add a reference-assembly-only constructor for WindowsRuntimeObject

After #2446 all WindowsRuntimeObject constructors live in the implementation-only WindowsRuntimeObject.Impl.cs and are stripped from the reference assembly, so the compiler would synthesize a protected parameterless constructor. That constructor is absent from the implementation assembly and would throw MissingMethodException at runtime if a user type derived from WindowsRuntimeObject.

Explicitly define it for the reference assembly only, marked obsolete (CSWINRT3001) and hidden, so deriving from WindowsRuntimeObject produces a build diagnostic instead. Only generated projections may derive from it.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

* Add back the CSWINRT3001 diagnostics doc

Re-add the diagnostics doc removed in #2434, updated for the new scenario (deriving from WindowsRuntimeObject is not supported), and rename it to match the diagnostic id (updating the solution reference, which was previously dangling).

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the dual reference-assembly build for WinRT.Runtime (set up via the
'CsWinRTBuildReferenceAssembly' switch) functional end to end, so the NuGet
package ships a proper reference assembly under 'ref\net10.0'.

The reference build recompiles WinRT.Runtime with all implementation-only
types stripped out, producing a lightweight metadata-only assembly along with
an XML doc that is likewise stripped to the public reference surface. NuGet
binds the compiler to the 'ref' assembly (and the runtime to the 'lib'
assembly), and the compiler resolves XML docs next to the compile-time
assembly, so the stripped XML is shipped next to the reference assembly in
'ref\net10.0' (IntelliSense then shows only the public surface). The 'lib'
folder ships only the implementation assembly; its XML doc is not packaged,
since it would never be surfaced and only documents implementation details.

The reference build is run as AnyCPU, matching how the solution builds the
implementation assembly, so its outputs land in the platform-less
'bin\<config>\net10.0' and 'obj\<config>\net10.0\ref' folders. Because the
reference build overwrites the 'bin' outputs, the implementation assembly is
staged out before it runs, and both are packaged from the staging folder.

- nuget: ship 'ref\net10.0\WinRT.Runtime.dll' and its XML (lib has dll only)
- build.cmd: stage the impl dll, build the reference assembly, stage ref dll + xml
- CsWinRT-Build-Steps.yml: build and stage the reference assembly and its XML
- CsWinRT-PublishToNuGet-Steps.yml: add the 'net10_runtime_ref(_xml)' props

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add docs for reference assembly and impl-only API

Document the WinRT.Runtime dual-build (implementation + stripped reference assembly) and the implementation-only API workflow. Updates include: explaining CsWinRTBuildReferenceAssembly and compilation symbols (WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY / WINDOWS_RUNTIME_REFERENCE_ASSEMBLY / WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE), ProduceReferenceAssembly=false behavior, the WindowsRuntimeImplementationOnlyMember attribute, BannedSymbols.txt + Microsoft.CodeAnalysis.BannedApiAnalyzers guard, reference-only WindowsRuntimeObject() constructor and CSWINRT3001 diagnostic, and packaging into ref\net10.0 alongside lib\net10.0. Also update interop-generator SKILL and reference docs to note that generator-consumed runtime APIs are marked as implementation-only and stripped from the reference assembly to ensure version compatibility.

* Add docs for implementation-only WinRT.Runtime types

Add a new "Implementation-only types consumed from WinRT.Runtime" section to .github/skills/interop-generator/SKILL.md describing the categories of implementation-only APIs the generator calls (ABI marshallers, scalar/object/delegate marshallers, array marshallers, native object wrappers, interface method implementations, collection adapters, ComWrappers/object-reference types, event sources, type-map groups/attributes, IID table/error helpers). Explain that these APIs live only in the WinRT.Runtime implementation assembly, that generated code emits [IgnoresAccessChecksTo] to reach them, and note the need to version-match cswinrtinteropgen to the targeted WinRT.Runtime. Also update .github/skills/update-interop-generator-instructions/SKILL.md to add a verification step to ensure the implementation-only types inventory is current and reconciled with References/InteropReferences.cs and the WinRT.Runtime implementation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant