Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
abe3808
Add WindowsRuntimeImplementationOnlyMemberAttribute
Sergio0694 Apr 16, 2026
f29f9a7
Use [WindowsRuntimeImplementationOnlyMember] for implementation-only …
Sergio0694 Apr 16, 2026
cd74794
Remove WindowsRuntimeConstants.cs
Sergio0694 Apr 16, 2026
91328fa
Remove CSWINRT3001 NoWarn suppression
Sergio0694 Apr 16, 2026
8d28af0
Remove AD0001 NoWarn workaround
Sergio0694 Apr 16, 2026
0263523
Update WinRT.Runtime.csproj
Sergio0694 Apr 16, 2026
e868734
Split WindowsRuntimeObject into partial class
Sergio0694 Apr 16, 2026
07d8731
Remove impl-only files from ref assembly build via attribute scan
Sergio0694 Apr 16, 2026
e74cd26
Exclude ABI types from reference assemblies
Sergio0694 Apr 17, 2026
3d44e59
Guard attribute usages of impl-only types with WINDOWS_RUNTIME_IMPLEM…
Sergio0694 Apr 17, 2026
d21d0b4
Strip SupportedOSPlatform and ContractVersion from impl assembly for …
Sergio0694 Apr 17, 2026
a70b56b
Unconditionally apply SupportedOSPlatform attribute
Sergio0694 Apr 17, 2026
0fad3cd
Conditional metadata imports and define impl constant
Sergio0694 Apr 17, 2026
28f5e6b
Add implementation-only marker and exclusions
Sergio0694 Apr 17, 2026
355c053
Stub method bodies in reference assembly with throw null
Sergio0694 Apr 17, 2026
34c6802
Add StructLayout attributes and suppress CS1574
Sergio0694 Apr 17, 2026
6726a39
Update WinRT.Runtime for ref assemblies
Sergio0694 Mar 30, 2026
9dc65c8
Delete CSWINRT3001 diagnostic documentation
Sergio0694 Mar 30, 2026
c257126
Update instructions for reference assembly approach
Sergio0694 Mar 30, 2026
934e7e6
Document private implementation detail APIs in interop generator skill
Sergio0694 Mar 30, 2026
b9d00bd
Correct reference assembly mechanism in skills/instructions
Sergio0694 Apr 17, 2026
34f28ea
Exclude Exceptions folder from build
Sergio0694 Apr 19, 2026
1f69db5
Strip WindowsRuntimeClassName attribute from reference assembly
Sergio0694 Apr 19, 2026
efda739
Merge adjacent ifdef blocks where attribute order is preserved
Sergio0694 Apr 19, 2026
7df976b
Put REFERENCE_ASSEMBLY branch first in conditional attribute blocks
Sergio0694 Apr 19, 2026
0805548
Put [Guid] attribute first on interfaces and merge ifdef blocks
Sergio0694 Apr 19, 2026
b89f76a
Refactor WinRT metadata attributes and conditionals
Sergio0694 Apr 27, 2026
73f42a2
Skip emitting [ApiContract] attribute for implementation assemblies
Sergio0694 Apr 27, 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
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ The runtime library (`WinRT.Runtime.dll`) provides all common infrastructure for
- **Warnings as errors**: release only. `EnforceCodeStyleInBuild` enabled, `AnalysisLevelStyle` = `latest-all`.
- **Strong-name signed** with `key.snk`
- **AOT compatible**: `IsAotCompatible = true`
- **Reference assembly**: the project is built twice for NuGet packaging. With `CsWinRTBuildReferenceAssembly=true`, it produces a reference assembly (for `ref\net10.0\` in the NuGet) that strips all private implementation detail types and members. The normal build produces the full implementation assembly (for `lib\net10.0\`) and defines `WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY`; the reference assembly build defines `WINDOWS_RUNTIME_REFERENCE_ASSEMBLY` instead. Stripping is driven primarily by the `[WindowsRuntimeImplementationOnlyMember]` attribute (a `[Conditional("WINDOWS_RUNTIME_REFERENCE_ASSEMBLY")]` marker on top-level types) plus an MSBuild target that removes any source file containing that attribute, any file with a top-of-file `#define WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE`, and entire implementation-only folders (e.g. `ABI\`, `NativeObjects\`, most `InteropServices\` subfolders). Method bodies remaining in the reference assembly are stubbed with `throw null`. This replaces the previous approach of marking implementation details with `[Obsolete]` and `[EditorBrowsable(Never)]` attributes.

**Directory structure:**

Expand Down Expand Up @@ -554,7 +555,7 @@ The MSBuild integration is orchestrated through several `.props` and `.targets`
- **Compiler strict mode**: `<Features>strict</Features>` in all projects
- **XML documentation**: generated for all projects
- **`SkipLocalsInit`**: enabled in runtime and build tools for performance
- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes), `CSWINRT3001` (obsolete internal members)
- **Suppressed warnings**: `CS8500` (ref safety in unsafe contexts), `AD0001` (analyzer crashes)
- **Strong-name signing**: all assemblies signed with `src/WinRT.Runtime2/key.snk`

