Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c5d389a
Add isolated infrastructure for end-to-end smoke tests
Sergio0694 Jun 18, 2026
4fdad94
Add consumption end-to-end smoke test
Sergio0694 Jun 18, 2026
7feaf70
Add authoring end-to-end smoke test
Sergio0694 Jun 18, 2026
2e00052
Build and run smoke tests in local and CI builds
Sergio0694 Jun 18, 2026
06ce13f
Verify the authoring smoke test produces a .winmd defining the expect…
Sergio0694 Jun 18, 2026
73e8523
Trim comments and whitespace in smoke tests
Sergio0694 Jun 18, 2026
de07e31
Centralize shared smoke test configuration in Directory.Build.props
Sergio0694 Jun 18, 2026
48ad594
Document the smoke tests and refresh test documentation
Sergio0694 Jun 18, 2026
a50f9b9
Use the CsWinRTDependencies feed for smoke test restore
Sergio0694 Jun 19, 2026
1a4c14e
Keep type map group types in the WinRT.Runtime reference assembly
Sergio0694 Jun 19, 2026
ac10bd6
Run each smoke test as its own CI step with a final failure gate
Sergio0694 Jun 19, 2026
dba8342
Compile generated projections against the WinRT.Runtime implementatio…
Sergio0694 Jun 19, 2026
fb55354
Make the WinRT.Runtime reference assembly swap an explicit opt-out
Sergio0694 Jun 20, 2026
9b524b3
Expose the authoring component assembly attributes in the reference a…
Sergio0694 Jun 20, 2026
b1e1f90
Move vtable types to runtime and add assembly attributes
Sergio0694 Jun 20, 2026
9af3bda
Remove unsupported-marshalling remarks from enums
Sergio0694 Jun 20, 2026
56a156d
Skip the reference projection generator for input-less authored compo…
Sergio0694 Jun 21, 2026
de23c27
Set DisableRuntimeMarshalling for the smoke test projects
Sergio0694 Jun 21, 2026
4f46621
Run the smoke tests on both CoreCLR and Native AOT
Sergio0694 Jun 21, 2026
7e4a8bf
Document the public-but-hidden implementation-only type strategy
Sergio0694 Jun 21, 2026
3ffdbc4
Cover the public-but-hidden implementation-only type strategy in the …
Sergio0694 Jun 21, 2026
4955aa6
Include RestrictedErrorInfoExceptionMarshaller
Sergio0694 Jun 21, 2026
956937e
Add a projection smoke test for the real NuGet package
Sergio0694 Jun 21, 2026
55ba53b
Make WindowsRuntimeReferenceAssemblyAttribute public-but-hidden (CSWI…
Sergio0694 Jun 21, 2026
68a47fd
Stop reference projections from referencing implementation-only types
Sergio0694 Jun 22, 2026
d7f00d4
Broaden the authoring smoke test to cover more projection shapes
Sergio0694 Jun 22, 2026
695c9c6
Recognize reference projection types in interop generator discovery
Sergio0694 Jun 22, 2026
e29c0d3
Recover Windows Runtime metadata name from the implementation projection
Sergio0694 Jun 22, 2026
3baab03
Emit XAML struct metadata attributes only for implementation projections
Sergio0694 Jun 22, 2026
2bf66d0
Emit GetDefaultInterface helper only for implementation projections
Sergio0694 Jun 22, 2026
3d490e5
Clarify runtime class shapes in Thermometer test
Sergio0694 Jun 22, 2026
8927d4b
Guard implementation-only TokenizerHelper usage in reference projections
Sergio0694 Jun 22, 2026
43ea99b
Guard implementation-only DispatcherQueueSynchronizationContext wrappers
Sergio0694 Jun 22, 2026
efe226d
Stub mapped-interface members in reference projections
Sergio0694 Jun 22, 2026
d2ebe9c
Make the synthetic ref-mode ctor accessible to derived projected classes
Sergio0694 Jun 22, 2026
eba4de3
Emit accessible parameterless ctor for unsealed composable classes in…
Sergio0694 Jun 22, 2026
b786281
Guard implementation-only storage-extension bodies in reference proje…
Sergio0694 Jun 22, 2026
c140feb
Add Windows SDK and XAML reference-projection smoke tests
Sergio0694 Jun 22, 2026
3d84e8a
Build the Windows SDK projection smoke tests as plain net10.0
Sergio0694 Jun 23, 2026
2590815
Simplify over-escaped interpolated raw strings in MappedInterfaceStub…
Sergio0694 Jun 23, 2026
038b42c
Update testing skill for the Windows SDK projection smoke tests
Sergio0694 Jun 23, 2026
e44bac4
Use <see> tag for TypeDefinition in docs
Sergio0694 Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ WinRT.Runtime2/

- **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`.
- **`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).
- **`[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/`). 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).
- **`[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).
- **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:
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).
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.
- **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`).
- **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`.
- **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.
Expand Down Expand Up @@ -727,7 +730,7 @@ All five .NET build tools (`cswinrtprojectionrefgen`, `cswinrtprojectiongen`, `c
| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0014`, `9999` |
| Interop Generator | `CSWINRTINTEROPGENxxxx` | `0001`–`0097`, `9999` |
| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0010`, `9999` |
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` (deriving from `WindowsRuntimeObject`) |
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` (deriving from `WindowsRuntimeObject`), `CSWINRT3002` (type map group types), `CSWINRT3003` (component authoring attributes), `CSWINRT3004` (`WindowsRuntimeReferenceAssemblyAttribute`) |

---

Expand Down
4 changes: 3 additions & 1 deletion .github/skills/interop-generator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ The generator processes two categories of assemblies:
- `System.Collections.Concurrent.ConditionalWeakTable<,>` — Memory semantics conflict

**Type inclusion criteria:**
- Must be a projected Windows Runtime type (marked with `[WindowsRuntimeMetadata]` or similar)
- Must be a projected Windows Runtime type. A type is recognized as projected in any of three ways: it carries the per-type `[WindowsRuntimeMetadata]` attribute (implementation projections and types in `WinRT.Runtime.dll`); it is a public type from an authored component assembly (`IsComponentWindowsRuntimeType`); or it is defined in a reference projection assembly marked `[WindowsRuntimeReferenceAssembly]` (`IsReferenceProjectionWindowsRuntimeType`). The latter two do **not** carry the per-type `[WindowsRuntimeMetadata]` attribute — reference projections shipped in NuGet packages have it stripped (it is an implementation-only attribute, absent from the `WinRT.Runtime.dll` reference assembly they compile against), so the interop generator recognizes them by their assembly-level marker instead.
- Generic types must be fully constructed (no open generic parameters)
- Type hierarchy must be fully resolvable (no missing dependencies)
- Must not be a managed-only type (types that never cross the Windows Runtime boundary)
Expand Down Expand Up @@ -737,6 +737,8 @@ Almost everything the generated code calls into lives in `WinRT.Runtime.dll` as

Because these APIs are absent from the `WinRT.Runtime.dll` reference assembly, the generated `WinRT.Interop.dll` references types that exist only in the implementation assembly, and the generator emits assembly-level `[IgnoresAccessChecksTo]` to reach the non-public members among them. This is also why `cswinrtinteropgen` must be version-matched to the `WinRT.Runtime.dll` it targets (see "Version compatibility" above): the shape of these types can change at any time.

> **Note:** A small subset of the types listed above is not stripped but instead kept **public but hidden** in the reference assembly — most notably the three type map group types, which are marked reference-assembly-only `[Obsolete(DiagnosticId = "CSWINRT3002")]` + `[EditorBrowsable(Never)]`. The reason: those types are also named by the CsWinRT source generator's `[assembly: TypeMapAssemblyTarget<TGroup>]` output, which is compiled into user code **against the reference assembly**, so stripping them would break that compilation. The interop generator is unaffected by the distinction (it always resolves the implementation assembly), but it must still treat them as unstable, version-matched implementation details. See the "Two strategies for implementation-only API" note in `.github/copilot-instructions.md` for the full rationale and the other cases (`CSWINRT3003`, `CSWINRT3004`).

## Key patterns and conventions

### Naming conventions for generated types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ These are the well-known assemblies and their compact identifiers:

Compact identifiers are prefixed with `#` to distinguish them from user-defined assembly names.

For types not belonging to any well-known assembly, the implementation also checks for a `[WindowsRuntimeMetadata]` attribute on the resolved type definition. If the attribute is present, the Windows Runtime metadata name from the attribute is used as the assembly identifier instead of the actual assembly name. This allows types carrying WinRT metadata to be identified by their canonical Windows Runtime name rather than the .NET assembly they happen to live in. If the attribute is not present, the raw assembly name is used as-is.
For types not belonging to any well-known assembly, the implementation also derives the assembly identifier from the type's `[WindowsRuntimeMetadata]` value (the source `.winmd` module name, i.e. its "stem") instead of the actual assembly name. This allows types carrying WinRT metadata to be identified by their canonical Windows Runtime name rather than the .NET assembly they happen to live in. The stem is read directly from the attribute when present — implementation projections, the authored component projection, and manually projected types in `WinRT.Runtime.dll` all carry it. Reference projections shipped in Windows Runtime projection NuGet packages have the attribute **stripped** (it is an implementation-only attribute, absent from the `WinRT.Runtime.dll` reference assembly they compile against); for a type defined in such a reference projection (detected via `IsReferenceProjectionWindowsRuntimeType`), the stem is instead recovered from the matching type in the third-party implementation projection (`WinRT.Projection.dll`), located through `RuntimeContext.GetLoadedAssemblies()`. This recovery is essential because the projection writer encodes that very same stem into the `[UnsafeAccessorType]` references it emits into the implementation projection, and the reference projection's own assembly name can differ from the stem (e.g. when several `.winmd` files are merged into a single projection assembly, as with WinUI / Windows App SDK). If the stem cannot be found anywhere, the raw assembly name is used as-is. The recovery logic lives in `GetWindowsRuntimeMetadataName` (`src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs`).

> [!NOTE]
> Not all BCL types live in `System.Runtime`. For example, the `System.Numerics` types (`Matrix3x2`, `Matrix4x4`, `Plane`, `Quaternion`, `Vector2`, `Vector3`, `Vector4`) are in the `System.Numerics.Vectors` assembly, so their assembly identifier is `System-Numerics-Vectors` (not `#corlib`) after the `.` → `-` substitution. The assembly used is always the one from the type's actual metadata scope, not the namespace.
Expand Down
Loading