Skip to content

Commit 45f017b

Browse files
Sergio0694Copilot
andauthored
Build and ship a reference assembly for WinRT.Runtime (#2453)
* Use a dedicated attribute to mark implementation-only members (#2434) * 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> * Move WindowsRuntimeObject implementation to partial file (#2446) 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. * Set up dual reference assembly build for WinRT.Runtime (#2444) * 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 to guard the reference assembly surface (#2447) * 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. * Add a reference-assembly-only constructor for WindowsRuntimeObject (#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> * Package the WinRT.Runtime reference assembly (#2451) 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> * Document the WinRT.Runtime reference assembly and impl-only APIs (#2452) * 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. * Add end-to-end smoke tests for the CsWinRT NuGet package (#2456) * Add isolated infrastructure for end-to-end smoke tests Adds blank Directory.Build.props/.targets and a Directory.Packages.props that disables central package management, so the smoke tests can consume the real CsWinRT NuGet package in full isolation from the repository build infrastructure. A .gitignore keeps their default bin/obj output out of source control. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add consumption end-to-end smoke test A .NET app that parses a JSON object from Windows.Data.Json and round-trips it via Stringify, exercising the Windows SDK projection, the interop generator, and the WinRT.Runtime ref/impl assemblies against the real NuGet package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add authoring end-to-end smoke test A Windows Runtime component library exposing a minimal class, exercising WinMD generation, the reference projection, and the forwarder assembly against the real NuGet package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Build and run smoke tests in local and CI builds Adds a runner that builds and runs the consumption test and builds the authoring test against a given package source and version, and wires it into build.cmd (after the pack step, on x64) and the PublishToNuGet CI steps (after packing). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Verify the authoring smoke test produces a .winmd defining the expected type After building the component, the runner now locates the generated Authoring.winmd and asserts it is a Windows Runtime metadata file that defines the Authoring.Greeter type, using a dependency-free inspection that works in any PowerShell host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Trim comments and whitespace in smoke tests Remove explanatory comments and extra blank lines from SmokeTests projects and source files to reduce noise. Changes touch Authoring.csproj, Greeter.cs, Consumption.csproj, Program.cs, Directory.Build.props, Directory.Build.targets, and Directory.Packages.props. No functional code changes — only formatting and comment cleanup to keep the smoke test files minimal. * Centralize shared smoke test configuration in Directory.Build.props Moves the target framework, Windows SDK ref pack pin, NuGet restore sources, CsWinRT package version/source, and the CsWinRT package reference (all previously duplicated in both smoke test projects) into the shared SmokeTests Directory.Build.props. Each project now only declares what makes it different: the consumption app's output type and the authoring library's component flag. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Document the smoke tests and refresh test documentation Updates the testing skill and docs/structure.md to account for the new SmokeTests projects, and fixes other outdated test documentation: expands the SourceGenerator2Test analyzer test list (now CSWINRT2009-2017), corrects the AuthoringTest status and TFM, fixes the TestComponentCSharp IDL filename and the cswinrt.slnx reference, and lists the missing test projects in the repository structure doc. Also adds a smoke-tests verification step to the update-testing-instructions skill. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use the CsWinRTDependencies feed for smoke test restore CI agents cannot reach public NuGet (api.nuget.org), which broke the smoke test restore. Restore the preview Windows SDK ref pack from the repository's standard CsWinRTDependencies feed instead (the same feed the rest of the repo uses, e.g. src/WinRT.Internal), keeping the local CsWinRT build output as the source for the package under test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Keep type map group types in the WinRT.Runtime reference assembly The type map group types (WindowsRuntimeComWrappersTypeMapGroup, WindowsRuntimeMetadataTypeMapGroup, and DynamicInterfaceCastableImplementationTypeMapGroup) were stripped from the reference assembly as implementation-only types. However, they are referenced by code the CsWinRT source generator emits into consumer assemblies (the '[assembly: TypeMapAssemblyTarget<T>]' attributes that register assemblies for the interop type map infrastructure), which is compiled against the reference assembly. Stripping them broke consumers with CS0234. Keep these three types in the reference assembly, and instead mark them '[Obsolete]' (with the new CSWINRT3002 diagnostic) and '[EditorBrowsable(Never)]', only in the reference assembly, to discourage direct use in user code. This mirrors the reference-assembly-only obsolete pattern already used for the parameterless 'WindowsRuntimeObject' constructor (CSWINRT3001). The marker is reference-assembly-only because the implementation assembly has internal '[assembly: TypeMap<T>]' usages that would otherwise trip the obsolete warning. The projection writer's generated-file header now also suppresses CSWINRT3002, since the generated projection code references these types. Adds the docs/diagnostics/cswinrt3002.md page. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Run each smoke test as its own CI step with a final failure gate Previously all smoke tests ran in a single CI step, so a failure didn't make it obvious which one failed. Following the unit test pattern in CsWinRT-Test-Steps.yml, each smoke test now runs as its own step with 'continueOnError' (an individual failure only marks the job as 'SucceededWithIssues'), and a final gate step turns any such issue into an actual failure. This makes it clear exactly which smoke test failed while still letting every test run and report. The runner script gains a '-Test' parameter (Consumption, Authoring, or All) so CI can invoke a single test per step; local builds keep using the default 'All'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Compile generated projections against the WinRT.Runtime implementation assembly The NuGet package ships WinRT.Runtime as both a reference assembly (ref/net10.0, with implementation-only types stripped) and an implementation assembly (lib/net10.0). The projection and interop generators compile and inspect generated implementation projection code that references those implementation-only types (e.g. WindowsRuntimeObjectReferenceValue, RestrictedErrorInfo, the marshallers), so they must use the implementation assembly rather than the reference assembly that MSBuild resolves for compilation. Introduce a CsWinRTRuntimeImplementationAssemblyPath property (defaulting to the package lib layout) and swap WinRT.Runtime for it in each affected generator target. Repo-internal builds override the property to point at the locally built implementation assembly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make the WinRT.Runtime reference assembly swap an explicit opt-out The prior commit made the projection and interop generators unconditionally swap the WinRT.Runtime reference assembly for an implementation assembly, and overrode CsWinRTRuntimeImplementationAssemblyPath in src/Directory.Build.props to point at src/WinRT.Runtime2/bin/<config>/net10.0/WinRT.Runtime.dll. The official build rebuilds that bin output as the stripped reference assembly (CsWinRTBuildReferenceAssembly=true), so the override pointed the generators at the stripped assembly and broke repo builds. The package always lays out both the reference assembly (ref/net10.0) and the implementation assembly (lib/net10.0), so the generators can simply assume the implementation assembly is present and swap to it; if it were ever missing, the smoke tests (and any real consumer) would fail loudly. The swap is now controlled by CsWinRTSwapRuntimeReferenceAssembly (default true). This repo references WinRT.Runtime directly as its implementation assembly, so it opts out via src/Directory.Build.props. The fragile bin-pointing override is removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Expose the authoring component assembly attributes in the reference assembly (CSWINRT3003) The authoring smoke test failed to build because the CsWinRT source generator emits '[assembly: WindowsRuntimeComponentAssembly]' and '[assembly: WindowsRuntimeComponentAssemblyExportsType(...)]' into the component (in 'ManagedExports.g.cs'), but both attributes were stripped from the 'WinRT.Runtime' reference assembly (they were marked as implementation-only files), so the component compilation could not resolve them. Keep both attributes in the reference assembly and mark them, only there, with a reference-assembly-only '[Obsolete]' (new diagnostic CSWINRT3003) plus '[EditorBrowsable(Never)]', exactly like the type map group types (CSWINRT3002). The generated code that applies them already suppresses warnings, so normal builds are unaffected, while direct use in user code is discouraged. Add a 'docs/diagnostics/cswinrt3003.md' page describing the diagnostic. Also fix a related leak: the 'ReferenceVftbl' struct in the projection writer's 'InspectableVftbl' baseline declared its 'GetTrustLevel' slot as 'TrustLevel*', referencing the implementation-only 'Windows.Foundation.TrustLevel' enum. The slot is layout-only (the vtable is populated by copying 'IInspectableImpl.Vtable' and only 'get_Value' is set), so type it as 'int*' to match 'IInspectableVftbl.GetTrustLevel' and keep 'TrustLevel' out of the generated component projection. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Move vtable types to runtime and add assembly attributes Relocate and consolidate vtable and assembly metadata: removed the duplicate InspectableVftbl resource and added a shared AssemblyAttributes.cs. Updated ReferenceImplFactory to reference IReferenceVftbl. Adjusted IInspectableVftbl, IReferenceVftbl and IUnknownVftbl visibility and API surface (made structs public, marked with WindowsRuntimeImplementationOnlyMember, tightened helper methods to internal, and added pragma disables). These changes centralize vtable definitions in the runtime project and unify assembly-level attributes (DisableRuntimeMarshalling / trimmable/AOT metadata) for projection builds. * Remove unsupported-marshalling remarks from enums Drop redundant XML <remarks> comments that stated marshalling was not supported for TrustLevel and TypeKind. Clean up TrustLevel.cs and TypeKind.cs by removing the two-line remarks blocks so the enum docs are concise. No behavior changes. * Skip the reference projection generator for input-less authored components When a project is an authored Windows Runtime component ('CsWinRTComponent=true') that does not reference any input .winmd files, there are no Windows Runtime types for 'cswinrtprojectionrefgen' to project: the component only authors its own types, which are described by the .winmd that 'cswinrtwinmdgen' emits and projected into 'WinRT.Component.dll' by 'cswinrtprojectiongen'. Skip invoking the reference projection generator in that case. This keeps authoring-only builds faster and avoids emitting projection support code (and its assembly-level attributes) into the component's own assembly. The skip is decided in the target body, where '@(CsWinRTInputs)' is reliably populated by the 'CsWinRTRemoveWinMDReferences' dependency (a target condition would evaluate before that item is populated). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Set DisableRuntimeMarshalling for the smoke test projects CsWinRT relies on runtime marshalling being disabled for its interop. Authored components normally get '[assembly: DisableRuntimeMarshalling]' emitted in generated projection source, but an input-less component now skips the reference projection generator entirely, so the project must opt in itself. Set it in the shared smoke test 'Directory.Build.props', exactly as a real component author would in their own project. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Run the smoke tests on both CoreCLR and Native AOT Add a '-Runtime' parameter to the smoke test runner: 'CoreCLR' (default) keeps the existing build-and-run behavior, while 'NativeAot' publishes the project with 'PublishAot=true' for win-x64, exercising the full publish pipeline (projection and interop generators, then ILC). Consumption publishes a self-contained app and runs it directly from the '.exe'; authoring just verifies the component publishes cleanly (the output is never loaded). In CI, suffix the existing smoke test steps with '(CoreCLR)' and add a '(NAOT)' step after each, so a failure points at the exact runtime. All four keep 'continueOnError' and feed the existing final gate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document the public-but-hidden implementation-only type strategy CsWinRT now keeps a few implementation-only types public in the reference assembly but hidden (reference-assembly-only [Obsolete] + [EditorBrowsable(Never)]), instead of stripping them entirely. Explain both strategies and why stripping is not always possible: when CsWinRT-generated user code names the type and is compiled against the reference assembly, stripping would cause CS0234/CS0246, so the type must remain present but discouraged. Cover the current cases (CSWINRT3002 type map groups, CSWINRT3003 component authoring attributes, CSWINRT3004 TrustLevel) and update the runtime error-id table accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Cover the public-but-hidden implementation-only type strategy in the maintenance skills The two 'update-*-instructions' skills drive the self-maintenance of copilot-instructions.md and the interop-generator SKILL.md, but their checklists did not mention the public-but-hidden implementation-only type strategy that those docs now describe. Extend the WinRT.Runtime and source-generator verification items in the copilot-instructions maintenance skill to cover the two strategies (strip-entirely vs public-but-hidden) and the current CSWINRT3001-3004 cases, and extend the interop-generator maintenance skill (resolvers/references and related-docs steps) to verify the public-but-hidden note and the marking scheme. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Include RestrictedErrorInfoExceptionMarshaller Add an explicit <Compile Include> for InteropServices/Marshalling/RestrictedErrorInfoExceptionMarshaller.cs so the public RestrictedErrorInfoExceptionMarshaller type is present in reference assemblies. The file was previously excluded by the blanket removals; we add it back manually for simplicity while keeping other implementation-only folders excluded. * Add a projection smoke test for the real NuGet package Add a third smoke test that generates a reference projection for a third-party Windows Runtime component, exactly as a NuGet projection author would. It sets 'CsWinRTGenerateReferenceProjection=true' and points 'CsWinRTInputs' at a '.winmd', exercising 'cswinrtprojectionrefgen' and 'cswinrtimplgen' end-to-end against the packed package. The projected metadata is reused from the sibling 'Authoring' smoke test: a build-ordering 'ProjectReference' (ReferenceOutputAssembly/Private both false, mirroring 'src/WinRT.Internal') ensures 'Authoring.winmd' exists before this project runs the generator, avoiding a separate hand-authored '.winmd'. The test is build-only (CoreCLR), verifying both a forwarder and a 'ref' reference assembly are produced. Wire it into 'run-smoke-tests.ps1' (-Test Projection, skipped for Native AOT), 'src/build.cmd', and a CoreCLR CI step, and document it in docs/structure.md and the testing skills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make WindowsRuntimeReferenceAssemblyAttribute public-but-hidden (CSWINRT3004) The reference projection generator emits '[assembly: WindowsRuntimeReferenceAssembly]' into the projection assemblies it produces (via the projection writer's 'AssemblyAttributes.cs' base resource), and that attribute genuinely ships in the reference projection assemblies of Windows Runtime projection NuGet packages. It must therefore remain resolvable in the 'WinRT.Runtime.dll' reference assembly, so it cannot be stripped like other implementation-only types. Apply the same public-but-hidden strategy already used for the type map group types (CSWINRT3002) and the component authoring attributes (CSWINRT3003): keep the type in the reference assembly but mark it reference-assembly-only '[Obsolete(DiagnosticId = CSWINRT3004)]' + '[EditorBrowsable(Never)]' to discourage direct use. Add the obsolete message/diagnostic-id constants, a 'docs/diagnostics/cswinrt3004.md' page, and suppress the diagnostic in the generated 'AssemblyAttributes.cs'. Update the copilot instructions (the two-strategies note and the error-id table) and the maintenance skill, repurposing the previously-stale CSWINRT3004 id (it had been documented for a since-reverted 'TrustLevel' case that no longer exists). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stop reference projections from referencing implementation-only types A reference projection ships in a Windows Runtime projection NuGet package and is compiled (with 'ProduceOnlyReferenceAssembly') against the stripped 'WinRT.Runtime.dll' reference assembly, which contains only the public API surface. The projection writer was emitting code that referenced implementation-only types in signature positions (field types, method parameters, base types, attributes) that do not exist in that reference assembly, so building a reference projection from the package failed to compile (CS0246/CS0234). In reference-projection mode, stop emitting the implementation-only signatures: the '[WindowsRuntimeMetadata]' attribute, the private '_objRef_*' fields (typed 'WindowsRuntimeObjectReference'), the static activation-factory object references, the activation-factory plumbing (factory callback classes and args structs), and the '[UnsafeAccessor]' static extern declarations (whose parameters are typed 'WindowsRuntimeObjectReference'). Constructors keep their public signatures but emit a 'throw null' body instead of a base call referencing implementation-only activation types. Member bodies are unaffected: a reference assembly compile does not bind method bodies, so the public member signatures are all that needs to resolve. Implementation-mode output is unchanged. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Broaden the authoring smoke test to cover more projection shapes The authoring smoke test produces 'Authoring.winmd', which the projection smoke test then generates a reference projection for. Add a richer set of authored Windows Runtime types alongside the existing 'Greeter' so the projection path exercises more codegen: two enums (including a [Flags] enum), a struct, a delegate, an interface, and a runtime class with two constructors (default and parameterized), instance methods, a settable property, an interface-implemented property, an event, and static members. Validated end-to-end locally: the types compile, 'cswinrtwinmdgen' emits a valid '.winmd' for them, and the resulting reference projection compiles cleanly against the stripped 'WinRT.Runtime' reference assembly (no implementation-only types referenced). Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Recognize reference projection types in interop generator discovery The interop generator identified projected Windows Runtime types via the per-type [WindowsRuntimeMetadata] attribute (IsProjectedWindowsRuntimeType). That attribute is now stripped from reference projections (it is an implementation-only type, absent from the WinRT.Runtime reference assembly that reference projections compile against), so the type-hierarchy discovery crawl over reference projection assemblies stopped recognizing any projected classes and failed with CSWINRTINTEROPGEN0036 (no type hierarchy key-value pairs discovered). Add IsReferenceProjectionWindowsRuntimeType, which recognizes a projected type by its declaring assembly's [WindowsRuntimeReferenceAssembly] marker, mirroring the existing IsComponentWindowsRuntimeType handling for authored components (which likewise expose projected types without the per-type attribute). Fold it into the five "is this a projected Windows Runtime type" gates that can observe reference projection definitions directly (type hierarchy base-type tracking, IsProjectedWindowsRuntimeClassType, IsWindowsRuntimeType, IsNotExclusiveToWindowsRuntimeType, and the projected interface branch in InteropInterfaceEntriesResolver). The type-hierarchy base-type check now inspects the resolved base type definition (instead of the unresolved reference) so the assembly-level marker can be read off it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Recover Windows Runtime metadata name from the implementation projection The interop type-name marker for a third-party (non-well-known) projected type must be the source .winmd stem, because the projection writer encodes that same stem into the [UnsafeAccessorType] references it bakes into the implementation projection (WinRT.Projection.dll). The generator read that stem from the per-type [WindowsRuntimeMetadata] attribute. That attribute is now stripped from reference projections (it is an implementation-only attribute, absent from the WinRT.Runtime reference assembly they compile against). The discovery input set keeps the reference projection and excludes the forwarder/impl .dll, so a third-party type resolves to the reference projection where the attribute is gone. The marker would then fall back to the reference projection's own assembly name, which is not guaranteed to equal the .winmd stem (e.g. WinUI / Windows App SDK merge several .winmd files into one projection assembly), producing [UnsafeAccessor] references that don't match the generated interop type names. Add a TypeDefinition.GetWindowsRuntimeMetadataName(RuntimeContext) overload that reads the attribute directly when present, and otherwise — for a type from a reference projection — recovers the stem from the matching type in the third-party implementation projection (WinRT.Projection.dll), which retains it. The implementation projection is located through RuntimeContext.GetLoadedAssemblies(), which returns assemblies loaded via LoadModule even when they are outside the resolver's path set, so this needs no changes to the many InteropUtf8NameFactory call sites (they already flow the RuntimeContext through). When the stem cannot be found, the caller's fallback to the assembly name is preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Emit XAML struct metadata attributes only for implementation projections The hand-authored full-replacement XAML struct additions (CornerRadius, Duration, GridLength, KeyTime, RepeatBehavior, Matrix3D, for both the Windows.UI.Xaml and Microsoft.UI.Xaml namespaces) carried their [WindowsRuntimeMetadata("...")] attribute outside the '#if !CSWINRT_REFERENCE_PROJECTION' block, so it was also emitted into reference projections. That attribute is implementation-only and is stripped from the WinRT.Runtime reference assembly, so a reference projection compiled against that reference assembly would fail to resolve it. Move the attribute inside the existing '#if !CSWINRT_REFERENCE_PROJECTION' block, alongside the other implementation-only attributes ([WindowsRuntimeClassName] and the ComWrappers marshaller), so it is only emitted for implementation projections. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Emit GetDefaultInterface helper only for implementation projections The 'internal new GetDefaultInterface()' helper emitted on unsealed projected runtime classes returns 'WindowsRuntimeObjectReferenceValue', which is an implementation-only type stripped from the WinRT.Runtime reference assembly. It was emitted in both implementation and reference-projection modes, but its only consumer is the ABI marshaller (also implementation-only and not emitted in reference-projection mode). As a result, the helper was dead code in a reference projection, yet it still failed to compile against the WinRT.Runtime reference assembly that reference projections bind against. This only surfaced for unsealed classes: the helper exists so that overrides on derived classes can hide it with 'new', so sealed classes never emit it. That is why simple sealed components (e.g. the authoring smoke test) never hit it, while the Windows SDK XAML projections (full of unsealed base classes) did. Gate the helper on '!context.Settings.ReferenceProjection', matching the sibling 'IWindowsRuntimeInterface<T>.GetInterface()' helper, so neither is emitted into reference projections. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify runtime class shapes in Thermometer test Update Thermometer smoke test comments to mention both a sealed (non-derivable) runtime class and an unsealed (derivable) runtime class. Add a commented-out Weather class (TODO) to represent an unsealed runtime class for future authoring support. The change documents intended coverage and leaves a placeholder for when unsealed class authoring is supported. * Guard implementation-only TokenizerHelper usage in reference projections The hand-authored XAML struct/Color additions (CornerRadius, Thickness, Matrix, Matrix3D for both Windows.UI.Xaml and Microsoft.UI.Xaml, plus Windows.UI.Color) format their ToString output using 'WindowsRuntime.InteropServices.TokenizerHelper' to get the culture's numeric list separator. That helper is an implementation-only API ([WindowsRuntimeImplementationOnlyMember]) that is stripped from the WinRT.Runtime reference assembly, so a reference projection that includes any of these types fails to compile against that reference assembly (CS0234). Following the same pattern the runtime uses for its reference-assembly stubs, guard each affected method body with '#if CSWINRT_REFERENCE_PROJECTION' / 'throw null;' / '#else' / the existing implementation / '#endif'. The implementation projection keeps the real body; the reference projection gets a stub, so it no longer references the stripped helper. The bodies are never executed in a reference projection (the real implementation is generated at app build time). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Guard implementation-only DispatcherQueueSynchronizationContext wrappers The DispatcherQueueSynchronizationContext additions (Windows.System and Microsoft.UI.Dispatching) wrap the implementation-only 'WindowsRuntime.InteropServices.DispatcherQueueSynchronizationContext' ([WindowsRuntimeImplementationOnlyMember], stripped from the WinRT.Runtime reference assembly). The impl-only type appeared in a private field, both constructors, and every method body, so a reference projection that included these types failed to compile against the reference assembly. Guard the implementation-only members (the private '_innerContext' field and the private copy constructor) with '#if !CSWINRT_REFERENCE_PROJECTION', and stub the public API bodies (the public constructor, Post, Send, CreateCopy) with '#if CSWINRT_REFERENCE_PROJECTION' / 'throw null;' / '#else'. The reference projection keeps the public API surface but no longer references the stripped type; the real implementation is generated for the implementation projection at app build time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stub mapped-interface members in reference projections MappedInterfaceStubFactory emits the C# collection-interface members (IList<T>, IDictionary<K,V>, IEnumerable<T>, IDisposable, the bindable collections, and INotifyDataErrorInfo) on classes that implement the corresponding WinRT interfaces. It already skipped its implementation-only '[UnsafeAccessor]' externs in reference-projection mode, but it still emitted the member *bodies*, which reference those skipped externs (the '*Methods_*' helpers), the skipped '_objRef_*' fields, and the implementation-only 'ABI.*Methods' helpers. None of those exist in a reference projection, so a reference projection that included any mapped-collection class failed to compile against the stripped WinRT.Runtime reference assembly. Give each per-interface 'Emit*' method a reference-projection-first branch that emits the full member set as a single stub (every body is 'throw null'), keeping the public member signatures identical to the implementation projection. The implementation path is unchanged. The previously inline switch cases (IBindableIterable, IBindableIterator, INotifyDataErrorInfo) are extracted into their own 'Emit*' methods so they follow the same shape. Validated that the implementation-projection output is byte-for-byte identical to before across the full Windows SDK projection (no runtime impact), and that the reference projection no longer references any of the stripped plumbing. This removes ~4,900 of the reference-projection compile errors against the stripped reference assembly (the remaining errors are tracked separately in #2468). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make the synthetic ref-mode ctor accessible to derived projected classes In a reference projection, a projected class with no activatable/composable constructors gets a synthetic 'private TypeName() { throw null; }' ctor to suppress the C# compiler's implicit public default constructor. When such a class is the base of another projected class (e.g. 'UriActionEntity : ActionEntity'), the derived class's own synthetic ctor implicitly chains to the base's parameterless ctor -- which was 'private', so the derived class failed to compile against the stripped WinRT.Runtime reference assembly (CS0122). The real 'WindowsRuntimeObjectReference'-based ctor that derived classes chain to in the implementation projection is not emitted in a reference projection, so the synthetic ctor is the only one available. Emit the synthetic ctor as 'private protected' for unsealed classes (which can be base classes), so derived projected classes in the same projection can chain to it. Sealed classes keep 'private'. The ctor stays non-public, so it still suppresses the implicit public default constructor and does not widen the public API surface. The implementation projection is unaffected (its output is unchanged). This removes the CS0122 reference-projection errors (tracked in #2468). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Emit accessible parameterless ctor for unsealed composable classes in ref projections In reference-projection mode the real WindowsRuntimeObjectReference-based base constructor is not emitted, so a derived projected class's ref-mode constructor (whose implicit base() call has no target) fails to compile against an unsealed base that only exposes parameterized composable constructors. Emit a 'private protected TypeName() { throw null; }' for unsealed classes that don't already emit a public parameterless constructor (a default [Activatable], or a factory/composable method with no user parameters), giving derived projected classes a base-chain target. Sealed classes keep the existing behavior (a synthetic ctor only to suppress the implicit public default ctor when none are emitted). Validated byte-identical implementation-mode output for the full Windows SDK projection; reduces the reference-projection compile from 192 to 12 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Guard implementation-only storage-extension bodies in reference projections WindowsRuntimeStorageExtensions calls implementation-only APIs (WindowsRuntimeIOHelpers and the IStorage*HandleAccessMethods classes) that are absent from the WinRT.Runtime reference assembly, so the hand-authored Windows.Storage addition failed to compile in reference-projection mode. Following the same pattern the runtime uses for its reference-assembly stubs (and the earlier TokenizerHelper guard), wrap each affected method body with '#if CSWINRT_REFERENCE_PROJECTION' / 'throw null;' / '#else' / the existing implementation / '#endif'. The implementation projection keeps the real body; the reference projection gets a stub. The added directives are inert when CSWINRT_REFERENCE_PROJECTION is undefined, so implementation-mode output is unchanged. With this fix the full Windows SDK reference projection compiles with 0 errors against the stripped WinRT.Runtime reference assembly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add Windows SDK and XAML reference-projection smoke tests Generating the Windows SDK reference projections (the 'Microsoft.Windows.SDK.NET.Ref' projection packages) is exactly what a downstream repository does with the CsWinRT package, and it is where reference-projection codegen regressions surface: the generated projection must compile against the stripped 'WinRT.Runtime' reference assembly, with no references to implementation-only runtime types. Add two end-to-end smoke tests that exercise this so such regressions fail here, early, instead of breaking that package. * WindowsSdkProjection generates the base Windows SDK reference projection from the 'Microsoft.Windows.SDK.Contracts' '.winmd' files. * WindowsSdkXamlProjection generates the 'Windows.UI.Xaml' reference projection, which references the base projection above (mirroring how the UWP XAML projection package depends on the base Windows SDK projection package). Both mirror the existing 'Projection' smoke test (CsWinRTGenerateReferenceProjection) and the Windows SDK metadata staging in 'WinRT.Sdk.Projection.csproj', and use the same namespace include/exclude split the projection generator applies for 'WinRT.Sdk.Projection' and 'WinRT.Sdk.Xaml.Projection' (see 'WriteWindowsSdkFilters'). The shared 'WindowsSdkContracts.targets' downloads 'Microsoft.Windows.SDK.Contracts' and feeds its '.winmd' files into '@(CsWinRTInputs)'. That package, like the preview Windows SDK ref pack, is restored from the CsWinRTDependencies feed, alongside the local package under test. The reference-projection verification in 'run-smoke-tests.ps1' (forwarder + reference assembly produced) is shared across all three reference-projection tests, and both new tests are build-only (CoreCLR), wired into the CI as their own 'continueOnError' steps. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Build the Windows SDK projection smoke tests as plain net10.0 The two Windows SDK reference-projection smoke tests inherited the '-windows10.0.26100.1' target framework from the shared 'Directory.Build.props'. That target framework makes the .NET SDK add an implicit reference to the prebuilt Windows SDK projection ('Microsoft.Windows.SDK.NET.dll') -- the very projection these tests regenerate -- so every generated type collided with its prebuilt counterpart (tens of thousands of CS0436), and 'ApiContractAttribute' collided between 'Microsoft.Windows.SDK.NET' and 'WinRT.Runtime' (CS0433), failing the build. Override the target framework to plain 'net10.0' for both projects, exactly as the real Windows SDK projection is built ('src/WinRT.Sdk.Projection/WinRT.Sdk.Projection.csproj'): generating the Windows SDK projection means it cannot reference the Windows SDK projection. The base 'Windows.*' types the XAML projection needs come from the sibling 'WindowsSdkProjection' reference assembly instead. The '-windows'-specific properties the shared props also set ('TargetPlatformMinVersion', 'WindowsSdkPackageVersion') are inert without a target platform. The package's reference-projection flow fully supports this (CsWinRTExeTFM resolves to 'net10.0' for any net10.0+ target framework), and the generated projection still compiles against the packaged 'WinRT.Runtime' reference assembly (NuGet uses 'ref/net10.0' at compile time), which is what these tests validate. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify over-escaped interpolated raw strings in MappedInterfaceStubFactory Several reference- and implementation-mode stub strings used the '$$""" + ... + """' form (with '{{x}}' interpolation holes) even though their content has no literal braces, so the doubled '$$' was unnecessary. Reduce those six to a single '$' (with '{x}' holes). The strings that do contain literal braces (indexers, event accessors, 'Dispose() { }') still need '$$' and are left unchanged. This is a purely cosmetic change: the generated text is identical. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update testing skill for the Windows SDK projection smoke tests The smoke tests section listed three projects; it now documents all five, adding 'WindowsSdkProjection' and 'WindowsSdkXamlProjection' (the Windows SDK reference projections built from 'Microsoft.Windows.SDK.Contracts', staged by the shared 'WindowsSdkContracts.targets'). Also correct the smoke tests' shared configuration: 'RestoreSources' uses the local package output plus the 'CsWinRTDependencies' feed (not public NuGet), the two SDK projection tests override the target framework to plain 'net10.0', and the reference-projection verification is shared across all three reference-projection tests (build-only, CoreCLR). Refresh the 'Authoring' smoke test description to reflect its broader authored type catalog ('Thermometer.cs'), and update the routing table accordingly. Update the 'update-testing-instructions' skill's smoke-test verification step to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use <see> tag for TypeDefinition in docs Replace XML <c> tag with a <see cref="TypeDefinition"/> reference in the GetWindowsRuntimeMetadataName documentation to produce a proper XML doc reference and improve IntelliSense/linking. No functional changes. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add [WindowsRuntimeType] marker and centralize WinRT metadata mapping (#2467) * Add [WindowsRuntimeType] marker and repurpose [WindowsRuntimeMetadata] The per-type [WindowsRuntimeMetadata("contract")] attribute served two purposes: marking a type as a Windows Runtime type (presence) and recording the source .winmd stem (value). The runtime only ever reads the presence; the value is used exclusively by build-time tooling. Carrying the string on every projected type is pure metadata overhead at runtime. Split the two concerns: - Add a parameterless [WindowsRuntimeType] marker attribute (implementation-only), applied to every projected type and to proxy types for custom-mapped types. - Repurpose [WindowsRuntimeMetadata] into a (Type, string) attribute targeting a single class with AllowMultiple, so the type -> .winmd-stem mapping can live on a centralized, trimmable lookup type (emitted by the projection generator in a later change) instead of on each type. - Remove [WindowsRuntimeMappedMetadata]; proxy types now use [WindowsRuntimeType]. Update the runtime metadata/marshalling lookups accordingly: presence checks now use [WindowsRuntimeType], and the projected-vs-proxy distinction (previously inferred from [WindowsRuntimeMetadata] vs [WindowsRuntimeMappedMetadata]) now uses [WindowsRuntimeMappedType], which proxy types already carry to point at their public type. The 'System.EventHandler' proxy intentionally remains unmarked (a pure custom type, not a metadata type). Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com> * Emit [WindowsRuntimeType] and a centralized metadata-types lookup Replace the per-type [WindowsRuntimeMetadata("stem")] attribute emitted on every projected type with the parameterless [WindowsRuntimeType] marker, and move the type -> source .winmd-stem mapping to a centralized ABI.WindowsRuntimeMetadataTypes lookup type carrying one [WindowsRuntimeMetadata(typeof(T), "stem")] per projected type. This mirrors how ABI.WindowsRuntimeDefaultInterfaces centralizes default interfaces, and lets the (build-time only) metadata be trimmed away when unused. The mapping is recorded as a side effect of emitting the per-type marker (the single chokepoint), so the lookup set never drifts from the set of types that actually carry the marker (e.g. enums are not projected in component mode). Both emission and the lookup type are skipped in reference-projection mode, where [WindowsRuntimeType] is stripped along with the rest of the implementation-only surface. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Read metadata name from the centralized lookup in the interop generator Update the interop generator for the [WindowsRuntimeType] marker + centralized metadata mapping: - IsProjectedWindowsRuntimeType now checks for [WindowsRuntimeType] (the parameterless marker) instead of the per-type [WindowsRuntimeMetadata] attribute. - GetWindowsRuntimeMetadataName now reads the source .winmd stem from the ABI.WindowsRuntimeMetadataTypes lookup type in the implementation projection (via a new GetWindowsRuntimeMetadataTypesLookup module extension, mirroring GetDefaultInterfacesLookup), rather than from a per-type attribute. This works uniformly for types resolved from implementation and reference projections, since both select the same implementation projection that carries the lookup type. - Generated proxy types now emit the parameterless [WindowsRuntimeType] marker instead of [WindowsRuntimeMappedMetadata("<contract>")]; the runtime only checks for its presence. - InteropReferences/WellKnownMetadataNames swap the [WindowsRuntimeMetadata]/ [WindowsRuntimeMappedMetadata] references for [WindowsRuntimeType]. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Read metadata name from the centralized lookup in the WinMD generator Update the WinMD generator for the [WindowsRuntimeType] marker + centralized metadata mapping, mirroring the interop generator: - IsWindowsRuntimeType now checks for the [WindowsRuntimeType] marker. - WindowsRuntimeAssemblyName now reads the source .winmd contract assembly name from the centralized ABI.WindowsRuntimeMetadataTypes lookup type in the type's declaring module (via a new GetWindowsRuntimeMetadataTypesLookup module extension), instead of from a per-type [WindowsRuntimeMetadata] attribute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update docs for the [WindowsRuntimeType] marker and metadata-types lookup Document the split of [WindowsRuntimeMetadata("contract")] into the parameterless [WindowsRuntimeType] marker plus the centralized, trimmable ABI.WindowsRuntimeMetadataTypes lookup type: a new subsection in the copilot instructions, the updated type-inclusion and generated-attribute notes in the interop generator skill, and the rewritten metadata-name derivation in the name-mangling reference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refactor metadata attribute file emission Replace repetitive line-by-line writes with local writer lambdas and a single interpolated file template. The change centralizes file header/namespace emission and moves metadata/interface entry loops into small helper methods (WriteMetadataEntries / WriteInterfaceMappings), then emits the final source via a single raw-string template. Improves readability and reduces duplication when producing WindowsRuntimeMetadataTypes.cs and related attribute mapping files. * Qualify metadata-types lookup entries with ABI.Impl in component mode The centralized `ABI.WindowsRuntimeMetadataTypes` lookup references each projected type via `typeof(...)`, using the name recorded by `WriteWinRTMetadataAttributeBody`. That recording used the canonical `type.Names()` namespace, but in component mode (producing `WinRT.Component.dll`) projected types are emitted under the `ABI.Impl.<Ns>` namespace (see `WriteBeginProjectedNamespace`). As a result the generated `WindowsRuntimeMetadataTypes.cs` referenced e.g. `typeof(global::AuthoringTest.AnotherNamespace.IMappedTypeParamClassClass)` while the interface is actually declared at `ABI.Impl.AuthoringTest.AnotherNamespace.IMappedTypeParamClassClass`, failing to compile with CS0234 and breaking the component projection for native consumers (AuthoringConsumptionTest / AuthoringConsumptionTest2). Mirror the `ABI.Impl.` prefix when recording the entry so the `typeof(...)` resolves to the actual emitted type, and so the `(Namespace, Name)` key matches what the interop generator computes for the type. Non-component projections are unaffected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pass iid to ActivateInstanceUnsafe (fix from #2476) Forward the 'iid' parameter into WindowsRuntimeActivationHelper.ActivateInstanceUnsafe. This ensures the activation helper receives the requested interface IID (used for composable/activation scenarios) instead of omitting it, fixing incorrect/default behavior when selecting the default interface during activation. * Fix Windows SDK projection smoke tests after projection target split PR #2487 split the projection target: the "Windows Metadata not provided or detected" check moved out of CsWinRTGenerateProjection into a new private _CsWinRTComputeProjectionRefGenInputs target, which runs as a dependency of CsWinRTGenerateProjection. MSBuild runs a target's DependsOnTargets before its BeforeTargets hooks, so the smoke tests' _StageWindowsSdkContractsInputs target (BeforeTargets=CsWinRTGenerateProjection) now added the SDK winmds to @(CsWinRTInputs) *after* the check had already run against an empty list and failed. Feed the SDK contract winmds into @(CsWinRTInputs) via a static, evaluation-time ItemGroup instead of a target, matching how every in-repo projection project (src/Projections/*, Projection/Projection.csproj) does it. This makes the inputs independent of how the CsWinRT projection targets are factored. NuGetPackageRoot and the restored winmds are both available at evaluation time (the smoke tests always run a full build, which restores first). The folder-existence diagnostic is kept in a small target anchored to the standard PrepareForBuild target so it stays ordering-independent. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 435af478-1759-4c7e-8176-3b92f72136ec --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 435af478-1759-4c7e-8176-3b92f72136ec
1 parent 2d55b36 commit 45f017b

414 files changed

Lines changed: 5276 additions & 3467 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,16 @@ The runtime library (`WinRT.Runtime.dll`) provides all common infrastructure for
173173
- **Warnings as errors**: release only. `EnforceCodeStyleInBuild` enabled, `AnalysisLevelStyle` = `latest-all`.
174174
- **Strong-name signed** with `key.snk`
175175
- **AOT compatible**: `IsAotCompatible = true`
176+
- **Dual-built**: produces both the implementation assembly and a stripped **reference assembly** (see "Reference assembly" below); the reference build adds a `Microsoft.CodeAnalysis.BannedApiAnalyzers` reference
176177

177178
**Directory structure:**
178179

179180
```
180181
WinRT.Runtime2/
181-
├── WindowsRuntimeObject.cs # Base class for ALL projected runtime classes
182+
├── WindowsRuntimeObject.cs # Base class for ALL projected runtime classes (partial; reference + shared surface)
183+
├── WindowsRuntimeObject.Impl.cs # Implementation-only half of WindowsRuntimeObject (excluded from the reference assembly)
182184
├── WindowsRuntimeInspectable.cs # Fallback type for unknown native objects
185+
├── BannedSymbols.txt # Banned API list for the reference assembly build (blocks implementation-only types from leaking)
183186
├── ABI/ # ABI type mappings (managed ↔ native)
184187
│ ├── System/ # Primitives, String, Uri, DateTimeOffset, collections, etc.
185188
│ ├── Windows.Foundation/ # Foundation types (Point, Rect, Size, etc.)
@@ -242,13 +245,27 @@ WinRT.Runtime2/
242245
- **T4 templates**: 6 `.tt` files generate constants (`HRESULT` codes, interface IIDs, XAML class names) and specialized marshallers (blittable array types).
243246
- **Feature switches**: opt-in/opt-out runtime features are controlled via `[FeatureSwitchDefinition]`-annotated properties in `WindowsRuntimeFeatureSwitches` (`Properties/WindowsRuntimeFeatureSwitches.cs`). Each switch is backed by an `AppContext` configuration property (e.g. `CSWINRT_ENABLE_MANIFEST_FREE_ACTIVATION`) and wired to an MSBuild property (e.g. `CsWinRTEnableManifestFreeActivation`) in `nuget/Microsoft.Windows.CsWinRT.targets`, which emits `RuntimeHostConfigurationOption` items with `Trim="true"`. This lets ILLink (trimming) and ILC (Native AOT) treat the switch values as constants and dead-code-eliminate all code behind disabled switches, making opt-in features fully pay-for-play.
244247

248+
**Reference assembly:**
249+
250+
`WinRT.Runtime` is built **twice**: once as the implementation assembly (the actual `WinRT.Runtime.dll` that runs) and once as a lightweight **reference assembly** that downstream tooling and `ProjectReference` consumers compile against. The reference build is selected via the `CsWinRTBuildReferenceAssembly` MSBuild property and strips out every implementation-only type and member, so none of them are exposed on the public API surface.
251+
252+
- **Compilation symbols**: the implementation build defines `WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY`; the reference build defines `WINDOWS_RUNTIME_REFERENCE_ASSEMBLY`. Members that differ between the two (e.g. the explicit interface implementations on `WindowsRuntimeObject`, which become `throw null` stubs) are guarded with `#if`/`#elif` on these symbols. An entire source file can opt out of the reference assembly by placing `#define WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE` at the top: the `RemoveWindowsRuntimeImplementationOnlyFiles` target removes those files (plus the whole `ABI/`, `NativeObjects/`, `Exceptions/`, `Windows.UI.Xaml.Interop/`, and most `InteropServices/` subfolders) before `CoreCompile`.
253+
- **`ProduceReferenceAssembly = false`**: the implementation build disables the SDK's automatic reference assembly, because `WinRT.Runtime` ships its own (the SDK-generated one would leak the implementation-only types to consumers and break reference projections).
254+
- **`[WindowsRuntimeImplementationOnlyMember]`** (`Attributes/WindowsRuntimeImplementationOnlyMemberAttribute.cs`): an `internal sealed`, `[Conditional("WINDOWS_RUNTIME_REFERENCE_ASSEMBLY")]` marker placed on implementation-only types/members (including most of the marker attributes under `Attributes/`). For the **common case** (a type/member that nothing outside the implementation assembly needs to reference), it replaces the older `[Obsolete] + [EditorBrowsable(Never)]` combination, makes the intent explicit, and is only ever emitted into the reference assembly (where it is stripped along with the members it marks).
255+
- **Two strategies for implementation-only API** — there are two ways CsWinRT keeps an implementation detail out of the supported surface, and the choice depends on whether *generated code that compiles against the reference assembly* needs to name the type:
256+
1. **Strip it entirely** (the default, preferred): mark it `[WindowsRuntimeImplementationOnlyMember]` (or place it in a file/folder excluded from the reference build) so it is **absent** from the reference assembly. This is the cleanest option and is used for everything that is only reached at runtime or via `[IgnoresAccessChecksTo]` from `WinRT.Interop.dll` (e.g. the ABI marshallers, native object wrappers, vtable helpers).
257+
2. **Keep it public but hidden** (the exception): leave the type in the reference assembly, but — only there (`#if WINDOWS_RUNTIME_REFERENCE_ASSEMBLY`) — mark it `[Obsolete(..., DiagnosticId = "CSWINRT3xxx", UrlFormat = ...)]` + `[EditorBrowsable(Never)]`. This is required when **CsWinRT-generated code** references the type by name *and that code is compiled against the reference assembly* (so stripping would cause `CS0234`/`CS0246`). Such generated code suppresses the diagnostic (e.g. `#pragma warning disable`), so normal builds are unaffected, while direct use in user code surfaces the obsolete warning. The obsolete message/diagnostic-id constants live in `Properties/WindowsRuntimeConstants.cs`, and each id has a docs page under `docs/diagnostics/`. Current cases: the three type map group types (`CSWINRT3002`; referenced by the source generator's `[assembly: TypeMapAssemblyTarget<TGroup>]` output), the component authoring attributes `WindowsRuntimeComponentAssemblyAttribute`/`WindowsRuntimeComponentAssemblyExportsTypeAttribute` (`CSWINRT3003`; referenced by the authoring source generator's `ManagedExports.g.cs`), and `WindowsRuntimeReferenceAssemblyAttribute` (`CSWINRT3004`; emitted as `[assembly: WindowsRuntimeReferenceAssembly]` by the projection writer's `AssemblyAttributes.cs` base resource, and — unlike the others — also genuinely shipped in the reference projection assemblies of Windows Runtime projection NuGet packages). The reference-assembly-only `WindowsRuntimeObject()` constructor (`CSWINRT3001`) is the same mechanism applied to a constructor.
258+
- **Banned API analyzer**: the reference build references `Microsoft.CodeAnalysis.BannedApiAnalyzers` and lists `WindowsRuntimeImplementationOnlyMemberAttribute` in `BannedSymbols.txt`, with `RS0030` promoted to an error, so the build fails if any implementation-only type ever leaks into the reference surface. It also suppresses warnings that only appear in the stripped build (`CS8597`, `IDE0005`, `IDE0380`).
259+
- **Reference-assembly-only `WindowsRuntimeObject()` constructor**: a parameterless `protected` constructor exists only in the reference assembly (`#if WINDOWS_RUNTIME_REFERENCE_ASSEMBLY`). It is marked `[Obsolete(..., DiagnosticId = "CSWINRT3001")]`, so user code that derives from `WindowsRuntimeObject` gets the `CSWINRT3001` warning; because the constructor is absent from the implementation assembly, doing so throws `MissingMethodException` at runtime. Only CsWinRT-generated projections may derive from `WindowsRuntimeObject`. The related messages live in `Properties/WindowsRuntimeConstants.cs`.
260+
- **Packaging**: the implementation assembly ships in `lib\net10.0\` of the `Microsoft.Windows.CsWinRT` NuGet package, and the reference assembly (with its XML documentation, trimmed to the reference surface) ships in `ref\net10.0\`. The dual build and staging are driven by `src/build.cmd` and the Azure Pipelines build steps.
261+
245262
**Types projected in WinRT.Runtime:**
246263

247264
Not all WinRT types are generated automatically by the projection writer into SDK projection assemblies. `WinRT.Runtime` contains two categories of types that require special handling:
248265

249266
#### Custom-mapped types
250267

251-
These are built-in C#/.NET types that are mapped to WinRT types. Because the .NET type already exists in the BCL (and is not owned by CsWinRT), it cannot be "projected" in the usual sense. Instead, CsWinRT associates the necessary WinRT metadata with it via attributes such as `[WindowsRuntimeMappedMetadata]` and dedicated ABI marshalling code in the `ABI/System/` directory. Some of these types map to identically-named WinRT types (e.g. `int``Int32`, `Guid``Guid`), while others map to a different WinRT type entirely. The `EventHandler` delegate is especially noteworthy: the non-generic `System.EventHandler` is handled as a special case (see `ABI/System/EventHandler.cs`), while `System.EventHandler<TEventArgs>` maps to `Windows.Foundation.EventHandler<T>`, and `System.EventHandler<TSender, TEventArgs>` (a two-parameter generic delegate projected by CsWinRT) maps to `Windows.Foundation.TypedEventHandler<TSender, TResult>`.
268+
These are built-in C#/.NET types that are mapped to WinRT types. Because the .NET type already exists in the BCL (and is not owned by CsWinRT), it cannot be "projected" in the usual sense. Instead, CsWinRT associates the necessary WinRT metadata with it via attributes such as `[WindowsRuntimeType]` (a parameterless marker; see below) and dedicated ABI marshalling code in the `ABI/System/` directory. Some of these types map to identically-named WinRT types (e.g. `int``Int32`, `Guid``Guid`), while others map to a different WinRT type entirely. The `EventHandler` delegate is especially noteworthy: the non-generic `System.EventHandler` is handled as a special case (see `ABI/System/EventHandler.cs`), while `System.EventHandler<TEventArgs>` maps to `Windows.Foundation.EventHandler<T>`, and `System.EventHandler<TSender, TEventArgs>` (a two-parameter generic delegate projected by CsWinRT) maps to `Windows.Foundation.TypedEventHandler<TSender, TResult>`.
252269

253270
The following table lists all custom-mapped types where the .NET type maps to a **differently-named** WinRT type:
254271

@@ -306,6 +323,12 @@ These are WinRT types that are defined directly in `WinRT.Runtime` rather than b
306323

307324
Some of these types — particularly the bindable collection interfaces (`IEnumerable`, `IList`) and XAML-related types — have **different IIDs and/or runtime class names** depending on whether `Windows.UI.Xaml.*` (UWP XAML) or `Microsoft.UI.Xaml.*` (WinUI) support is being used (controlled by the `CsWinRTUseWindowsUIXamlProjections` MSBuild property). This requires further special handling in both the generated projection code and the interop generator to ensure that the correct marshalling and metadata info is associated with them at publish time.
308325

326+
#### The `[WindowsRuntimeType]` marker and the metadata-types lookup
327+
328+
Every projected type, plus every proxy type for a custom-mapped type, is tagged with the parameterless, implementation-only **`[WindowsRuntimeType]`** marker (`Attributes/WindowsRuntimeTypeAttribute.cs`). The runtime and the build tools only ever check for the *presence* of this marker to decide whether a type participates in Windows Runtime marshalling — they never read any per-type metadata value at runtime. Proxy types are distinguished from projected types by `[WindowsRuntimeMappedType]` (which proxies carry to point at their public type); the `System.EventHandler` proxy intentionally stays unmarked, as it is a pure custom type rather than a real Windows Runtime metadata type.
329+
330+
The mapping from a projected type to its source `.winmd` module name (its "contract"/"stem") — needed only by build-time tooling (the interop and WinMD generators) — is **not** stored on each type. Instead, the projection generator emits a single centralized, fully trimmable lookup type, **`ABI.WindowsRuntimeMetadataTypes`**, carrying one **`[WindowsRuntimeMetadata(typeof(T), "stem")]`** entry per projected type (`Attributes/WindowsRuntimeMetadataAttribute.cs`, repurposed to a `(Type, string)`, `class`-targeting, `AllowMultiple` attribute). This mirrors how `ABI.WindowsRuntimeDefaultInterfaces` centralizes default interfaces, and lets the metadata be dead-code-eliminated when unused. Both the marker and the lookup type are implementation-only and are stripped from reference projections. `WinRT.Runtime`'s own manually-projected types do not contribute to a lookup type: the interop generator addresses them with the well-known `#CsWinRT` assembly identifier, and the WinMD generator reads their contract from the real `[ContractVersion]` metadata in the `WinRT.Runtime` reference assembly.
331+
309332
### 2. WinRT.SourceGenerator2 (`src/Authoring/WinRT.SourceGenerator2/`)
310333

311334
A Roslyn incremental source generator and diagnostic analyzer package. Runs at **design time** (IntelliSense) and **build time**. It is intentionally lightweight — heavy codegen is deferred to the post-build CLI tools.
@@ -618,7 +641,7 @@ A small build project that produces **`WindowsRuntime.Internal.winmd`** — the
618641

619642
**Project settings:**
620643

621-
- **Target**: `net10.0-windows10.0.26100.1` (the `.1` TFM revision selects the `cswinrt3` Windows SDK projection reference assemblies, which carry `[WindowsRuntimeMetadata]` attributes the WinMD generator reads)
644+
- **Target**: `net10.0-windows10.0.26100.1` (the `.1` TFM revision selects the `cswinrt3` Windows SDK projection reference assemblies, which carry the `[ContractVersion]` (and `[WindowsRuntimeType]`) Windows Runtime metadata the WinMD generator reads)
622645
- **Assembly name**: `WindowsRuntime.Internal`
623646
- **Nullable**: `disable` (the Windows Runtime type system does not support nullability annotations)
624647
- **WindowsSdkPackageVersion**: pinned (e.g. `10.0.26100.85-preview`) so the .NET SDK adds the implicit framework reference to the matching `Microsoft.Windows.SDK.NET.Ref` package
@@ -669,7 +692,7 @@ The MSBuild integration is orchestrated through several `.props` and `.targets`
669692
- **Compiler strict mode**: `<Features>strict</Features>` in all projects
670693
- **XML documentation**: generated for all projects
671694
- **`SkipLocalsInit`**: enabled in runtime and build tools for performance
672-
- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes), `CSWINRT3001` (obsolete internal members)
695+
- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes)
673696
- **Strong-name signing**: all assemblies signed with `src/WinRT.Runtime2/key.snk`
674697

675698
### Naming conventions
@@ -713,7 +736,7 @@ All five .NET build tools (`cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `c
713736
| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001``0014`, `9999` |
714737
| Interop Generator | `CSWINRTINTEROPGENxxxx` | `0001``0100`, `9999` |
715738
| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001``0010`, `9999` |
716-
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` |
739+
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` (deriving from `WindowsRuntimeObject`), `CSWINRT3002` (type map group types), `CSWINRT3003` (component authoring attributes), `CSWINRT3004` (`WindowsRuntimeReferenceAssemblyAttribute`) |
717740

718741
---
719742

0 commit comments

Comments
 (0)