From d61676fc943b950a87892ee42eb6d46f1bc15c3c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 13:15:14 -0800 Subject: [PATCH 01/15] Add WindowsRuntimeAuthoringAssemblyExportsTypeAttribute Introduce an assembly-level WindowsRuntimeAuthoringAssemblyExportsTypeAttribute used by the CsWinRT generator to record the Type that contains the managed GetActivationFactory method for authoring scenarios. The sealed attribute stores a Type (ExportsType), is marked Obsolete with diagnostic metadata, and EditorBrowsableState.Never to indicate it is not intended for direct use. Includes XML docs and license header. --- ...meAuthoringAssemblyExportsTypeAttribute.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs diff --git a/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs new file mode 100644 index 000000000..b859206f5 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +namespace WindowsRuntime.InteropServices; + +/// +/// Indicates which type contains the managed GetActivationFactory method to invoke for authoring scenarios. This +/// attribute is only meant to be used within an assembly annotated with . +/// +/// +/// This attribute is emitted by the CsWinRT generator, and it is not meant to be used directly. +/// +/// +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public sealed class WindowsRuntimeAuthoringAssemblyExportsTypeAttribute : Attribute +{ + /// + /// Creates a new instance with the specified parameters. + /// + /// The type that contains the managed GetActivationFactory method to invoke for authoring scenarios. + public WindowsRuntimeAuthoringAssemblyExportsTypeAttribute(Type exportsType) + { + ExportsType = exportsType; + } + + /// + /// Gets the type that contains the managed GetActivationFactory method to invoke for authoring scenarios. + /// + public Type ExportsType { get; } +} \ No newline at end of file From 676f261af2a57486b848c9d41054b23203c83673 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 13:34:33 -0800 Subject: [PATCH 02/15] Add IndentedTextWriter helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce IndentedTextWriter (src/Authoring/WinRT.SourceGenerator2/Helpers) — a portable helper for building indented source text using pooled buffers and C# interpolated string handlers. Ported from ComputeSharp and MIT-licensed, it provides indentation management (default 4 spaces), block scoping (Block IDisposable to emit braces and adjust indent), multiline-safe Write/WriteLine, conditional WriteIf/WriteLineIf overloads, and custom interpolated-string handlers (WriteInterpolatedStringHandler and WriteIfInterpolatedStringHandler). Includes ToStringAndClear and Dispose semantics to trim/clear the underlying buffer. --- .../IndentedInterpolatedStringHandler.cs | 508 ++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs diff --git a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs new file mode 100644 index 000000000..3e510ff16 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs @@ -0,0 +1,508 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Ported from ComputeSharp. +// See: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.SourceGeneration/Helpers/IndentedTextWriter.cs. + +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text; + +#pragma warning disable IDE0060, IDE0290 + +namespace WindowsRuntime.SourceGenerator; + +/// +/// A helper type to build sequences of values with pooled buffers. +/// +internal ref struct IndentedTextWriter +{ + /// + /// The default indentation (4 spaces). + /// + private const string DefaultIndentation = " "; + + /// + /// The default new line ('\n'). + /// + private const char DefaultNewLine = '\n'; + + /// + /// The instance that text will be written to. + /// + private DefaultInterpolatedStringHandler _handler; + + /// + /// The current indentation level. + /// + private int _currentIndentationLevel; + + /// + /// The current indentation, as text. + /// + private string _currentIndentation; + + /// + /// The cached array of available indentations, as text. + /// + private string[] _availableIndentations; + + /// + /// Creates a new value with the specified parameters. + /// v + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + public IndentedTextWriter(int literalLength, int formattedCount) + { + _handler = new DefaultInterpolatedStringHandler(literalLength, formattedCount); + _currentIndentationLevel = 0; + _currentIndentation = ""; + _availableIndentations = new string[4]; + _availableIndentations[0] = ""; + + for (int i = 1, n = _availableIndentations.Length; i < n; i++) + { + _availableIndentations[i] = _availableIndentations[i - 1] + DefaultIndentation; + } + } + + /// + /// Increases the current indentation level. + /// + public void IncreaseIndent() + { + _currentIndentationLevel++; + + if (_currentIndentationLevel == _availableIndentations.Length) + { + Array.Resize(ref _availableIndentations, _availableIndentations.Length * 2); + } + + // Set both the current indentation and the current position in the indentations + // array to the expected indentation for the incremented level (ie. one level more). + _currentIndentation = _availableIndentations[_currentIndentationLevel] + ??= _availableIndentations[_currentIndentationLevel - 1] + DefaultIndentation; + } + + /// + /// Decreases the current indentation level. + /// + public void DecreaseIndent() + { + _currentIndentationLevel--; + _currentIndentation = _availableIndentations[_currentIndentationLevel]; + } + + /// + /// Writes a block to the underlying buffer. + /// + /// A value to close the open block with. + public Block WriteBlock() + { + WriteLine("{"); + IncreaseIndent(); + + return new(this); + } + + /// + /// Writes content to the underlying buffer. + /// + /// The content to write. + /// Whether the input content is multiline. + public void Write(string content, bool isMultiline = false) + { + Write(content.AsSpan(), isMultiline); + } + + /// + /// Writes content to the underlying buffer. + /// + /// The content to write. + /// Whether the input content is multiline. + public void Write(ReadOnlySpan content, bool isMultiline = false) + { + if (isMultiline) + { + while (content.Length > 0) + { + int newLineIndex = content.IndexOf(DefaultNewLine); + + if (newLineIndex < 0) + { + // There are no new lines left, so the content can be written as a single line + WriteRawText(content); + + break; + } + else + { + ReadOnlySpan line = content[..newLineIndex]; + + // Write the current line (if it's empty, we can skip writing the text entirely). + // This ensures that raw multiline string literals with blank lines don't have + // extra whitespace at the start of those lines, which would otherwise happen. + WriteIf(!line.IsEmpty, line); + WriteLine(); + + // Move past the new line character (the result could be an empty span) + content = content[(newLineIndex + 1)..]; + } + } + } + else + { + WriteRawText(content); + } + } + + /// + /// Writes content to the underlying buffer. + /// + /// The interpolated string handler with content to write. + [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")] + public readonly void Write([InterpolatedStringHandlerArgument("")] ref WriteInterpolatedStringHandler handler) + { + } + + /// + /// Writes content to the underlying buffer depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The content to write. + /// Whether the input content is multiline. + public void WriteIf(bool condition, string content, bool isMultiline = false) + { + if (condition) + { + Write(content.AsSpan(), isMultiline); + } + } + + /// + /// Writes content to the underlying buffer depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The content to write. + /// Whether the input content is multiline. + public void WriteIf(bool condition, ReadOnlySpan content, bool isMultiline = false) + { + if (condition) + { + Write(content, isMultiline); + } + } + + /// + /// Writes content to the underlying buffer depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The interpolated string handler with content to write. + [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")] + public readonly void WriteIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] ref WriteIfInterpolatedStringHandler handler) + { + } + + /// + /// Writes a line to the underlying buffer. + /// + /// Indicates whether to skip adding the line if there already is one. + public void WriteLine(bool skipIfPresent = false) + { + if (skipIfPresent && _handler.Text is [.., '\n', '\n']) + { + return; + } + + _handler.AppendFormatted(DefaultNewLine); + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line. + /// + /// The content to write. + /// Whether the input content is multiline. + public void WriteLine(string content, bool isMultiline = false) + { + WriteLine(content.AsSpan(), isMultiline); + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line. + /// + /// The content to write. + /// Whether the input content is multiline. + public void WriteLine(ReadOnlySpan content, bool isMultiline = false) + { + Write(content, isMultiline); + WriteLine(); + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line. + /// + /// The interpolated string handler with content to write. + public void WriteLine([InterpolatedStringHandlerArgument("")] ref WriteInterpolatedStringHandler handler) + { + WriteLine(); + } + + /// + /// Writes a line to the underlying buffer depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// Indicates whether to skip adding the line if there already is one. + public void WriteLineIf(bool condition, bool skipIfPresent = false) + { + if (condition) + { + WriteLine(skipIfPresent); + } + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The content to write. + /// Whether the input content is multiline. + public void WriteLineIf(bool condition, string content, bool isMultiline = false) + { + if (condition) + { + WriteLine(content.AsSpan(), isMultiline); + } + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The content to write. + /// Whether the input content is multiline. + public void WriteLineIf(bool condition, ReadOnlySpan content, bool isMultiline = false) + { + if (condition) + { + Write(content, isMultiline); + WriteLine(); + } + } + + /// + /// Writes content to the underlying buffer and appends a trailing new line depending on an input condition. + /// + /// The condition to use to decide whether or not to write content. + /// The interpolated string handler with content to write. + public void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] ref WriteIfInterpolatedStringHandler handler) + { + if (condition) + { + WriteLine(); + } + } + + /// + public string ToStringAndClear() + { + string text = _handler.Text.Trim().ToString(); + + _handler.Clear(); + + return text; + } + + /// + public void Dispose() + { + _handler.Clear(); + } + + /// + /// Writes raw text to the underlying buffer, adding leading indentation if needed. + /// + /// The raw text to write. + private void WriteRawText(ReadOnlySpan content) + { + if (_handler.Text.Length == 0 || _handler.Text[^1] == DefaultNewLine) + { + _handler.AppendLiteral(_currentIndentation); + } + + _handler.AppendFormatted(content); + } + + /// + /// A delegate representing a callback to write data into an instance. + /// + /// The type of data to use. + /// The input data to use to write into . + /// The instance to write into. + public delegate void Callback(T value, IndentedTextWriter writer); + + /// + /// Represents an indented block that needs to be closed. + /// + /// The input instance to wrap. + public ref struct Block(IndentedTextWriter writer) : IDisposable + { + /// + /// The instance to write to. + /// + private IndentedTextWriter _writer = writer; + + /// + public void Dispose() + { + IndentedTextWriter writer = _writer; + + _writer = default; + + // We check the indentation as a way of knowing if we have reset the field before. + // The field itself can't be 'null', but if we have assigned 'default' to it, then + // that field will be 'null' even though it's always set from the constructor. + if (writer._currentIndentation is not null) + { + writer.DecreaseIndent(); + writer.WriteLine("}"); + } + } + } + + /// + /// Provides a handler used by the language compiler to append interpolated strings into instances. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public readonly ref struct WriteInterpolatedStringHandler + { + /// The associated to which to append. + private readonly IndentedTextWriter _writer; + + /// Creates a handler used to append an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The associated to which to append. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public WriteInterpolatedStringHandler(int literalLength, int formattedCount, IndentedTextWriter writer) + { + _writer = writer; + } + + /// Writes the specified string to the handler. + /// The string to write. + public void AppendLiteral(string value) + { + _writer.Write(value); + } + + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) + { + AppendFormatted(value); + } + + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) + { + _writer.Write(value); + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The type of the value to write. + public void AppendFormatted(T? value) + { + if (value is not null) + { + _writer.Write(value.ToString()!); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// The type of the value to write. + [UnconditionalSuppressMessage("Style", "IDE0038", Justification = "Not using pattern matching to avoid boxing.")] + public void AppendFormatted(T? value, string? format) + { + if (value is IFormattable) + { + _writer.Write(((IFormattable)value).ToString(format, CultureInfo.InvariantCulture)); + } + else if (value is not null) + { + _writer.Write(value.ToString()!); + } + } + } + + /// + /// Provides a handler used by the language compiler to conditionally append interpolated strings into instances. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public readonly ref struct WriteIfInterpolatedStringHandler + { + /// The associated to use. + private readonly WriteInterpolatedStringHandler _writer; + + /// Creates a handler used to append an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The associated to which to append. + /// The condition to use to decide whether or not to write content. + /// A value indicating whether formatting should proceed. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public WriteIfInterpolatedStringHandler(int literalLength, int formattedCount, IndentedTextWriter writer, bool condition, out bool shouldAppend) + { + if (condition) + { + _writer = new WriteInterpolatedStringHandler(literalLength, formattedCount, writer); + + shouldAppend = true; + } + else + { + _writer = default; + + shouldAppend = false; + } + } + + /// + public void AppendLiteral(string value) + { + _writer.AppendLiteral(value); + } + + /// + public void AppendFormatted(string? value) + { + _writer.AppendFormatted(value); + } + + /// + public void AppendFormatted(ReadOnlySpan value) + { + _writer.AppendFormatted(value); + } + + /// + public void AppendFormatted(T? value) + { + _writer.AppendFormatted(value); + } + + /// + public void AppendFormatted(T? value, string? format) + { + _writer.AppendFormatted(value, format); + } + } +} \ No newline at end of file From ecac362e45ae1155db3ced4fbfee1a8030143ede Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 14:38:01 -0800 Subject: [PATCH 03/15] Add Docs entries to cswinrt.slnx Add documentation and images to the cswinrt solution by including Folder/File entries for /Docs/, /Docs/cswinrtgen/, /Docs/diagnostics/, and /Docs/images/. This exposes markdown docs (usage, authoring, interop, versioning, etc.), cswinrtgen docs, diagnostics, and related images in the solution explorer so the docs are available alongside the code. --- src/cswinrt.slnx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index f656521e7..972ff5d1b 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -19,6 +19,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9e89a52ce9e3ad8c34100a3c779b468ad584c5fe Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 15:00:22 -0800 Subject: [PATCH 04/15] Update WinRT interop DLL spec Clarify and extend the WinRT interop DLL specification: add a rule to derive namespaces for nested types from the outermost declaring type; document that types with a [WindowsRuntimeMetadata] attribute use the attribute's WinRT metadata name as the assembly identifier when not in a well-known assembly; add native-sized integer primitives (nint, nuint); and remove #WinUI2 and #Win2D from the list of compact well-known assembly identifiers. These changes improve handling of nested types, metadata-backed WinRT types, and native-sized integer support. --- docs/winrt-interop-dll-spec.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/winrt-interop-dll-spec.md b/docs/winrt-interop-dll-spec.md index ce1194699..2972aab29 100644 --- a/docs/winrt-interop-dll-spec.md +++ b/docs/winrt-interop-dll-spec.md @@ -25,6 +25,7 @@ The **mangled namespace** for a given type is defined as follows: 1. **No namespace**: if the input type has no namespace, the generated namespace defaults to `ABI`. 2. **Existing namespace**: if the input type has a namespace, the generated namespace prepends `ABI.` to the original namespace. +3. **Nested types**: if the input type is nested, the namespace is derived from the outermost declaring type (i.e. the top-level enclosing type), using the same rules above. The **mangled type name** for a given type is defined as follows: @@ -45,11 +46,11 @@ These are the well-known assemblies and their compact identifiers: - `System.Runtime`: `#corlib` - `Microsoft.Windows.SDK.NET` or `Microsoft.Windows.UI.Xaml`: `#Windows` - `WinRT.Runtime`: `#CsWinRT` -- `Microsoft.UI.Xaml.Projection`: `#WinUI2` -- `Microsoft.Graphics.Canvas.Interop`: `#Win2D` 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. + ### Examples **Primitive type** @@ -134,6 +135,8 @@ primitiveType : 'bool' | 'ulong' | 'float' | 'double' + | 'nint' + | 'nuint' | 'string' | 'object'; @@ -154,8 +157,6 @@ arrayType : '<' mangledTypeName '>' 'Array'; assemblyName : '#corlib' | '#Windows' | '#CsWinRT' - | '#WinUI2' - | '#Win2D' | identifier; // After substitutions, identifiers may contain '-' (from namespaces), '+' (nested type separators, From 830e6210c05ceb70686c9d7c5fef8fdadd3b3566 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 15:19:22 -0800 Subject: [PATCH 05/15] Use scoped for span/ref parameters in writer Add C# 'scoped' to ReadOnlySpan and scoped ref interpolated-string handler parameters, and mark several Write/WriteIf/WriteLine overloads as readonly. Replace direct instance WriteLine calls with Unsafe.AsRef(in this).WriteLine() to allow invocation from readonly contexts. Rename file from IndentedInterpolatedStringHandler.cs to IndentedTextWriter.cs. These changes clarify span lifetimes and prevent span escaping in the indented text writer. --- ...StringHandler.cs => IndentedTextWriter.cs} | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) rename src/Authoring/WinRT.SourceGenerator2/Helpers/{IndentedInterpolatedStringHandler.cs => IndentedTextWriter.cs} (94%) diff --git a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs similarity index 94% rename from src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs rename to src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs index 3e510ff16..bde91427c 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedInterpolatedStringHandler.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs @@ -123,7 +123,7 @@ public void Write(string content, bool isMultiline = false) /// /// The content to write. /// Whether the input content is multiline. - public void Write(ReadOnlySpan content, bool isMultiline = false) + public void Write(scoped ReadOnlySpan content, bool isMultiline = false) { if (isMultiline) { @@ -164,7 +164,7 @@ public void Write(ReadOnlySpan content, bool isMultiline = false) /// /// The interpolated string handler with content to write. [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")] - public readonly void Write([InterpolatedStringHandlerArgument("")] ref WriteInterpolatedStringHandler handler) + public readonly void Write([InterpolatedStringHandlerArgument("")] scoped ref WriteInterpolatedStringHandler handler) { } @@ -188,7 +188,7 @@ public void WriteIf(bool condition, string content, bool isMultiline = false) /// The condition to use to decide whether or not to write content. /// The content to write. /// Whether the input content is multiline. - public void WriteIf(bool condition, ReadOnlySpan content, bool isMultiline = false) + public void WriteIf(bool condition, scoped ReadOnlySpan content, bool isMultiline = false) { if (condition) { @@ -202,7 +202,7 @@ public void WriteIf(bool condition, ReadOnlySpan content, bool isMultiline /// The condition to use to decide whether or not to write content. /// The interpolated string handler with content to write. [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")] - public readonly void WriteIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] ref WriteIfInterpolatedStringHandler handler) + public readonly void WriteIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] scoped ref WriteIfInterpolatedStringHandler handler) { } @@ -235,7 +235,7 @@ public void WriteLine(string content, bool isMultiline = false) /// /// The content to write. /// Whether the input content is multiline. - public void WriteLine(ReadOnlySpan content, bool isMultiline = false) + public void WriteLine(scoped ReadOnlySpan content, bool isMultiline = false) { Write(content, isMultiline); WriteLine(); @@ -245,9 +245,9 @@ public void WriteLine(ReadOnlySpan content, bool isMultiline = false) /// Writes content to the underlying buffer and appends a trailing new line. /// /// The interpolated string handler with content to write. - public void WriteLine([InterpolatedStringHandlerArgument("")] ref WriteInterpolatedStringHandler handler) + public readonly void WriteLine([InterpolatedStringHandlerArgument("")] scoped ref WriteInterpolatedStringHandler handler) { - WriteLine(); + Unsafe.AsRef(in this).WriteLine(); } /// @@ -283,7 +283,7 @@ public void WriteLineIf(bool condition, string content, bool isMultiline = false /// The condition to use to decide whether or not to write content. /// The content to write. /// Whether the input content is multiline. - public void WriteLineIf(bool condition, ReadOnlySpan content, bool isMultiline = false) + public void WriteLineIf(bool condition, scoped ReadOnlySpan content, bool isMultiline = false) { if (condition) { @@ -297,11 +297,11 @@ public void WriteLineIf(bool condition, ReadOnlySpan content, bool isMulti /// /// The condition to use to decide whether or not to write content. /// The interpolated string handler with content to write. - public void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] ref WriteIfInterpolatedStringHandler handler) + public readonly void WriteLineIf(bool condition, [InterpolatedStringHandlerArgument("", nameof(condition))] scoped ref WriteIfInterpolatedStringHandler handler) { if (condition) { - WriteLine(); + Unsafe.AsRef(in this).WriteLine(); } } @@ -325,7 +325,7 @@ public void Dispose() /// Writes raw text to the underlying buffer, adding leading indentation if needed. /// /// The raw text to write. - private void WriteRawText(ReadOnlySpan content) + private void WriteRawText(scoped ReadOnlySpan content) { if (_handler.Text.Length == 0 || _handler.Text[^1] == DefaultNewLine) { @@ -408,7 +408,7 @@ public void AppendFormatted(string? value) /// Writes the specified character span to the handler. /// The span to write. - public void AppendFormatted(ReadOnlySpan value) + public void AppendFormatted(scoped ReadOnlySpan value) { _writer.Write(value); } @@ -488,7 +488,7 @@ public void AppendFormatted(string? value) } /// - public void AppendFormatted(ReadOnlySpan value) + public void AppendFormatted(scoped ReadOnlySpan value) { _writer.AppendFormatted(value); } From bbb59c94e2fe8b3bb089a4b62fa37abd41418844 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 15:21:39 -0800 Subject: [PATCH 06/15] Remove IIncrementalGenerator implementation Drop the IIncrementalGenerator interface from the TypeMapAssemblyTargetGenerator class declaration. The class remains partial and retains its generation methods; this change decouples the type from the incremental generator interface in preparation for refactoring or alternative registration. --- .../TypeMapAssemblyTargetGenerator.Execute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs index 73ea894b4..089d82d06 100644 --- a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs +++ b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.Execute.cs @@ -11,7 +11,7 @@ namespace WindowsRuntime.SourceGenerator; /// -public partial class TypeMapAssemblyTargetGenerator : IIncrementalGenerator +public partial class TypeMapAssemblyTargetGenerator { /// /// Generation methods for . From cd42c2066a10e4317d5313fee8f2ce1bcd59a8f0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 15:21:57 -0800 Subject: [PATCH 07/15] Add IndentedTextWriter attribute helper Introduce IndentedTextWriterExtensions containing WriteGeneratedAttributes to emit [GeneratedCode] (with the extension assembly version) and optional [DebuggerNonUserCode] / [ExcludeFromCodeCoverage] attributes. Supports fully-qualified or short type names and an option to exclude non-user-code attributes. Ported from ComputeSharp (MIT) for use in source generators to mark generated files. --- .../IndentedTextWriterExtensions.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs new file mode 100644 index 000000000..21cfeef59 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/IndentedTextWriterExtensions.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Ported from ComputeSharp. +// See: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.SourceGeneration/Helpers/IndentedTextWriter.cs. + +using System; + +namespace WindowsRuntime.SourceGenerator; + +/// +/// Extension methods for the type. +/// +internal static class IndentedTextWriterExtensions +{ + /// + /// Writes the following attributes into a target writer: + /// + /// [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] + /// [global::System.Diagnostics.DebuggerNonUserCode] + /// [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + /// + /// + /// The instance to write into. + /// The name of the generator. + /// Whether to use fully qualified type names or not. + /// Whether to also include the attribute for non-user code. + public static void WriteGeneratedAttributes( + this ref IndentedTextWriter writer, + string generatorName, + bool useFullyQualifiedTypeNames = true, + bool includeNonUserCodeAttributes = true) + { + // We can use this class to get the assembly, as all files for generators are just included + // via shared projects. As such, the assembly will be the same as the generator type itself. + Version assemblyVersion = typeof(IndentedTextWriterExtensions).Assembly.GetName().Version!; + + if (useFullyQualifiedTypeNames) + { + writer.WriteLine($$"""[global::System.CodeDom.Compiler.GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]"""); + + if (includeNonUserCodeAttributes) + { + writer.WriteLine($$"""[global::System.Diagnostics.DebuggerNonUserCode]"""); + writer.WriteLine($$"""[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]"""); + } + } + else + { + writer.WriteLine($$"""[GeneratedCode("{{generatorName}}", "{{assemblyVersion}}")]"""); + + if (includeNonUserCodeAttributes) + { + writer.WriteLine($$"""[DebuggerNonUserCode]"""); + writer.WriteLine($$"""[ExcludeFromCodeCoverage]"""); + } + } + } +} \ No newline at end of file From 2c0b4cbcb1c5248a452119033c168c52e3ea4bcc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 15:33:30 -0800 Subject: [PATCH 08/15] Add string EscapeIdentifierName extension Introduce StringExtensions with an EscapeIdentifierName extension for string used by the source generator. The method returns "_" for null/empty, preserves already valid identifiers, and otherwise replaces invalid identifier characters with '_' using Microsoft.CodeAnalysis.CSharp.SyntaxFacts. It builds the result efficiently with DefaultInterpolatedStringHandler and is intended to produce safe identifier names (e.g., for assembly or symbol names). --- .../Extensions/StringExtensions.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs new file mode 100644 index 000000000..e6351d53a --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.CSharp; + +namespace WindowsRuntime.SourceGenerator; + +/// +/// Extensions for . +/// +internal static class StringExtensions +{ + /// The input value. + extension(string value) + { + /// + /// Escapes an identifier name. + /// + /// The escaped identifier name from the current value. + public string EscapeIdentifierName() + { + // If the current value is empty, we just use an underscore for the identifier name. This + // should generally never be the case, as this method is mostly used for assembly names. + if (value is null or "") + { + return "_"; + } + + // Optimization: we can just return the name as is if it's already a valid identifier. Generally speaking this + // should always be the case for assembly names (except if they have one or more parts separated by a '.'). + if (SyntaxFacts.IsValidIdentifier(value)) + { + return value; + } + + DefaultInterpolatedStringHandler handler = new(literalLength: value.Length, formattedCount: 0); + + // Build the escaped name by just replacing all invalid characters with '_' + foreach (char c in value) + { + handler.AppendFormatted(SyntaxFacts.IsIdentifierPartCharacter(c) ? c : '_'); + } + + return handler.ToStringAndClear(); + } + } +} \ No newline at end of file From 92559a015c00a50771df93a5efda8c159e1ec80f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 18:55:54 -0800 Subject: [PATCH 09/15] Add interpolated string handler overloads Introduce Write and WriteLine overloads that accept a DefaultInterpolatedStringHandler and an isMultiline flag. The new methods forward handler.Text to the existing Write/WriteLine implementations, clear the handler afterwards, and include an UnconditionalSuppressMessage on Write to suppress CA1822 since the handler call implicitly passes 'this'. This enables more efficient handling of interpolated strings when writing to the IndentedTextWriter. --- .../Helpers/IndentedTextWriter.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs index bde91427c..5e795b8cf 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs @@ -168,6 +168,19 @@ public readonly void Write([InterpolatedStringHandlerArgument("")] scoped ref Wr { } + /// + /// Writes content to the underlying buffer. + /// + /// The interpolated string handler with content to write. + /// Whether the input content is multiline. + [UnconditionalSuppressMessage("Performance", "CA1822", Justification = "This method implicitly passes 'this' to the input handler.")] + public readonly void Write(scoped ref DefaultInterpolatedStringHandler handler, bool isMultiline) + { + Unsafe.AsRef(in this).Write(handler.Text, isMultiline); + + handler.Clear(); + } + /// /// Writes content to the underlying buffer depending on an input condition. /// @@ -250,6 +263,18 @@ public readonly void WriteLine([InterpolatedStringHandlerArgument("")] scoped re Unsafe.AsRef(in this).WriteLine(); } + /// + /// Writes content to the underlying buffer and appends a trailing new line. + /// + /// The interpolated string handler with content to write. + /// Whether the input content is multiline. + public readonly void WriteLine(scoped ref DefaultInterpolatedStringHandler handler, bool isMultiline) + { + Unsafe.AsRef(in this).WriteLine(handler.Text, isMultiline); + + handler.Clear(); + } + /// /// Writes a line to the underlying buffer depending on an input condition. /// From c136f7e679255574262b1db6f9401900c8a53aad Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 18:56:06 -0800 Subject: [PATCH 10/15] Add IsAccessibleFromCompilationAssembly extension Introduce IsAccessibleFromCompilationAssembly(Compilation) to ISymbolExtensions to check whether an ISymbol is accessible from a given compilation's assembly (uses compilation.IsSymbolAccessibleWithin). Also add XML doc param for the symbol parameter to improve documentation. --- .../Extensions/ISymbolExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs index 924c66a9e..117fcd2cd 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs @@ -13,6 +13,7 @@ namespace WindowsRuntime.SourceGenerator; /// internal static class ISymbolExtensions { + /// The input instance. extension(ISymbol symbol) { /// @@ -47,5 +48,15 @@ public bool TryGetAttributeWithType(ITypeSymbol typeSymbol, [NotNullWhen(true)] return false; } + + /// + /// Checks whether a given symbol is accessible from the assembly of a given compilation (including eg. through nested types). + /// + /// The instance currently in use. + /// Whether is accessible from the assembly for . + public bool IsAccessibleFromCompilationAssembly(Compilation compilation) + { + return compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly); + } } } \ No newline at end of file From 37521aa7c3fc4864e9d23fc1343f997af1eefaa8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 18:56:34 -0800 Subject: [PATCH 11/15] Add AuthoringExportTypes source generator Introduce an incremental source generator to emit managed and native export types for authoring scenarios. Adds AuthoringExportTypesGenerator (split into core, Execute and Helpers), models for generator options and info (AuthoringExportTypesOptions, AuthoringManagedExportsInfo, AuthoringNativeExportsInfo), and generation logic that produces ManagedExports.g.cs and NativeExports.g.cs. Adds AnalyzerConfigOptionsExtension methods to read CsWinRTComponent and CsWinRTMergeReferencedActivationFactories MSBuild properties and uses them (along with PublishAot) to control emission and merging of referenced activation factories. Includes helper to discover dependent assembly export type names when merging activation factories. --- .../AuthoringExportTypesGenerator.Execute.cs | 393 ++++++++++++++++++ .../AuthoringExportTypesGenerator.Helpers.cs | 70 ++++ .../AuthoringExportTypesGenerator.cs | 37 ++ .../AnalyzerConfigOptionsExtensions.cs | 18 + .../Models/AuthoringExportTypesOptions.cs | 41 ++ .../Models/AuthoringManagedExportsInfo.cs | 15 + .../Models/AuthoringNativeExportsInfo.cs | 11 + 7 files changed, 585 insertions(+) create mode 100644 src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs create mode 100644 src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs create mode 100644 src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs create mode 100644 src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs create mode 100644 src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs create mode 100644 src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs new file mode 100644 index 000000000..c3f6721fa --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs @@ -0,0 +1,393 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using WindowsRuntime.SourceGenerator.Models; + +namespace WindowsRuntime.SourceGenerator; + +/// +public partial class AuthoringExportTypesGenerator +{ + /// + /// Generation methods for . + /// + private static class Execute + { + /// + /// Gets the options for the generator. + /// + /// The input options provider. + /// The cancellation token for the operation. + /// The resulting options. + public static AuthoringExportTypesOptions GetOptions(AnalyzerConfigOptionsProvider provider, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + return new( + PublishAot: provider.GlobalOptions.GetPublishAot(), + IsComponent: provider.GlobalOptions.GetCsWinRTComponent(), + MergeReferencedActivationFactories: provider.GlobalOptions.GetCsWinRTMergeReferencedActivationFactories()); + } + + /// + /// Gets the info for generating native exports. + /// + /// The input data. + /// The cancellation token for the operation. + /// The resulting info. + public static AuthoringNativeExportsInfo GetNativeExportsInfo((Compilation compilation, AuthoringExportTypesOptions Options) data, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + return new( + AssemblyName: data.compilation.AssemblyName ?? "", + Options: data.Options); + } + + /// + /// Gets all portable executable references from a compilation. + /// + /// The input data. + /// The cancellation token for the operation. + /// All portable executable references for the input compilation. + public static AuthoringManagedExportsInfo GetManagedExportsInfo((Compilation Compilation, AuthoringExportTypesOptions Options) data, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + // Skip going through references if merging is not enabled + if (!data.Options.MergeReferencedActivationFactories) + { + return new( + AssemblyName: data.Compilation.AssemblyName ?? "", + MergedManagedExportsTypeNames: [], + Options: data.Options); + } + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + + // Go through all references to find transitively-references Windows Runtime components + foreach (MetadataReference metadataReference in data.Compilation.References) + { + token.ThrowIfCancellationRequested(); + + if (data.Compilation.GetAssemblyOrModuleSymbol(metadataReference) is not IAssemblySymbol assemblySymbol) + { + continue; + } + + token.ThrowIfCancellationRequested(); + + // Add the type name if the assembly is a Windows Runtime component + if (Helpers.TryGetDependentAssemblyExportsTypeName( + assemblySymbol: assemblySymbol, + compilation: data.Compilation, + token: token, + name: out string? name)) + { + builder.Add(name); + } + } + + token.ThrowIfCancellationRequested(); + + return new( + AssemblyName: data.Compilation.AssemblyName ?? "", + MergedManagedExportsTypeNames: builder.ToImmutable(), + Options: data.Options); + } + + /// + /// Emits the managed exports for authored components. + /// + /// The instance to use. + /// The input info. + public static void EmitManagedExports(SourceProductionContext context, AuthoringManagedExportsInfo info) + { + if (!info.Options.ShouldEmitManagedExports()) + { + return; + } + + IndentedTextWriter writer = new(literalLength: 0, formattedCount: 0); + + // Emit the '[WindowsRuntimeAuthoringAssemblyExportsType]' attribute so other tooling (including this same generator) + // can reliably find the generated export types from other assemblies, which is needed when merging activation factories. + writer.WriteLine($$""" + // + #pragma warning disable + + [assembly: global::WindowsRuntime.InteropServices.WindowsRuntimeAuthoringAssemblyExportsType(typeof(global::ABI.{{info.AssemblyName.EscapeIdentifierName()}}.ManagedExports))] + + namespace ABI.{{info.AssemblyName.EscapeIdentifierName()}}; + + using global::System; + using global::System.CodeDom.Compiler; + using global::System.ComponentModel; + using global::System.Diagnostics; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + + /// + /// Contains the managed exports for activating types from the current project in an authoring scenario. + /// + """); + + // Emit the standard generated attributes, and also mark the type as hidden, since it's generated as a public type + writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false); + writer.WriteLine($$""" + [EditorBrowsable(EditorBrowsableState.Never)] + public static unsafe class ManagedExports + """); + + // Indent via a block, as we'll also need to emit custom logic for the activation factory + using (writer.WriteBlock()) + { + // The 'GetActivationFactory' method is the one that actually contains all the high-level logic for + // activation scenarios. If we have any native exports (see below), those would call this method too. + writer.WriteLine($$""" + /// + /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes. + /// + /// The class identifier that is associated with an activatable runtime class. + /// The resulting pointer to the activation factory that corresponds with the class specified by . + public static void* GetActivationFactory(ReadOnlySpan activatableClassId) + """); + + using (writer.WriteBlock()) + { + // Emit the unsafe accessor pointing to 'WinRT.Authoring.dll', where the actual activation code is located + if (info.Options.IsComponent) + { + writer.WriteLine($""" + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, MethodName = "GetActivationFactory")] + static extern void* AuthoringGetActivationFactory( + [UnsafeAccessorType("ABI.{info.AssemblyName.EscapeIdentifierName()}.ManagedExports, WinRT.Authoring")] object? _, + ReadOnlySpan activatableClassId); + """); + } + + // Emit the specialized code to redirect the activation + if (info.Options.IsComponent && !info.Options.MergeReferencedActivationFactories) + { + writer.WriteLine("return AuthoringGetActivationFactory(null, activatableClassId);"); + } + else if (info.Options.IsComponent && info.Options.MergeReferencedActivationFactories) + { + writer.WriteLine(""" + void* activationFactory = AuthoringGetActivationFactory(null, activatableClassId); + + return activationFactory ?? ReferencedManagedExports.GetActivationFactory(activatableClassId); + """); + } + else if (info.Options.MergeReferencedActivationFactories) + { + writer.WriteLine("ReferencedManagedExports.GetActivationFactory(activatableClassId);"); + } + } + + // Emit the reflection friendly overload as well + writer.WriteLine(); + writer.WriteLine($$""" + /// + /// This overload uses reflection friendly types and is only meant to be used from managed runtime hosts. + public static nint GetActivationFactory(string activatableClassId) + { + return (nint)GetActivationFactory(activatableClassId.AsSpan()); + } + """); + } + + // Emit a helper type with the logic for merging activaton factories, if needed + if (info.Options.MergeReferencedActivationFactories) + { + writer.WriteLine(); + writer.WriteLine(""" + /// + /// Contains the logic for activating types from transitively referenced Windows Runtime components. + /// + """); + writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false); + writer.WriteLine("file static unsafe class ReferencedManagedExports"); + + using (writer.WriteBlock()) + { + writer.WriteLine(""" + /// + /// Retrieves the activation factory from all dependent Windows Runtime components. + /// + /// The class identifier that is associated with an activatable runtime class. + /// The resulting pointer to the activation factory that corresponds with the class specified by . + public static void* GetActivationFactory(ReadOnlySpan activatableClassId) + """); + + using (writer.WriteBlock()) + { + writer.WriteLine("void* activationFactory"); + + // Iterate through all transitvely referenced activation factories from other components and use the first that succeeds + foreach (string managedExportsTypeName in info.MergedManagedExportsTypeNames) + { + writer.WriteLine(); + writer.WriteLine($$""" + activationFactory = global::{{managedExportsTypeName}}.GetActivationFactory(activatableClassId); + + if (activationFactory is not null) + { + return activationFactory; + } + """); + } + + // No match across the referenced factories, we can't do anything else + writer.WriteLine(); + writer.WriteLine("return null;"); + } + } + } + + context.AddSource("ManagedExports.g.cs", writer.ToStringAndClear()); + } + + /// + /// Emits the native exports for authored components. + /// + /// The instance to use. + /// The input info. + public static void EmitNativeExports(SourceProductionContext context, AuthoringNativeExportsInfo info) + { + if (!info.Options.ShouldEmitNativeExports()) + { + return; + } + + IndentedTextWriter writer = new(literalLength: 0, formattedCount: 0); + + // Because we're generating code in our own namespace (which nobody else will use), we can use namespace-scoped + // using directives, which allows the generated code to be more concise. We can also do the same with type + // aliases, so we can emit the native methods with a signature that more closely matches the original. + writer.WriteLine($""" + // + #pragma warning disable + + namespace ABI.{info.AssemblyName.EscapeIdentifierName()}; + + using global::System; + using global::System.CodeDom.Compiler; + using global::System.Diagnostics; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + using global::System.Runtime.InteropServices; + using global::WindowsRuntime.InteropServices; + + using HRESULT = int; + using unsafe HSTRING = void*; + using IActivationFactory = void; + + /// + /// Contains the native exports for activating types from the current project in an authoring scenario. + /// + """); + + // Emit the attributes to mark the code as generated, and to exclude it from code coverage as well. We also use a + // file-scoped P/Invoke, so we don't take a dependency on private implementation detail types from 'WinRT.Runtime.dll'. + writer.WriteGeneratedAttributes(nameof(AuthoringExportTypesGenerator), useFullyQualifiedTypeNames: false); + writer.WriteLine($$""" + internal static unsafe class NativeExports + { + /// + /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes. + /// + /// The class identifier that is associated with an activatable runtime class. + /// A pointer to the activation factory that corresponds with the class specified by . + /// The HRESULT for the operation. + /// + [UnmanagedCallersOnly(EntryPoint = nameof(DllGetActivationFactory), CallConvs = [typeof(CallConvStdcall)])] + public static HRESULT DllGetActivationFactory(HSTRING activatableClassId, IActivationFactory** factory) + { + const int E_INVALIDARG = unchecked((int)0x80070057); + const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)(0x80040111)); + const int S_OK = 0; + + if (activatableClassId is null || factory is null) + { + return E_INVALIDARG; + } + + ReadOnlySpan managedActivatableClassId = HStringMarshaller.ConvertToManagedUnsafe(activatableClassId); + + try + { + IActivationFactory* result = ManagedExports.GetActivationFactory(managedActivatableClassId); + + if ((void*)obj is null) + { + *factory = null; + + return CLASS_E_CLASSNOTAVAILABLE; + } + + *factory = (void*)obj; + + return S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + + {GenerateNativeDllGetActivationFactoryImpl(context)} + } + + /// + /// Determines whether the DLL that implements this function is in use. If not, the caller can unload the DLL from memory. + /// + /// This method always returns S_FALSE. + /// + [UnmanagedCallersOnly(EntryPoint = nameof(DllCanUnloadNow), CallConvs = [typeof(CallConvStdcall)])] + public static HRESULT DllCanUnloadNow() + { + const HRESULT S_FALSE = 1; + + return S_FALSE; + } + } + + /// + /// A marshaller for the Windows Runtime HSTRING type. + /// + file static class HStringMarshaller + { + /// + /// Converts an input HSTRING value to a value. + /// + /// The input HSTRING value to marshal. + /// The resulting value. + public static ReadOnlySpan ConvertToManagedUnsafe(HSTRING value) + { + uint length; + char* buffer = WindowsRuntimeImports.WindowsGetStringRawBuffer(value, &length); + + return new(buffer, (int)length); + } + } + + /// + /// Native imports for fundamental COM and Windows Runtime APIs. + /// + file static class WindowsRuntimeImports + { + /// + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", EntryPoint = "WindowsGetStringRawBuffer", ExactSpelling = true)] + [SupportedOSPlatform("windows6.2")] + public static extern partial char* WindowsGetStringRawBuffer(HSTRING @string, uint* length); + } + """); + + context.AddSource("NativeExports.g.cs", writer.ToStringAndClear()); + } + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs new file mode 100644 index 000000000..0901de457 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace WindowsRuntime.SourceGenerator; + +/// +public partial class AuthoringExportTypesGenerator +{ + /// + /// Helper methods for . + /// + private static class Helpers + { + /// + /// Tries to get the name of a dependent Windows Runtime component from a given assembly. + /// + /// The assembly symbol to analyze. + /// The instance to use. + /// The instance to use. + /// The resulting type name, if found. + /// Whether a type name was found. + public static bool TryGetDependentAssemblyExportsTypeName( + IAssemblySymbol assemblySymbol, + Compilation compilation, + CancellationToken token, + [NotNullWhen(true)] out string? name) + { + // Get the attribute to lookup to find the target type to use + INamedTypeSymbol winRTAssemblyExportsTypeAttributeSymbol = compilation.GetTypeByMetadataName("WindowsRuntime.InteropServices.WindowsRuntimeAuthoringAssemblyExportsTypeAttribute")!; + + // Make sure the assembly does have the attribute on it + if (!assemblySymbol.TryGetAttributeWithType(winRTAssemblyExportsTypeAttributeSymbol, out AttributeData? attributeData)) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + // Sanity check: we should have a valid type in the annotation + if (attributeData.ConstructorArguments is not [{ Kind: TypedConstantKind.Type, Value: INamedTypeSymbol assemblyExportsTypeSymbol }]) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + // Other sanity check: this type should be accessible from this compilation + if (!assemblyExportsTypeSymbol.IsAccessibleFromCompilationAssembly(compilation)) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + name = assemblyExportsTypeSymbol.ToDisplayString(); + + return true; + } + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs new file mode 100644 index 000000000..c81da7aa3 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.CodeAnalysis; +using WindowsRuntime.SourceGenerator.Models; + +namespace WindowsRuntime.SourceGenerator; + +/// +/// A generator to emit export types needing for authoring scenarios. +/// +[Generator] +public sealed partial class AuthoringExportTypesGenerator : IIncrementalGenerator +{ + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Get the options for the generator from the analyzer options + IncrementalValueProvider options = context.AnalyzerConfigOptionsProvider.Select(Execute.GetOptions); + + // Get the generation info specific to managed exports + IncrementalValueProvider managedExportsInfo = context.CompilationProvider + .Combine(options) + .Select(Execute.GetManagedExportsInfo); + + // Get the generation info specific to native exports + IncrementalValueProvider nativeExportsInfo = context.CompilationProvider + .Combine(options) + .Select(Execute.GetNativeExportsInfo); + + // Generate the managed exports type + context.RegisterImplementationSourceOutput(managedExportsInfo, Execute.EmitManagedExports); + + // Generate the native exports type + context.RegisterImplementationSourceOutput(nativeExportsInfo, Execute.EmitNativeExports); + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs index 721ea8c86..f7a6fa202 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/AnalyzerConfigOptionsExtensions.cs @@ -22,6 +22,24 @@ public bool GetPublishAot() return options.GetBooleanProperty("PublishAot"); } + /// + /// Gets the value of the "CsWinRTComponent" property. + /// + /// The value of the "CsWinRTComponent" property. + public bool GetCsWinRTComponent() + { + return options.GetBooleanProperty("CsWinRTComponent"); + } + + /// + /// Gets the value of the "CsWinRTMergeReferencedActivationFactories" property. + /// + /// The value of the "CsWinRTMergeReferencedActivationFactories" property. + public bool GetCsWinRTMergeReferencedActivationFactories() + { + return options.GetBooleanProperty("CsWinRTMergeReferencedActivationFactories"); + } + /// /// Tries to get the value of a boolean MSBuild property. /// diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs new file mode 100644 index 000000000..83a08c8e6 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringExportTypesOptions.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsRuntime.SourceGenerator.Models; + +/// +/// Options for . +/// +/// +/// +/// +internal record AuthoringExportTypesOptions( + bool PublishAot, + bool IsComponent, + bool MergeReferencedActivationFactories) +{ + /// + /// Gets whether the managed exports should be emitted. + /// + /// Whether the managed exports should be emitted. + public bool ShouldEmitManagedExports() + { + return IsComponent || MergeReferencedActivationFactories; + } + + /// + /// Gets whether the native exports should be emitted. + /// + /// Whether the native exports should be emitted. + public bool ShouldEmitNativeExports() + { + if (!PublishAot) + { + return false; + } + + // We need these either in normal publishing scenarios where AOT is enabled, or also + // if the project is not a component, but we're merging referenced activation factories. + return IsComponent || MergeReferencedActivationFactories; + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs new file mode 100644 index 000000000..3781bc5fe --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringManagedExportsInfo.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsRuntime.SourceGenerator.Models; + +/// +/// Options for specific to managed exports. +/// +/// The assembly name for the current assembly. +/// The names of the merged managed exports types. +/// The options for the generator. +internal record AuthoringManagedExportsInfo( + string AssemblyName, + EquatableArray MergedManagedExportsTypeNames, + AuthoringExportTypesOptions Options); \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs new file mode 100644 index 000000000..38c52a5ef --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator2/Models/AuthoringNativeExportsInfo.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsRuntime.SourceGenerator.Models; + +/// +/// Options for specific to native exports. +/// +/// The assembly name for the current assembly. +/// The options for the generator. +internal record AuthoringNativeExportsInfo(string AssemblyName, AuthoringExportTypesOptions Options); \ No newline at end of file From 460b3c83dcc718c2dd4eb73c53eb79ad4e85c689 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 18:56:44 -0800 Subject: [PATCH 12/15] Remove unused using System.Linq Remove the unused 'using System.Linq;' directive from TypeMapAssemblyTargetGenerator.cs to clean up the code and avoid analyzer/compiler warnings. No functional changes. --- .../WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs index 58d224412..9f695a5df 100644 --- a/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs +++ b/src/Authoring/WinRT.SourceGenerator2/TypeMapAssemblyTargetGenerator.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis; namespace WindowsRuntime.SourceGenerator; From 64e1818dca0f48c500933899398dc5eef4057e54 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 19:29:24 -0800 Subject: [PATCH 13/15] Fix code-gen bugs and typos in generator Correct several mistakes in the source generator output and helper comments: - AuthoringExportTypesGenerator.Execute.cs: return the referenced activation factory instead of just calling it; add missing semicolon for "void* activationFactory"; fix null-check and assignment to use the actual result variable (result instead of obj); remove an extraneous generated block insertion; make the extern WindowsGetStringRawBuffer signature non-partial. - IndentedTextWriter.cs: remove a stray character in the XML doc closing tag. These changes fix compilation/runtime issues in generated code and clean up a documentation typo. --- .../AuthoringExportTypesGenerator.Execute.cs | 12 +++++------- .../Helpers/IndentedTextWriter.cs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs index c3f6721fa..d71e746af 100644 --- a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs @@ -185,7 +185,7 @@ public static unsafe class ManagedExports } else if (info.Options.MergeReferencedActivationFactories) { - writer.WriteLine("ReferencedManagedExports.GetActivationFactory(activatableClassId);"); + writer.WriteLine("return ReferencedManagedExports.GetActivationFactory(activatableClassId);"); } } @@ -226,7 +226,7 @@ public static nint GetActivationFactory(string activatableClassId) using (writer.WriteBlock()) { - writer.WriteLine("void* activationFactory"); + writer.WriteLine("void* activationFactory;"); // Iterate through all transitvely referenced activation factories from other components and use the first that succeeds foreach (string managedExportsTypeName in info.MergedManagedExportsTypeNames) @@ -323,14 +323,14 @@ public static HRESULT DllGetActivationFactory(HSTRING activatableClassId, IActiv { IActivationFactory* result = ManagedExports.GetActivationFactory(managedActivatableClassId); - if ((void*)obj is null) + if ((void*)result is null) { *factory = null; return CLASS_E_CLASSNOTAVAILABLE; } - *factory = (void*)obj; + *factory = (void*)result; return S_OK; } @@ -338,8 +338,6 @@ public static HRESULT DllGetActivationFactory(HSTRING activatableClassId, IActiv { return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); } - - {GenerateNativeDllGetActivationFactoryImpl(context)} } /// @@ -383,7 +381,7 @@ file static class WindowsRuntimeImports /// [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", EntryPoint = "WindowsGetStringRawBuffer", ExactSpelling = true)] [SupportedOSPlatform("windows6.2")] - public static extern partial char* WindowsGetStringRawBuffer(HSTRING @string, uint* length); + public static extern char* WindowsGetStringRawBuffer(HSTRING @string, uint* length); } """); diff --git a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs index 5e795b8cf..64ef7c104 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Helpers/IndentedTextWriter.cs @@ -52,7 +52,7 @@ internal ref struct IndentedTextWriter /// /// Creates a new value with the specified parameters. - /// v + /// /// The number of constant characters outside of interpolation expressions in the interpolated string. /// The number of interpolation expressions in the interpolated string. public IndentedTextWriter(int literalLength, int formattedCount) From c6144b97e292c61fda29488b7e6929294c856ec0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 7 Feb 2026 19:29:38 -0800 Subject: [PATCH 14/15] Optimize identifier escaping and keyword handling Add pragma to suppress IDE0046 and refactor string-escaping logic to avoid allocating the interpolated-string handler for already-valid identifiers. When escaping, prefix a leading '_' if the first char can't start an identifier, replace invalid identifier chars with '_', and detect keywords to prefix an underscore so the result is a valid C# identifier. --- .../Extensions/StringExtensions.cs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs index e6351d53a..f9dbf63ee 100644 --- a/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs +++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/StringExtensions.cs @@ -4,6 +4,8 @@ using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.CSharp; +#pragma warning disable IDE0046 + namespace WindowsRuntime.SourceGenerator; /// @@ -27,22 +29,41 @@ public string EscapeIdentifierName() return "_"; } + string escapedValue; + // Optimization: we can just return the name as is if it's already a valid identifier. Generally speaking this // should always be the case for assembly names (except if they have one or more parts separated by a '.'). if (SyntaxFacts.IsValidIdentifier(value)) { - return value; + escapedValue = value; } + else + { + DefaultInterpolatedStringHandler handler = new(literalLength: value.Length, formattedCount: 0); - DefaultInterpolatedStringHandler handler = new(literalLength: value.Length, formattedCount: 0); + // Add a leading '_' if the first character is not a valid start character for an identifier + if (!SyntaxFacts.IsIdentifierStartCharacter(value[0])) + { + handler.AppendFormatted('_'); + } + + // Build the escaped name by just replacing all invalid characters with '_' + foreach (char c in value) + { + handler.AppendFormatted(SyntaxFacts.IsIdentifierPartCharacter(c) ? c : '_'); + } + + escapedValue = handler.ToStringAndClear(); + } - // Build the escaped name by just replacing all invalid characters with '_' - foreach (char c in value) + // If the resulting identifier is still not valid (eg. a keyword like 'class'), + // adjust it so that the final result is actually a valid identifier we can use. + if (SyntaxFacts.GetKeywordKind(escapedValue) is not SyntaxKind.None) { - handler.AppendFormatted(SyntaxFacts.IsIdentifierPartCharacter(c) ? c : '_'); + return $"_{escapedValue}"; } - return handler.ToStringAndClear(); + return escapedValue; } } } \ No newline at end of file From 332ec73f07d641035b505cba58e2e5a78f2bf620 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 10 Feb 2026 11:47:12 -0800 Subject: [PATCH 15/15] Replace Authoring exports attribute with Component Rename the assembly exports attribute from the 'Authoring' variant to the 'Component' variant and update usages. The source generator now emits WindowsRuntimeComponentAssemblyExportsType and looks up that type when locating export types. The attribute class/file was renamed to WindowsRuntimeComponentAssemblyExportsTypeAttribute and its docs were updated to reference WindowsRuntimeComponentAssemblyAttribute so generator and runtime consistently use the new attribute name. --- .../AuthoringExportTypesGenerator.Execute.cs | 4 ++-- .../AuthoringExportTypesGenerator.Helpers.cs | 2 +- ...indowsRuntimeComponentAssemblyExportsTypeAttribute.cs} | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/WinRT.Runtime2/InteropServices/Attributes/{WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs => WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs} (85%) diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs index d71e746af..0eb151d99 100644 --- a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Execute.cs @@ -114,13 +114,13 @@ public static void EmitManagedExports(SourceProductionContext context, Authoring IndentedTextWriter writer = new(literalLength: 0, formattedCount: 0); - // Emit the '[WindowsRuntimeAuthoringAssemblyExportsType]' attribute so other tooling (including this same generator) + // Emit the '[WindowsRuntimeComponentAssemblyExportsType]' attribute so other tooling (including this same generator) // can reliably find the generated export types from other assemblies, which is needed when merging activation factories. writer.WriteLine($$""" // #pragma warning disable - [assembly: global::WindowsRuntime.InteropServices.WindowsRuntimeAuthoringAssemblyExportsType(typeof(global::ABI.{{info.AssemblyName.EscapeIdentifierName()}}.ManagedExports))] + [assembly: global::WindowsRuntime.InteropServices.WindowsRuntimeComponentAssemblyExportsType(typeof(global::ABI.{{info.AssemblyName.EscapeIdentifierName()}}.ManagedExports))] namespace ABI.{{info.AssemblyName.EscapeIdentifierName()}}; diff --git a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs index 0901de457..d00837804 100644 --- a/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs +++ b/src/Authoring/WinRT.SourceGenerator2/AuthoringExportTypesGenerator.Helpers.cs @@ -30,7 +30,7 @@ public static bool TryGetDependentAssemblyExportsTypeName( [NotNullWhen(true)] out string? name) { // Get the attribute to lookup to find the target type to use - INamedTypeSymbol winRTAssemblyExportsTypeAttributeSymbol = compilation.GetTypeByMetadataName("WindowsRuntime.InteropServices.WindowsRuntimeAuthoringAssemblyExportsTypeAttribute")!; + INamedTypeSymbol winRTAssemblyExportsTypeAttributeSymbol = compilation.GetTypeByMetadataName("WindowsRuntime.InteropServices.WindowsRuntimeComponentAssemblyExportsTypeAttribute")!; // Make sure the assembly does have the attribute on it if (!assemblySymbol.TryGetAttributeWithType(winRTAssemblyExportsTypeAttributeSymbol, out AttributeData? attributeData)) diff --git a/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs similarity index 85% rename from src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs rename to src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs index b859206f5..9bdc7db4e 100644 --- a/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeAuthoringAssemblyExportsTypeAttribute.cs +++ b/src/WinRT.Runtime2/InteropServices/Attributes/WindowsRuntimeComponentAssemblyExportsTypeAttribute.cs @@ -8,7 +8,7 @@ namespace WindowsRuntime.InteropServices; /// /// Indicates which type contains the managed GetActivationFactory method to invoke for authoring scenarios. This -/// attribute is only meant to be used within an assembly annotated with . +/// attribute is only meant to be used within an assembly annotated with . /// /// /// This attribute is emitted by the CsWinRT generator, and it is not meant to be used directly. @@ -19,13 +19,13 @@ namespace WindowsRuntime.InteropServices; DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] -public sealed class WindowsRuntimeAuthoringAssemblyExportsTypeAttribute : Attribute +public sealed class WindowsRuntimeComponentAssemblyExportsTypeAttribute : Attribute { /// - /// Creates a new instance with the specified parameters. + /// Creates a new instance with the specified parameters. /// /// The type that contains the managed GetActivationFactory method to invoke for authoring scenarios. - public WindowsRuntimeAuthoringAssemblyExportsTypeAttribute(Type exportsType) + public WindowsRuntimeComponentAssemblyExportsTypeAttribute(Type exportsType) { ExportsType = exportsType; }