### C++ project (cswinrt)
Expand Down Expand Up @@ -597,7 +598,6 @@ All four .NET build tools (`cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtint
| Projection Generator | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` |
| Interop Generator | `CSWINRTINTEROPGENxxxx` | Various, `9999` |
| WinMD Generator | `CSWINRTWINMDGENxxxx` | `0001`–`0007` |
| Runtime (obsolete markers) | `CSWINRT3xxx` | `CSWINRT3001` |

---

Expand Down
19 changes: 18 additions & 1 deletion .github/skills/interop-generator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,24 @@ The `WinRT.Interop.dll` assembly is produced by `cswinrtinteropgen`, which is a
- If a project **does not** reference CsWinRT, then `cswinrtinteropgen` is loaded from the Windows SDK projections targeting pack.
- If CsWinRT **is** referenced (directly or transitively), then `cswinrtinteropgen` is loaded from that package, but only if the `WinRT.Runtime.dll` binary from that package has a higher version than the one in the Windows SDK projections package being referenced. This correctly handles cases where a dependent project might have a reference to an outdated CsWinRT package.

This version matching is critical because `cswinrtinteropgen` relies on "implementation details only" APIs in `WinRT.Runtime.dll` — APIs which are public, hidden, and marked as `[Obsolete]`, and which are exclusively meant to be consumed by generated code produced by `cswinrtinteropgen`. These APIs might change at any time without following semantic versioning for CsWinRT. For instance, they are crucial to support marshalling generic Windows Runtime collection interfaces, and the code in `WinRT.Interop.dll` makes heavy use of them in this scenario.
This version matching is critical because `cswinrtinteropgen` relies on internal implementation detail APIs in `WinRT.Runtime.dll` — APIs which are public in the implementation assembly but excluded from the reference assembly (via the `[WindowsRuntimeImplementationOnlyMember]` attribute and related MSBuild stripping, controlled by the `WINDOWS_RUNTIME_REFERENCE_ASSEMBLY` / `WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY` define constants), and which are exclusively meant to be consumed by generated code produced by `cswinrtinteropgen`. These APIs might change at any time without following semantic versioning for CsWinRT. For instance, they are crucial to support marshalling generic Windows Runtime collection interfaces, and the code in `WinRT.Interop.dll` makes heavy use of them in this scenario.

### Private implementation detail APIs

`WinRT.Runtime.dll` is shipped as two assemblies in the CsWinRT NuGet package: a **reference assembly** (in `ref\net10.0\`) that exposes only the versioned public API surface, and an **implementation assembly** (in `lib\net10.0\`) that contains the full set of types and members. Many types in the implementation assembly are "public" only because generated code in other assemblies (produced by `cswinrt.exe` and `cswinrtinteropgen`) needs to call them — they are not part of the stable API contract. These types are stripped from the reference assembly via the `[WindowsRuntimeImplementationOnlyMember]` attribute (a `[Conditional("WINDOWS_RUNTIME_REFERENCE_ASSEMBLY")]` marker that triggers an MSBuild target to remove the entire source file), an opt-in `#define WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE` directive at the top of a source file, or wholesale folder removals (e.g. `ABI\`, `NativeObjects\`, most `InteropServices\` subfolders), so consumers never see them. Method bodies that remain in the reference assembly are stubbed with `throw null`.

The interop generator receives a reference to the **implementation** `WinRT.Runtime.dll` (not the reference assembly) so that it can resolve and emit calls to these internal APIs. Examples of private implementation detail types that the interop generator uses heavily include:

- **Vtable structs** (`IVectorVftbl`, `IMapVftbl`, `IIterableVftbl`, etc.) — the interop generator emits static readonly vtable fields for each projected interface, populated with function pointers to marshalling stubs.
- **Collection adapters and methods** (`IListAdapter<T>`, `IDictionaryMethods<TKey, TValue>`, `IEnumerableMethods<T>`, etc.) — generated native object wrappers delegate collection operations to these adapter types.
- **Marshalling infrastructure** (`WindowsRuntimeObjectMarshaller`, `HStringMarshaller`, `WindowsRuntimeInterfaceMarshaller<T>`, array marshallers, etc.) — generated marshalling stubs call these to convert between managed and native representations.
- **Object reference types** (`WindowsRuntimeObjectReference`, `WindowsRuntimeObjectReferenceValue`, `CreateObjectReferenceMarshalingType`) — generated code uses these for COM pointer lifecycle management.
- **ComWrappers callbacks** (`IWindowsRuntimeObjectComWrappersCallback`, `WindowsRuntimeComWrappersMarshal`) — generated CCW vtable entries and type map registrations use these.
- **Type map groups** (`WindowsRuntimeComWrappersTypeMapGroup`, `WindowsRuntimeMetadataTypeMapGroup`, `DynamicInterfaceCastableImplementationTypeMapGroup`) — generated type map registrations target these groups.
- **Projection implementations** (`IPropertyValueImpl`, `IStringableImpl`, `IMarshalImpl`, etc.) — generated code wires up these implementations in CCW vtables.
- **ABI types** (under `ABI.*` namespaces) — per-type marshalling definitions and event source types.

All of these types are referenced extensively throughout the interop generator's builder and factory classes (in `Builders/` and `Factories/`). When modifying these types in `WinRT.Runtime`, the corresponding interop generator code must be updated in lockstep — which is why the version matching described above is essential.

## Project settings

Expand Down
1 change: 1 addition & 0 deletions .github/skills/update-copilot-instructions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Launch parallel explore agents for each of the 8 CsWinRT 3.0 projects listed in
- Key types listed still exist and have the described purposes
- T4 templates (`.tt` files) are accurately listed
- Project settings (TFM, language version, nullable, unsafe, etc.) are current
- Reference assembly build setup (`CsWinRTBuildReferenceAssembly`, `WINDOWS_RUNTIME_REFERENCE_ASSEMBLY` / `WINDOWS_RUNTIME_IMPLEMENTATION_ASSEMBLY` defines, `[WindowsRuntimeImplementationOnlyMember]` attribute, `#define WINDOWS_RUNTIME_IMPLEMENTATION_ONLY_FILE` opt-out) is accurately described
- Namespace organization matches

2. **WinRT.SourceGenerator2 (`src/Authoring/WinRT.SourceGenerator2/`)**
Expand Down
19 changes: 19 additions & 0 deletions build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,25 @@ steps:
WinRT.Runtime.xml
TargetFolder: $(StagingFolder)\net10.0

# Build WinRT.Runtime reference assembly with implementation detail types stripped out
- task: VSBuild@1
displayName: Build WinRT.Runtime reference assembly
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
inputs:
solution: $(Build.SourcesDirectory)\src\WinRT.Runtime2\WinRT.Runtime.csproj
msbuildArgs: /p:VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),CsWinRTBuildReferenceAssembly=true,ContinuousIntegrationBuild=true
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)

