Skip to content

Add end-to-end smoke tests for the CsWinRT NuGet package#2456

Open
Sergio0694 wants to merge 42 commits into
staging/winrt-runtime-ref-assemblyfrom
user/sergiopedri/smoke-tests
Open

Add end-to-end smoke tests for the CsWinRT NuGet package#2456
Sergio0694 wants to merge 42 commits into
staging/winrt-runtime-ref-assemblyfrom
user/sergiopedri/smoke-tests

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Adds minimal, isolated end-to-end smoke tests that consume the real Microsoft.Windows.CsWinRT NuGet package — a consumption app and an authoring component — to verify the package works correctly in a real production environment, independent of the repository's own build infrastructure.

Motivation

The CsWinRT 3.0 build pipeline is complex (reference projections, forwarder assemblies, several post-build generators, and the dual reference/implementation WinRT.Runtime assembly added in this branch). The existing unit and functional tests all build against the local build infrastructure (SimulateCsWinRTNugetReference, project references to the in-repo projections, etc.), so they never exercise the actual packaged ref/lib assemblies, the packaged generators, or the NuGet build targets the way a real customer consumes them. These smoke tests close that gap: they reference the packed NuGet package exactly like an external app or component author would, validating that the correct assemblies are referenced and that all generators run correctly end-to-end.

Changes

The smoke tests live under src/Tests/SmokeTests/ and are intentionally kept out of cswinrt.slnx (the package they consume only exists after the build packs it).

  • src/Tests/SmokeTests/Directory.Build.props / Directory.Build.targets: blank files that isolate the smoke tests from the repository build infrastructure, so they inherit nothing from src/Directory.Build.*.
  • src/Tests/SmokeTests/Directory.Packages.props: disables central package management for the smoke tests (and shadows the repository Directory.Packages.props), so each project pins the CsWinRT package version directly.
  • src/Tests/SmokeTests/.gitignore: ignores the default bin/obj output (these projects use the default output folders rather than the repo _build redirect).
  • src/Tests/SmokeTests/Consumption/: an Exe that calls JsonObject.Parse(...) then Stringify() from Windows.Data.Json, exercising the Windows SDK projection, the interop generator, and the WinRT.Runtime ref/impl assemblies.
  • src/Tests/SmokeTests/Authoring/: a CsWinRTComponent library exposing a minimal Greeter class, exercising WinMD generation, the reference projection, and the forwarder assembly.
  • src/Tests/SmokeTests/run-smoke-tests.ps1: builds and runs the consumption test (asserting a clean exit code), then builds the authoring test and verifies that the generated Authoring.winmd is a valid Windows Runtime metadata file defining Authoring.Greeter.
  • src/build.cmd: runs the smoke tests after the local nuget pack step (x64 only; skippable via cswinrt_run_smoke_tests=false).
  • build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml: runs the smoke tests after the CI NuGet pack step, against the freshly packed package.

Both projects override RestoreSources to restore the package being tested from the local build output (or the CI pack output) and everything else — notably the preview Windows SDK ref pack — from public NuGet. They use the real .NET SDK targeting pack via a pinned WindowsSdkPackageVersion and the .1 Windows TFM revision, mirroring src/WinRT.Internal. The package version and source default to the local build.cmd x64 Release output and are overridden by the build or CI that produced the package.

Validation

Verified end-to-end against a real CsWinRT 3.0 package using the committed configuration: the consumption test builds and runs (round-tripping the JSON to {"a":42} with a clean exit), and the authoring component's generated Authoring.winmd is confirmed to define Authoring.Greeter. In CI the same runner executes against the freshly packed package.

Sergio0694 and others added 6 commits June 18, 2026 12:20
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>
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>
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>
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>
…ed 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>
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.
@Sergio0694 Sergio0694 added testing Related to testing infrastructure CsWinRT 3.0 labels Jun 18, 2026
@Sergio0694 Sergio0694 requested a review from manodasanW June 18, 2026 20:46
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 83a4142 to 42e58d7 Compare June 18, 2026 20:55
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 42e58d7 to de07e31 Compare June 18, 2026 20:57
Sergio0694 and others added 7 commits June 18, 2026 14:52
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>
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>
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>
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>
…n 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>
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>
…ssembly (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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 85022f7 to 9b524b3 Compare June 20, 2026 20:33
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.
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.
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from a69dd92 to 9af3bda Compare June 21, 2026 00:30
Sergio0694 and others added 7 commits June 20, 2026 17:52
…nents

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>
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>
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>
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>
…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>
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 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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 3bc4fb3 to 956937e Compare June 21, 2026 18:01
…NRT3004)

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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 2e540ad to f2b9278 Compare June 22, 2026 01:07
Sergio0694 and others added 2 commits June 21, 2026 19:13
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>
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 7da8415 to d7f00d4 Compare June 22, 2026 02:19
Sergio0694 and others added 2 commits June 21, 2026 22:46
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>
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 1be69e0 to e29c0d3 Compare June 22, 2026 05:47
Sergio0694 and others added 9 commits June 22, 2026 12:02
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>
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>
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.
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>
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>
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>
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>
… 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>
…ctions

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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch 2 times, most recently from 0580ff7 to 5362e08 Compare June 23, 2026 00:03
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 5362e08 to c140feb Compare June 23, 2026 00:45
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>
@Sergio0694 Sergio0694 force-pushed the user/sergiopedri/smoke-tests branch from 456201f to 3d84e8a Compare June 23, 2026 04:01
Sergio0694 and others added 3 commits June 22, 2026 23:48
…Factory

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>
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>
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CsWinRT 3.0 testing Related to testing infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant