Project Windows Runtime struct fields as C# fields - #2505
Open
Sergio0694 wants to merge 5 commits into
Open
Conversation
Projected structs were emitting each Windows Runtime struct field as a
'{ readonly get; set; }' auto-property. That turned out to be problematic,
most notably for authoring: the WinMD generator maps public instance fields
back to Windows Runtime struct fields, so an authored struct written against
the projected shape produced an empty struct in the generated metadata. It
also prevents callers from taking a reference to a member, and is simply
confusing, given Windows Runtime structs are plain data.
Emit plain public fields instead, declared before the constructor so that
declaration order (which drives the sequential layout of the struct) mirrors
the metadata order.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d136d263-1a79-45e8-8a92-fb6dd28b2d79
'Point', 'Rect', 'Size' and 'EventRegistrationToken' are manually projected in
'WinRT.Runtime', and were mirroring the Windows Runtime struct fields as
'{ readonly get; set; }' properties. Expose them as plain public fields, to
match how projected structs are now generated.
The negative-value validation that lived in the 'Rect.Width'/'Rect.Height' and
'Size.Width'/'Size.Height' setters moves into the constructors that document it
(this is also what CsWinRT 2.x did). The remaining members ('Left', 'Top',
'Right', 'Bottom', 'IsEmpty', 'Empty', ...) are managed-only conveniences and
stay as properties.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d136d263-1a79-45e8-8a92-fb6dd28b2d79
The custom-mapped XAML struct types are defined in full by their addition
files, and mirrored the Windows Runtime struct fields as properties over
private backing fields (a shape inherited from the UWP/WPF managed
projections). Now that generated structs project their fields as C# fields,
these would otherwise be inconsistent with their siblings: 'Thickness',
'Matrix', 'GeneratorPosition' and 'Color' are generated types (so their
members are fields), while 'CornerRadius' and friends are hand-written.
Expose the actual metadata fields as public fields for:
- 'CornerRadius': TopLeft, TopRight, BottomRight, BottomLeft
- 'GridLength': Value, GridUnitType
- 'Duration': TimeSpan, Type
- 'KeyTime': TimeSpan
- 'RepeatBehavior': Count, Duration, Type
- 'Matrix3D': M11-M34, OffsetX, OffsetY, OffsetZ, M44
Field declaration order (and therefore the sequential layout each of these
blittable structs marshals with) is unchanged. Managed-only members are left
untouched: 'GridLength.IsAbsolute'/'IsAuto'/'IsStar'/'Auto',
'Duration.HasTimeSpan'/'Automatic'/'Forever', 'RepeatBehavior.HasCount'/
'HasDuration'/'Forever', 'Matrix3D.Identity'/'IsIdentity'/'HasInverse'/... and
all the operators, conversions and formatting helpers.
Validation that lived in the removed property setters is preserved where the
constructors already performed it ('CornerRadius.Validate', 'GridLength',
'KeyTime.FromTimeSpan'). 'GridLength' and 'KeyTime' are no longer 'readonly
struct'-s, since Windows Runtime structs are mutable data.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d136d263-1a79-45e8-8a92-fb6dd28b2d79
Refreshes the 'EventRegistrationToken' snippet in the event infrastructure doc, and documents the projection rule in the CsWinRT 3.0 spec (renaming the neighbouring 'Point'/'Rect'/'Size' section, which was already about their fields rather than properties). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d136d263-1a79-45e8-8a92-fb6dd28b2d79
Both comments justified skipping ABI struct emission / per-field marshalling for mapped structs by claiming their projected public fields don't match the WinMD field layout. That is no longer accurate now that the addition files expose the metadata fields directly. The actual reason is that these types are defined in full by an addition file rather than generated from metadata, so they're passed through by value and only get BoxToUnmanaged/UnboxToManaged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d136d263-1a79-45e8-8a92-fb6dd28b2d79
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Sergio0694
marked this pull request as ready for review
August 1, 2026 22:14
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Projects the fields of Windows Runtime struct types as C# fields instead of
{ readonly get; set; }properties, across the projection writer, the manually projected types inWinRT.Runtime, and the custom-mapped XAML types defined by addition files.Motivation
Projecting struct fields as properties turned out to be problematic, and confusing.
The biggest issue is authoring.
cswinrtwinmdgen.exemaps the public instance fields of an authoredstructto Windows Runtime struct fields, which is the only shape that can round-trip: an author writing a struct against the projected shape (properties) would silently produce an empty struct in the generated.winmd. Projecting fields as fields makes consumption and authoring symmetrical: the shape you consume is exactly the shape you author.Beyond authoring, Windows Runtime structs are plain data — there is no accessor to run any logic behind, so a property only obscures that, and it prevents callers from taking a reference to a member (e.g. to pass it as a
refargument, or to mutate an element of an array of structs in place). This also matches what C++/WinRT does, and what CsWinRT 2.x did for generated struct projections.Changes
Projection writer
src/WinRT.Projection.Writer/Builders/ProjectionFileBuilder.cs:WriteStructnow emits a public field per Windows Runtime struct field, declared before the constructor so declaration order (which drives the sequential layout of the struct) mirrors the metadata order.src/WinRT.Projection.Writer/Factories/AbiStructFactory.cs,src/WinRT.Projection.Writer/Factories/StructEnumMarshallerFactory.cs: refresh comments that justified the mapped-struct special cases by claiming their projected public fields don't match the WinMD field layout, which is no longer accurate.Manually projected types
src/WinRT.Runtime2/Windows.Foundation/Point.cs,Rect.cs,Size.cs,src/WinRT.Runtime2/InteropServices/Events/EventRegistrationToken.cs:X,Y,Width,HeightandValuebecome public fields. The negative-value validation that lived in theRect/Sizesetters moves into the constructors that document it (which is also what CsWinRT 2.x did). Managed-only members (Left,Top,Right,Bottom,IsEmpty,Empty, ...) are unchanged.Custom-mapped XAML types (addition files, both the
Microsoft.UI.XamlandWindows.UI.Xamlcopies)…/Microsoft.UI.Xaml.CornerRadius.cs:TopLeft,TopRight,BottomRight,BottomLeft…/Microsoft.UI.Xaml.GridLength.cs:Value,GridUnitType…/Microsoft.UI.Xaml.Duration.cs:TimeSpan,Type…/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs:TimeSpan…/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs:Count,Duration,Type…/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs:M11-M34,OffsetX,OffsetY,OffsetZ,M44These types are defined in full by their addition files, so they kept the property-over-private-backing-field shape inherited from the UWP/WPF managed projections. Leaving them alone would have made them inconsistent with their own siblings, since
Thickness,Matrix,GeneratorPositionandColorare generated types in CsWinRT 3.0 and therefore now expose fields. Field declaration order (and therefore the sequential layout each of these blittable structs marshals with) is unchanged, and validation that lived in the removed setters is preserved where the constructors already performed it.GridLengthandKeyTimeare no longerreadonly struct-s, since Windows Runtime structs are mutable data. Members that are not Windows Runtime struct fields are left untouched (GridLength.IsAbsolute/IsAuto/IsStar/Auto,Duration.HasTimeSpan/Automatic/Forever,RepeatBehavior.HasCount/HasDuration/Forever,Matrix3D.Identity/IsIdentity/HasInverse, and all operators, conversions and formatting helpers).Tests and docs
src/Tests/ProjectionWriterTest/Test_ProjectedStructs.cs: new tests asserting that projected struct fields are emitted as C# fields, and that no projected struct member is emitted as an auto-property, in both projection modes.docs/cswinrt3.0-spec.md: documents the projection rule, and renames the neighbouringPoint/Rect/Sizesection (which was already about their fields rather than properties).docs/event-infrastructure.md: refreshes theEventRegistrationTokensnippet.Validation
ProjectionWriterTest(16),WinMDGeneratorTest(17) andSourceGenerator2Test(129) all pass.Windows,Windows.UI.XamlandWinAppSDKprojections were regenerated end-to-end and compile cleanly, which covers the emitted structs, their marshallers, and all of the addition files.