# Stage WinRT.Runtime reference assembly
- task: CopyFiles@2
displayName: Stage WinRT.Runtime reference assembly
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
inputs:
SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime2\obj\$(BuildPlatform)\$(BuildConfiguration)\net10.0\ref
Contents: WinRT.Runtime.dll
TargetFolder: $(StagingFolder)\net10.0\ref

# Stage WinRT.Host.Shim
- task: CopyFiles@2
displayName: Stage WinRT.Host.Shim
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ steps:
command: pack
searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec
configurationToPack: Release
buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion)
buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WindowsRuntime.Internal.winmd;net10_runtime=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.dll;net10_runtime_xml=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Runtime.xml;net10_runtime_ref=$(Build.SourcesDirectory)\release_x86\net10.0\ref\WinRT.Runtime.dll;source_generator=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net10.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;cswinrtinteropgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtinteropgen.exe;cswinrtinteropgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtinteropgen.exe;cswinrtimplgen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtimplgen.exe;cswinrtimplgen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtimplgen.exe;cswinrtprojectiongen_x64=$(Build.SourcesDirectory)\release_x64\net10.0\native\cswinrtprojectiongen.exe;cswinrtprojectiongen_arm64=$(Build.SourcesDirectory)\release_arm64\net10.0\native\cswinrtprojectiongen.exe;run_cswinrt_generator_task=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Generator.Tasks.dll;branch=$(Build.SourceBranchName);commit=$(Build.SourceVersion)
packDestination: $(ob_outputDirectory)\packages

