Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
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 @@ -167,6 +167,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 via `#if !REFERENCE_ASSEMBLY`. The normal build produces the full implementation assembly (for `lib\net10.0\`). This replaces the previous approach of marking implementation details with `[Obsolete]` and `[EditorBrowsable(Never)]` attributes.

**Directory structure:**

Expand Down Expand Up @@ -506,7 +507,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 @@ -548,7 +549,6 @@ All three .NET build tools (`cswinrtimplgen`, `cswinrtprojectiongen`, `cswinrtin
| Impl Generator | `CSWINRTIMPLGENxxxx` | `0001`–`0010`, `9999` |
| Projection Generator | `CSWINRTPROJECTIONGENxxxx` | `0001`–`0008`, `9999` |
| Interop Generator | `CSWINRTINTEROPGENxxxx` | Various, `9999` |
| 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 `#if !REFERENCE_ASSEMBLY`), 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 `#if !REFERENCE_ASSEMBLY`, so consumers never see them.

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`, `#if !REFERENCE_ASSEMBLY`) 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
9 changes: 3 additions & 6 deletions src/WinRT.Runtime2/ABI/System/Boolean.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

return WellKnownErrorCodes.S_OK;
}
}
}
#endif
9 changes: 3 additions & 6 deletions src/WinRT.Runtime2/ABI/System/Byte.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#if !REFERENCE_ASSEMBLY
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Foundation;
Expand Down Expand Up @@ -43,10 +43,6 @@ namespace ABI.System;
/// <summary>
/// Marshaller for <see cref="byte"/>.
/// </summary>
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static unsafe class ByteMarshaller
{
/// <inheritdoc cref="WindowsRuntimeValueTypeMarshaller.BoxToUnmanaged{T}(T?, CreateComInterfaceFlags, in Guid)"/>
Expand Down Expand Up @@ -456,4 +452,5 @@ private static HRESULT GetDouble(void* thisPtr, double* value)
return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e);
}
}
}
}
#endif
9 changes: 3 additions & 6 deletions src/WinRT.Runtime2/ABI/System/Char.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

return WellKnownErrorCodes.S_OK;
}
}
}
#endif
Loading
Loading