- task: NuGetCommand@2
Expand Down
34 changes: 0 additions & 34 deletions docs/diagnostics/cswinrt30001.md

This file was deleted.

1 change: 1 addition & 0 deletions nuget/Microsoft.Windows.CsWinRT.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<file src="readme.txt"/>
<file src="$net10_runtime$" target="lib\net10.0\"/>
<file src="$net10_runtime_xml$" target="lib\net10.0\"/>
<file src="$net10_runtime_ref$" target="ref\net10.0\"/>
<file src="$source_generator$" target="analyzers\dotnet\cs\"/>
<file src="$winrt_host_x64$" target ="hosting\x64\native"/>
<file src="$winrt_host_x86$" target ="hosting\x86\native"/>
Expand Down
8 changes: 2 additions & 6 deletions src/WinRT.Runtime2/ABI/System/Boolean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Foundation;
Expand Down Expand Up @@ -43,10 +42,7 @@ namespace ABI.System;
/// <summary>
/// Marshaller for <see cref="bool"/>.
/// </summary>
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
[WindowsRuntimeImplementationOnlyMember]
public static unsafe class BooleanMarshaller
{
/// <inheritdoc cref="WindowsRuntimeValueTypeMarshaller.BoxToUnmanaged{T}(T?, CreateComInterfaceFlags, in Guid)"/>
Expand Down Expand Up @@ -288,4 +284,4 @@ private static HRESULT get_Type(void* thisPtr, PropertyType* value)

return WellKnownErrorCodes.S_OK;
}
}
}
8 changes: 2 additions & 6 deletions src/WinRT.Runtime2/ABI/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Foundation;
Expand Down Expand Up @@ -43,10 +42,7 @@ namespace ABI.System;
/// <summary>
/// Marshaller for <see cref="byte"/>.
/// </summary>
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
[WindowsRuntimeImplementationOnlyMember]
public static unsafe class ByteMarshaller
{
/// <inheritdoc cref="WindowsRuntimeValueTypeMarshaller.BoxToUnmanaged{T}(T?, CreateComInterfaceFlags, in Guid)"/>
Expand Down Expand Up @@ -456,4 +452,4 @@ private static HRESULT GetDouble(void* thisPtr, double* value)
return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e);
}
}
}
}
Loading
Loading