From 4b680f1a5645e5f63d64a8f9c1933a4854ba7893 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 6 Jan 2026 13:17:46 -0800 Subject: [PATCH 01/41] Add HStringArrayMarshaller for HSTRING array marshalling Introduces HStringArrayMarshaller to handle marshalling and cleanup of arrays of Windows Runtime HSTRINGs. Includes methods for converting, copying, and freeing unmanaged HSTRING arrays, with error handling and memory management. --- .../Marshalling/HStringArrayMarshaller.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs new file mode 100644 index 000000000..2a6353812 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of the Windows Runtime HSTRING type. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class HStringArrayMarshaller +{ + /// + /// Converts a value to an unmanaged HSTRING. + /// + /// The value to convert. + /// The resulting HSTRING. + /// It is responsibility of callers to free the returned HSTRING value. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out HSTRING* array) + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + size = (uint)source.Length; + array = (HSTRING*)Marshal.AllocCoTaskMem(sizeof(HSTRING) * source.Length); + + int i = 0; + + try + { + for (i = 0; i < source.Length; i++) + { + array[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + Free(size: (uint)i, array); + + size = 0; + array = null; + + throw; + } + + } + + /// + /// Converts a value to an unmanaged HSTRING. + /// + /// The value to convert. + /// The resulting HSTRING. + /// It is responsibility of callers to free the returned HSTRING value. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, HSTRING* array) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (source.IsEmpty) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + int i = 0; + + try + { + // Marshal all items, and keep track of how many have been marshaled in case of an exception + for (i = 0; i < source.Length; i++) + { + array[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + FreeRange(size: (uint)i, array); + + throw; + } + } + + /// + /// Frees a range of an HSTRING reference array. + /// + /// The size of the array. + /// The input array. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void FreeRange(uint size, HSTRING* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + HStringMarshaller.Free(array[i]); + + array[i] = null; + } + } + + /// + /// Frees an HSTRING reference array. + /// + /// The size of the array. + /// The input array. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Free(uint size, HSTRING* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + HStringMarshaller.Free(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From c2ae7293c5e369b725f1e936c9fd8ba99db914d6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 9 Jan 2026 15:38:36 -0800 Subject: [PATCH 02/41] Add marshaller for blittable value type arrays Introduces BlittableValueTypeArrayMarshaller to handle conversion and copying between managed and unmanaged arrays of blittable Windows Runtime types. Includes methods for marshaling, copying, and freeing memory, with validation and error handling. --- .../BlittableValueTypeArrayMarshaller.cs | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs new file mode 100644 index 000000000..67c29cfd2 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of blittable Windows Runtime types. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class BlittableValueTypeArrayMarshaller +{ + /// + /// Marshals a managed array to an unmanaged Windows Runtime array. + /// + /// The type of elements in the array. + /// The source array. + /// The size of the array. + /// The resulting Windows Runtime array. + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out T* array) + where T : unmanaged + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + size = (uint)source.Length; + array = (T*)Marshal.AllocCoTaskMem(sizeof(T) * source.Length); + + source.CopyTo(new Span(array, source.Length)); + } + + /// + /// Marshals an unmanaged Windows Runtime array to a managed array. + /// + /// The type of elements in the array. + /// The size of the array. + /// The source array. + /// The resulting managed array. + public static T[] ConvertToManaged(uint size, T* value) + where T : unmanaged + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + return new ReadOnlySpan(value, (int)size).ToArray(); + } + + /// + /// Copies items from a managed array to the target unmanaged Windows Runtime array. + /// + /// The type of elements in the array. + /// The source array. + /// The size of the array. + /// The destination array. + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* destination) + where T : unmanaged + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + source.CopyTo(new Span(destination, (int)size)); + } + + /// + /// Copies items from an unmanaged Windows Runtime array to the target managed array. + /// + /// The type of elements in the array. + /// The size of the array. + /// The source array. + /// The destination array. + public static void CopyToManaged(uint size, T* source, Span destination) + where T : unmanaged + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + new ReadOnlySpan(source, (int)size).CopyTo(destination); + } + + /// + /// Frees the specified array. + /// + /// The size of the array. + /// The input array. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Free(uint size, void* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From 21710940f2a31792ca32f020ce94322777025695 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 14:00:42 -0800 Subject: [PATCH 03/41] Refactor and rename blittable array marshaller class Renamed BlittableValueTypeArrayMarshaller to WindowsRuntimeBlittableValueTypeArrayMarshaller and made it a generic static class. Updated method signatures to remove redundant generic parameters and constraints, improving clarity and type safety. --- ...ntimeBlittableValueTypeArrayMarshaller.cs} | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) rename src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/{BlittableValueTypeArrayMarshaller.cs => WindowsRuntimeBlittableValueTypeArrayMarshaller.cs} (80%) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs similarity index 80% rename from src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs rename to src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs index 67c29cfd2..c61131291 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/BlittableValueTypeArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs @@ -11,21 +11,21 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// /// A marshaller for arrays of blittable Windows Runtime types. /// +/// THe type of elements in the array. [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] -public static unsafe class BlittableValueTypeArrayMarshaller +public static unsafe class WindowsRuntimeBlittableValueTypeArrayMarshaller + where T : unmanaged { /// /// Marshals a managed array to an unmanaged Windows Runtime array. /// - /// The type of elements in the array. /// The source array. /// The size of the array. /// The resulting Windows Runtime array. - public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out T* array) - where T : unmanaged + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out T* array) { if (source.IsEmpty) { @@ -44,12 +44,10 @@ public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, /// /// Marshals an unmanaged Windows Runtime array to a managed array. /// - /// The type of elements in the array. /// The size of the array. /// The source array. /// The resulting managed array. - public static T[] ConvertToManaged(uint size, T* value) - where T : unmanaged + public static T[] ConvertToManaged(uint size, T* value) { if (size == 0) { @@ -64,12 +62,10 @@ public static T[] ConvertToManaged(uint size, T* value) /// /// Copies items from a managed array to the target unmanaged Windows Runtime array. /// - /// The type of elements in the array. /// The source array. /// The size of the array. /// The destination array. - public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* destination) - where T : unmanaged + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* destination) { WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); @@ -86,12 +82,10 @@ public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* dest /// /// Copies items from an unmanaged Windows Runtime array to the target managed array. /// - /// The type of elements in the array. /// The size of the array. /// The source array. /// The destination array. - public static void CopyToManaged(uint size, T* source, Span destination) - where T : unmanaged + public static void CopyToManaged(uint size, T* source, Span destination) { WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); From 4d6f19414d00ceb4595e1bd950a5774e885ff98b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 14:20:22 -0800 Subject: [PATCH 04/41] Rename and update WindowsRuntimeBlittableValueTypeArrayMarshaller Renamed the file to use a generic type parameter in the filename and fixed a typo in the XML documentation. Also updated the Free method to use the generic type pointer for the array parameter. --- ... => WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/{WindowsRuntimeBlittableValueTypeArrayMarshaller.cs => WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs} (96%) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs similarity index 96% rename from src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs rename to src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs index c61131291..81fdfe44c 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs @@ -11,7 +11,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// /// A marshaller for arrays of blittable Windows Runtime types. /// -/// THe type of elements in the array. +/// The type of elements in the array. [Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] @@ -105,7 +105,7 @@ public static void CopyToManaged(uint size, T* source, Span destination) /// The size of the array. /// The input array. [MethodImpl(MethodImplOptions.NoInlining)] - public static void Free(uint size, void* array) + public static void Free(uint size, T* array) { if (size == 0) { From fd41cf719361055eb83199eb03f5bee587ddc320 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 14:20:27 -0800 Subject: [PATCH 05/41] Fix typo in comment in EventSource.cs Corrected 'THe' to 'The' in a comment to improve readability and maintain code quality. --- src/WinRT.Runtime2/InteropServices/Events/EventSource{T}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Runtime2/InteropServices/Events/EventSource{T}.cs b/src/WinRT.Runtime2/InteropServices/Events/EventSource{T}.cs index bffbb9b7f..74befaccb 100644 --- a/src/WinRT.Runtime2/InteropServices/Events/EventSource{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Events/EventSource{T}.cs @@ -88,7 +88,7 @@ public void Subscribe(T? handler) // That CCW will point the event invoker, ie. a stub on the event source state object. // This stub captures the event source state, and just invokes the target delegate. // If we don't need to register the handler, we still just add the new handler here. - // THe existing CCW will just pick it up the next time the native event is invoked. + // The existing CCW will just pick it up the next time the native event is invoked. state!.AddHandler(handler); if (registerHandler) From e2ea53105aea69a6add4d8c4f635ab1dabd7a93a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 14:20:39 -0800 Subject: [PATCH 06/41] Add marshaller for unmanaged WinRT value type arrays Introduced IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller interface and WindowsRuntimeUnmanagedValueTypeArrayMarshaller static class to support marshalling arrays of unmanaged Windows Runtime value types. These additions provide methods for converting, copying, and freeing arrays between managed and unmanaged representations, with support for custom element marshallers. --- ...alueTypeArrayElementMarshaller{T, TAbi}.cs | 35 +++++ ...anagedValueTypeArrayMarshaller{T, TAbi}.cs | 135 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller{T, TAbi}.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller{T, TAbi}.cs new file mode 100644 index 000000000..11743faf1 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller{T, TAbi}.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// An interface for marshalling implementations to support . +/// +/// The type of elements in the array. +/// The ABI type for type . +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public interface IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller + where T : unmanaged + where TAbi : unmanaged +{ + /// + /// Marshals an unmanaged Windows Runtime value type to its native representation. + /// + /// The input value to marshal. + /// The marshalled native value. + static abstract TAbi ConvertToUnmanaged(T value); + + /// + /// Marshals a native Windows Runtime value type to its managed representation. + /// + /// The input value to marshal. + /// The marshalled managed value. + static abstract T ConvertToManaged(TAbi value); +} diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs new file mode 100644 index 000000000..20ae46e21 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of unmanaged Windows Runtime types. +/// +/// The type of elements in the array. +/// The ABI type for type . +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeUnmanagedValueTypeArrayMarshaller + where T : unmanaged + where TAbi : unmanaged +{ + /// + /// The type of marshaller for each managed array element. + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out TAbi* array) + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + TAbi* destination = (TAbi*)Marshal.AllocCoTaskMem(sizeof(T) * source.Length); + + try + { + // Marshal all array elements with the provided element marshaller. + // We don't need to guard each call, as the ABI type is unmanaged. + for (int i = 0; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // If marshalling any element failed, release the allocated array to avoid + // leaking. This shouldn't really happen with unmanaged value type, since + // the conversions should be pretty trivial, but leave it just in case. + Marshal.FreeCoTaskMem((nint)destination); + } + + size = (uint)source.Length; + array = destination; + } + + /// + /// The type of marshaller for each managed array element. + public static T[] ConvertToManaged(uint size, TAbi* value) + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + T[] array = GC.AllocateUninitializedArray((int)size); + + for (int i = 0; i < size; i++) + { + array[i] = TElementMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, TAbi* destination) + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + for (int i = 0; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]); + } + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToManaged(uint size, TAbi* source, Span destination) + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); + } + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Free(uint size, TAbi* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From c69cc48e267cad76f650cc39dbd26b92a3b2dab3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 19:14:39 -0800 Subject: [PATCH 07/41] Add managed value type array marshaller for WinRT Introduces IWindowsRuntimeManagedValueTypeArrayElementMarshaller and WindowsRuntimeManagedValueTypeArrayMarshaller to support marshalling arrays of managed Windows Runtime value types. Also fixes exception handling in WindowsRuntimeUnmanagedValueTypeArrayMarshaller to rethrow after freeing memory. --- ...alueTypeArrayElementMarshaller{T, TAbi}.cs | 41 +++++ ...anagedValueTypeArrayMarshaller{T, TAbi}.cs | 166 ++++++++++++++++++ ...anagedValueTypeArrayMarshaller{T, TAbi}.cs | 2 + 3 files changed, 209 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeManagedValueTypeArrayElementMarshaller{T, TAbi}.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeManagedValueTypeArrayElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeManagedValueTypeArrayElementMarshaller{T, TAbi}.cs new file mode 100644 index 000000000..1f2052b0a --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeManagedValueTypeArrayElementMarshaller{T, TAbi}.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// An interface for marshalling implementations to support . +/// +/// The type of elements in the array. +/// The ABI type for type . +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public interface IWindowsRuntimeManagedValueTypeArrayElementMarshaller + where T : struct + where TAbi : unmanaged +{ + /// + /// Marshals an unmanaged Windows Runtime value type to its native representation. + /// + /// The input value to marshal. + /// The marshalled native value. + static abstract TAbi ConvertToUnmanaged(T value); + + /// + /// Marshals a native Windows Runtime value type to its managed representation. + /// + /// The input value to marshal. + /// The marshalled managed value. + static abstract T ConvertToManaged(TAbi value); + + /// + /// Disposes resources associated with an unmanaged value. + /// + /// The unmanaged value to dispose. + static abstract void Dispose(TAbi value); +} diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs new file mode 100644 index 000000000..5c78f856e --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of managed Windows Runtime types. +/// +/// The type of elements in the array. +/// The ABI type for type . +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeManagedValueTypeArrayMarshaller + where T : struct + where TAbi : unmanaged +{ + /// + /// The type of marshaller for each managed array element. + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out TAbi* array) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + TAbi* destination = (TAbi*)Marshal.AllocCoTaskMem(sizeof(T) * source.Length); + + int i = 0; + + try + { + // Marshal all array elements with the provided element marshaller. + // Because the native type contains resources, this might throw. + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Make sure to release all native resources for marshalled values + for (int j = 0; j < i; j++) + { + TElementMarshaller.Dispose(destination[j]); + } + + // Also release the allocated array to avoid leaking + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + /// The type of marshaller for each managed array element. + public static T[] ConvertToManaged(uint size, TAbi* value) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + T[] array = GC.AllocateUninitializedArray((int)size); + + for (int i = 0; i < size; i++) + { + array[i] = TElementMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, TAbi* destination) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + int i = 0; + + try + { + // Marshal the items in the input span + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Release resources for any items, if we failed + for (int j = 0; j < i; j++) + { + TElementMarshaller.Dispose(destination[j]); + } + + throw; + } + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToManaged(uint size, TAbi* source, Span destination) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); + } + } + + /// + /// The type of marshaller for each managed array element. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Free(uint size, TAbi* array) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (int i = 0; i < size; i++) + { + TElementMarshaller.Dispose(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs index 20ae46e21..24f752096 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -51,6 +51,8 @@ public static void ConvertToUnmanaged(ReadOnlySpan source // leaking. This shouldn't really happen with unmanaged value type, since // the conversions should be pretty trivial, but leave it just in case. Marshal.FreeCoTaskMem((nint)destination); + + throw; } size = (uint)source.Length; From 0488b420311ec8eeb143dbbe982406b563b8b895 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 10 Jan 2026 19:24:19 -0800 Subject: [PATCH 08/41] Add marshallers for Windows Runtime reference type arrays Introduces generic and non-generic marshallers for arrays of Windows Runtime reference types, including an interface for element marshalling. Also fixes a minor documentation typo in WindowsRuntimeObjectMarshaller. --- ...eReferenceTypeArrayElementMarshaller{T}.cs | 33 ++++ ...dowsRuntimeReferenceTypeArrayMarshaller.cs | 38 +++++ ...sRuntimeReferenceTypeArrayMarshaller{T}.cs | 141 ++++++++++++++++++ .../WindowsRuntimeObjectMarshaller.cs | 2 +- 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeReferenceTypeArrayElementMarshaller{T}.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeReferenceTypeArrayElementMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeReferenceTypeArrayElementMarshaller{T}.cs new file mode 100644 index 000000000..bd913731f --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeReferenceTypeArrayElementMarshaller{T}.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// An interface for marshalling implementations to support . +/// +/// The type of elements in the array. +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public unsafe interface IWindowsRuntimeReferenceTypeArrayElementMarshaller + where T : class +{ + /// + /// Marshals a Windows Runtime object to a instance. + /// + /// The input object to marshal. + /// A instance for . + static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(T? value); + + /// + /// Converts an unmanaged pointer to a Windows Runtime object to a managed object. + /// + /// The input object to convert to managed. + /// The resulting managed object. + static abstract T? ConvertToManaged(void* value); +} diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs new file mode 100644 index 000000000..5b3ad7158 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of reference Windows Runtime types. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeReferenceTypeArrayMarshaller +{ + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Free(uint size, void** array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (int i = 0; i < size; i++) + { + WindowsRuntimeUnknownMarshaller.Free(array[j]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs new file mode 100644 index 000000000..abab4b2cd --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of reference Windows Runtime types. +/// +/// The type of elements in the array. +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeReferenceTypeArrayMarshaller + where T : class +{ + /// + /// The type of marshaller for each managed array element. + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out void** array) + where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + void** destination = (void**)Marshal.AllocCoTaskMem(sizeof(void*) * source.Length); + + int i = 0; + + try + { + // Marshal all array elements with the provided element marshaller and detach their native pointers + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]).DetachThisPtrUnsafe(); + } + } + catch + { + // Make sure to release all marshalled objects so far (this shouldn't ever throw) + for (int j = 0; j < i; j++) + { + WindowsRuntimeUnknownMarshaller.Free(destination[j]); + } + + // Also release the allocated array to avoid leaking + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + /// The type of marshaller for each managed array element. + public static T?[] ConvertToManaged(uint size, void** value) + where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + T?[] array = new T[(int)size]; + + for (int i = 0; i < size; i++) + { + array[i] = TElementMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) + where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + int i = 0; + + try + { + // Marshal the items in the input span + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]).DetachThisPtrUnsafe(); + } + } + catch + { + // Release resources for any items, if we failed + for (int j = 0; j < i; j++) + { + WindowsRuntimeUnknownMarshaller.Free(destination[j]); + } + + throw; + } + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToManaged(uint size, void** source, Span destination) + where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/WindowsRuntimeObjectMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/WindowsRuntimeObjectMarshaller.cs index ac9be8284..08c8b115a 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/WindowsRuntimeObjectMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/WindowsRuntimeObjectMarshaller.cs @@ -72,7 +72,7 @@ static void ThrowNotSupportedException(object value) /// Converts an unmanaged pointer to a Windows Runtime object to a managed object. /// /// The input object to convert to managed. - /// The resulting managed managed object. + /// The resulting managed object. public static object? ConvertToManaged(void* value) { if (value is null) From d7fedc94ac6cf85086bc7cf9d2b24642faa879d3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 01:03:38 -0800 Subject: [PATCH 09/41] Fix loop variable type and index in array marshaller Changed the loop variable from int to uint and corrected the index variable from 'j' to 'i' in WindowsRuntimeReferenceTypeArrayMarshaller. This ensures proper iteration and memory management for arrays. --- .../SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs index 5b3ad7158..a4355f3c1 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs @@ -28,9 +28,9 @@ public static void Free(uint size, void** array) ArgumentNullException.ThrowIfNull(array); - for (int i = 0; i < size; i++) + for (uint i = 0; i < size; i++) { - WindowsRuntimeUnknownMarshaller.Free(array[j]); + WindowsRuntimeUnknownMarshaller.Free(array[i]); } Marshal.FreeCoTaskMem((nint)array); From 2058d8f31adfbe8a08cfe8fb7d9e999d8be8a5c5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 01:03:49 -0800 Subject: [PATCH 10/41] Add marshallers for Windows Runtime KeyValuePair arrays Introduces IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller and WindowsRuntimeKeyValuePairTypeArrayMarshaller to support marshalling arrays of KeyValuePair between managed and Windows Runtime representations. These types are marked obsolete and intended for internal use. --- ...ypeArrayElementMarshaller{TKey, TValue}.cs | 34 +++++ ...uePairTypeArrayMarshaller{TKey, TValue}.cs | 143 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller{TKey, TValue}.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller{TKey, TValue}.cs new file mode 100644 index 000000000..2c6d04021 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller{TKey, TValue}.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// An interface for marshalling implementations to support . +/// +/// The type of the key. +/// The type of the value. +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public unsafe interface IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller +{ + /// + /// Marshals a type to its native Windows Runtime representation. + /// + /// The input value to marshal. + /// The marshalled native value. + static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(KeyValuePair value); + + /// + /// Marshals a native Windows Runtime type to its managed representation. + /// + /// The input value to marshal. + /// The marshalled managed value. + static abstract KeyValuePair ConvertToManaged(void* value); +} diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs new file mode 100644 index 000000000..13eaab4f7 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of types. +/// +/// The type of the key. +/// The type of the value. +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeKeyValuePairTypeArrayMarshaller +{ + /// + /// The type of marshaller for each managed array element. + public static void ConvertToUnmanaged(ReadOnlySpan> source, out uint size, out void** array) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + void** destination = (void**)Marshal.AllocCoTaskMem(sizeof(void*) * source.Length); + + int i = 0; + + try + { + // Marshal all array elements (the ABI type for 'KeyValuePair<,>' is just 'void*') + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]).DetachThisPtrUnsafe(); + } + } + catch + { + // Release any allocated native 'IKeyValuePair<,>' values + for (int j = 0; j < i; j++) + { + WindowsRuntimeUnknownMarshaller.Free(destination[j]); + } + + // Also release the allocated array to avoid leaking + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + /// The type of marshaller for each managed array element. + public static KeyValuePair[] ConvertToManaged(uint size, void** value) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + KeyValuePair[] array = new KeyValuePair[(int)size]; + + for (int i = 0; i < size; i++) + { + array[i] = TElementMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToUnmanaged(ReadOnlySpan> source, uint size, void** destination) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + int i = 0; + + try + { + // Marshal the items in the input span + for (; i < source.Length; i++) + { + destination[i] = TElementMarshaller.ConvertToUnmanaged(source[i]).DetachThisPtrUnsafe(); + } + } + catch + { + // Release resources for any items, if we failed + for (int j = 0; j < i; j++) + { + WindowsRuntimeUnknownMarshaller.Free(destination[j]); + } + + throw; + } + } + + /// + /// The type of marshaller for each managed array element. + public static void CopyToManaged(uint size, void** source, Span> destination) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); + } + } +} \ No newline at end of file From 170fec4903ea537e70808ec016a92f097e3e1bbd Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:40:11 -0800 Subject: [PATCH 11/41] Move and update HStringArrayMarshaller for SzArrays Moved HStringArrayMarshaller to the SzArrays namespace and updated its implementation to use void** instead of HSTRING*, with improved exception safety and nullable string support. Also updated WindowsRuntimeReferenceTypeArrayMarshaller to accept nullable types and removed an unused using directive. --- .../Marshalling/HStringArrayMarshaller.cs | 142 ----------------- .../SzArrays/HStringArrayMarshaller.cs | 149 ++++++++++++++++++ ...uePairTypeArrayMarshaller{TKey, TValue}.cs | 1 - ...sRuntimeReferenceTypeArrayMarshaller{T}.cs | 4 +- 4 files changed, 151 insertions(+), 145 deletions(-) delete mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs deleted file mode 100644 index 2a6353812..000000000 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/HStringArrayMarshaller.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace WindowsRuntime.InteropServices.Marshalling; - -/// -/// A marshaller for arrays of the Windows Runtime HSTRING type. -/// -[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] -[EditorBrowsable(EditorBrowsableState.Never)] -public static unsafe class HStringArrayMarshaller -{ - /// - /// Converts a value to an unmanaged HSTRING. - /// - /// The value to convert. - /// The resulting HSTRING. - /// It is responsibility of callers to free the returned HSTRING value. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out HSTRING* array) - { - if (source.IsEmpty) - { - size = 0; - array = null; - - return; - } - - size = (uint)source.Length; - array = (HSTRING*)Marshal.AllocCoTaskMem(sizeof(HSTRING) * source.Length); - - int i = 0; - - try - { - for (i = 0; i < source.Length; i++) - { - array[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); - } - } - catch - { - Free(size: (uint)i, array); - - size = 0; - array = null; - - throw; - } - - } - - /// - /// Converts a value to an unmanaged HSTRING. - /// - /// The value to convert. - /// The resulting HSTRING. - /// It is responsibility of callers to free the returned HSTRING value. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void CopyToUnmanaged(ReadOnlySpan source, uint size, HSTRING* array) - { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); - - if (source.IsEmpty) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - int i = 0; - - try - { - // Marshal all items, and keep track of how many have been marshaled in case of an exception - for (i = 0; i < source.Length; i++) - { - array[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); - } - } - catch - { - FreeRange(size: (uint)i, array); - - throw; - } - } - - /// - /// Frees a range of an HSTRING reference array. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeRange(uint size, HSTRING* array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (uint i = 0; i < size; i++) - { - HStringMarshaller.Free(array[i]); - - array[i] = null; - } - } - - /// - /// Frees an HSTRING reference array. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Free(uint size, HSTRING* array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (uint i = 0; i < size; i++) - { - HStringMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } -} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs new file mode 100644 index 000000000..3a7b2268d --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of the Windows Runtime HSTRING type. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class HStringArrayMarshaller +{ + /// + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out void** array) + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + void** destination = (void**)Marshal.AllocCoTaskMem(sizeof(void*) * source.Length); + + int i = 0; + + try + { + // Marshal all input 'string'-s with 'HStringMarshaller' (note that 'HSTRING' is not a COM object) + for (; i < source.Length; i++) + { + destination[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Make sure to release all marshalled objects so far (this shouldn't ever throw) + for (int j = 0; j < i; j++) + { + HStringMarshaller.Free(destination[j]); + } + + // Also release the allocated array to avoid leaking + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + public static string[] ConvertToManaged(uint size, void** value) + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + string[] array = new string[(int)size]; + + for (int i = 0; i < size; i++) + { + array[i] = HStringMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + int i = 0; + + try + { + // Marshal the items in the input span + for (; i < source.Length; i++) + { + destination[i] = HStringMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Release resources for any items, if we failed + for (int j = 0; j < i; j++) + { + HStringMarshaller.Free(destination[j]); + } + + throw; + } + } + + /// + public static void CopyToManaged(uint size, void** source, Span destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = HStringMarshaller.ConvertToManaged(source[i]); + } + } + + /// + public static void Free(uint size, void** array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + HStringMarshaller.Free(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs index 13eaab4f7..2f9f7679a 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace WindowsRuntime.InteropServices.Marshalling; diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs index abab4b2cd..aa6b46999 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -20,7 +20,7 @@ public static unsafe class WindowsRuntimeReferenceTypeArrayMarshaller { /// /// The type of marshaller for each managed array element. - public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out void** array) + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out void** array) where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller { if (source.IsEmpty) @@ -85,7 +85,7 @@ public static void ConvertToUnmanaged(ReadOnlySpan source /// /// The type of marshaller for each managed array element. - public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller { WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); From 040229239a305f635432ee22be753c9ce87fdb8e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:40:16 -0800 Subject: [PATCH 12/41] Add TypeArrayMarshaller for WinRT Type arrays Introduces TypeArrayMarshaller to handle marshalling of arrays of Windows Runtime Type objects. Provides methods for converting, copying, and freeing unmanaged and managed representations, with proper resource management and error handling. --- .../SzArrays/TypeArrayMarshaller.cs | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs new file mode 100644 index 000000000..da13fa94a --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of the Windows Runtime type. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class TypeArrayMarshaller +{ + /// + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out ABI.System.Type* array) + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + ABI.System.Type* destination = (ABI.System.Type*)Marshal.AllocCoTaskMem(sizeof(ABI.System.Type) * source.Length); + + int i = 0; + + try + { + // Marshal all input 'Type' objects with its marshaller (note the ABI type will be a value type) + for (; i < source.Length; i++) + { + destination[i] = ABI.System.TypeMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Dispose each ABI value (it contains an 'HSTRING' which we should release) + for (int j = 0; j < i; j++) + { + ABI.System.TypeMarshaller.Dispose(destination[j]); + } + + // Also release the allocated array to avoid leaking + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + public static Type?[] ConvertToManaged(uint size, ABI.System.Type* value) + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + Type?[] array = new Type[(int)size]; + + for (int i = 0; i < size; i++) + { + array[i] = ABI.System.TypeMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, ABI.System.Type* destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + int i = 0; + + try + { + // Marshal the items in the input span + for (; i < source.Length; i++) + { + destination[i] = ABI.System.TypeMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Release resources for any items, if we failed + for (int j = 0; j < i; j++) + { + ABI.System.TypeMarshaller.Dispose(destination[j]); + } + + throw; + } + } + + /// + public static void CopyToManaged(uint size, ABI.System.Type* source, Span destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = ABI.System.TypeMarshaller.ConvertToManaged(source[i]); + } + } + + /// + public static void Free(uint size, ABI.System.Type* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + ABI.System.TypeMarshaller.Dispose(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From 88a41c747fde2bcd51487e52e26774a59c95e627 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:46:01 -0800 Subject: [PATCH 13/41] Add ExceptionArrayMarshaller for WinRT exception arrays Introduces a marshaller for arrays of Windows Runtime Exception types, providing methods for converting, copying, and freeing unmanaged and managed representations. This supports interop scenarios where arrays of exceptions need to be marshalled between managed and unmanaged code. --- .../SzArrays/ExceptionArrayMarshaller.cs | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs new file mode 100644 index 000000000..58b79f2f3 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of the Windows Runtime type. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class ExceptionArrayMarshaller +{ + /// + public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, out ABI.System.Exception* array) + { + if (source.IsEmpty) + { + size = 0; + array = null; + + return; + } + + ABI.System.Exception* destination = (ABI.System.Exception*)Marshal.AllocCoTaskMem(sizeof(ABI.System.Exception) * source.Length); + + try + { + // Marshal all input 'Exception'-s with 'ExceptionMarshaller' (note that 'HResult' is blittable) + for (int i = 0; i < source.Length; i++) + { + destination[i] = ABI.System.ExceptionMarshaller.ConvertToUnmanaged(source[i]); + } + } + catch + { + // Release the allocated array to avoid leaking (this shouldn't really happen) + Marshal.FreeCoTaskMem((nint)destination); + + throw; + } + + size = (uint)source.Length; + array = destination; + } + + /// + public static Exception?[] ConvertToManaged(uint size, ABI.System.Exception* value) + { + if (size == 0) + { + return []; + } + + ArgumentNullException.ThrowIfNull(value); + + Exception?[] array = new Exception[(int)size]; + + for (int i = 0; i < size; i++) + { + array[i] = ABI.System.ExceptionMarshaller.ConvertToManaged(value[i]); + } + + return array; + } + + /// + public static void CopyToUnmanaged(ReadOnlySpan source, uint size, ABI.System.Exception* destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(destination); + + for (int i = 0; i < source.Length; i++) + { + destination[i] = ABI.System.ExceptionMarshaller.ConvertToUnmanaged(source[i]); + } + } + + /// + public static void CopyToManaged(uint size, ABI.System.Exception* source, Span destination) + { + WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(source); + + for (uint i = 0; i < size; i++) + { + destination[(int)i] = ABI.System.ExceptionMarshaller.ConvertToManaged(source[i]); + } + } + + /// + public static void Free(uint size, ABI.System.Exception* array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From 79ed70379c4a12d68ce7ed9b1d6f489358e56eee Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:46:08 -0800 Subject: [PATCH 14/41] Remove unnecessary MethodImpl attributes from marshallers Eliminated [MethodImpl(MethodImplOptions.NoInlining)] attributes and related using directives from Free methods in array marshaller classes to simplify code and reduce unnecessary metadata. --- .../WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs | 2 -- .../WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs | 2 -- .../SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs | 2 -- .../WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs index 81fdfe44c..d6c4cbd9e 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace WindowsRuntime.InteropServices.Marshalling; @@ -104,7 +103,6 @@ public static void CopyToManaged(uint size, T* source, Span destination) /// /// The size of the array. /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] public static void Free(uint size, T* array) { if (size == 0) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs index 5c78f856e..56f036726 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace WindowsRuntime.InteropServices.Marshalling; @@ -145,7 +144,6 @@ public static void CopyToManaged(uint size, TAbi* source, Sp /// /// The type of marshaller for each managed array element. - [MethodImpl(MethodImplOptions.NoInlining)] public static void Free(uint size, TAbi* array) where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs index a4355f3c1..5bac06b19 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace WindowsRuntime.InteropServices.Marshalling; @@ -18,7 +17,6 @@ namespace WindowsRuntime.InteropServices.Marshalling; public static unsafe class WindowsRuntimeReferenceTypeArrayMarshaller { /// - [MethodImpl(MethodImplOptions.NoInlining)] public static void Free(uint size, void** array) { if (size == 0) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs index 24f752096..a225ab099 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace WindowsRuntime.InteropServices.Marshalling; @@ -122,7 +121,6 @@ public static void CopyToManaged(uint size, TAbi* source, Sp } /// - [MethodImpl(MethodImplOptions.NoInlining)] public static void Free(uint size, TAbi* array) { if (size == 0) From 71b2b7c62f0379432bf882db45953283b24013ed Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:48:49 -0800 Subject: [PATCH 15/41] Move Free method to generic marshaller class Deleted WindowsRuntimeReferenceTypeArrayMarshaller.cs and moved the Free method to WindowsRuntimeReferenceTypeArrayMarshaller.cs. This consolidates array marshalling logic into the generic class and removes the obsolete static class. --- ...dowsRuntimeReferenceTypeArrayMarshaller.cs | 36 ------------------- ...sRuntimeReferenceTypeArrayMarshaller{T}.cs | 18 ++++++++++ 2 files changed, 18 insertions(+), 36 deletions(-) delete mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs deleted file mode 100644 index 5bac06b19..000000000 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; - -namespace WindowsRuntime.InteropServices.Marshalling; - -/// -/// A marshaller for arrays of reference Windows Runtime types. -/// -[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] -[EditorBrowsable(EditorBrowsableState.Never)] -public static unsafe class WindowsRuntimeReferenceTypeArrayMarshaller -{ - /// - public static void Free(uint size, void** array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (uint i = 0; i < size; i++) - { - WindowsRuntimeUnknownMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } -} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs index aa6b46999..8c8ce3f6f 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -138,4 +138,22 @@ public static void CopyToManaged(uint size, void** source, S destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); } } + + /// + public static void Free(uint size, void** array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + WindowsRuntimeUnknownMarshaller.Free(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } } \ No newline at end of file From d9ce60d72b842da9a0a91f3de3cf754f6340f7c4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 12:55:37 -0800 Subject: [PATCH 16/41] Refactor array helper usage in marshallers Removed WindowsRuntimeArrayHelpers and replaced its usage with the new WindowsRuntimeArrayMarshallerHelpers in all relevant marshaller classes. Updated validation logic to use the new helper methods, improving clarity and encapsulation of array size checks. --- .../WindowsRuntimeArrayHelpers.cs | 176 ------------------ .../SzArrays/ExceptionArrayMarshaller.cs | 4 +- .../SzArrays/HStringArrayMarshaller.cs | 4 +- .../SzArrays/TypeArrayMarshaller.cs | 4 +- .../WindowsRuntimeArrayMarshallerHelpers.cs | 54 ++++++ ...imeBlittableValueTypeArrayMarshaller{T}.cs | 4 +- ...uePairTypeArrayMarshaller{TKey, TValue}.cs | 4 +- ...anagedValueTypeArrayMarshaller{T, TAbi}.cs | 4 +- ...sRuntimeReferenceTypeArrayMarshaller{T}.cs | 4 +- ...anagedValueTypeArrayMarshaller{T, TAbi}.cs | 4 +- 10 files changed, 70 insertions(+), 192 deletions(-) delete mode 100644 src/WinRT.Runtime2/InteropServices/InteropDllExports/WindowsRuntimeArrayHelpers.cs create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeArrayMarshallerHelpers.cs diff --git a/src/WinRT.Runtime2/InteropServices/InteropDllExports/WindowsRuntimeArrayHelpers.cs b/src/WinRT.Runtime2/InteropServices/InteropDllExports/WindowsRuntimeArrayHelpers.cs deleted file mode 100644 index 904001354..000000000 --- a/src/WinRT.Runtime2/InteropServices/InteropDllExports/WindowsRuntimeArrayHelpers.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WindowsRuntime.InteropServices.Marshalling; - -namespace WindowsRuntime.InteropServices; - -/// -/// A helper for generated marshaller types for Windows Runtime arrays. -/// -[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, - DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, - UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] -[EditorBrowsable(EditorBrowsableState.Never)] -public static unsafe class WindowsRuntimeArrayHelpers -{ - /// - /// Validates that the specified destination span has the required number of elements. - /// - /// The type of elements in the destination span. - /// The expected number of elements in the destination span. - /// The span to validate. - /// Thrown if the length of does not equal . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [StackTraceHidden] - public static void ValidateDestinationSize(uint size, Span destination) - { - if (destination.Length != size) - { - [StackTraceHidden] - static void ThrowArgumentException() => throw new ArgumentException("The destination array is too small.", nameof(destination)); - - ThrowArgumentException(); - } - } - - /// - /// Validates that the specified destination span has the required number of elements. - /// - /// The type of elements in the destination span. - /// The span to validate. - /// The expected number of elements in the destination span. - /// Thrown if the length of does not equal . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [StackTraceHidden] - public static void ValidateDestinationSize(ReadOnlySpan source, uint size) - { - if (source.Length != size) - { - [StackTraceHidden] - static void ThrowArgumentException() => throw new ArgumentException("The destination array is too small.", "destination"); - - ThrowArgumentException(); - } - } - - /// - /// Frees an HSTRING reference array. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeHStringArrayUnsafe(uint size, HSTRING* array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (int i = 0; i < size; i++) - { - HStringMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } - - /// - /// Frees an object reference array. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeObjectArrayUnsafe(uint size, void** array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (int i = 0; i < size; i++) - { - WindowsRuntimeUnknownMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } - - /// - /// Frees a reference array of values. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeTypeArrayUnsafe(uint size, ABI.System.Type* array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (int i = 0; i < size; i++) - { - HStringMarshaller.Free(array[i].Name); - } - - Marshal.FreeCoTaskMem((nint)array); - } - - /// - /// Frees a reference array of some blittable type. - /// - /// The size of the array. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeBlittableArrayUnsafe(uint size, void* array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - Marshal.FreeCoTaskMem((nint)array); - } - - /// - /// Frees a range of an HSTRING reference array. - /// - /// The offset to free the array up to. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeHStringRangeUnsafe(int offset, HSTRING* array) - { - for (int i = 0; i < offset; i++) - { - HStringMarshaller.Free(array[i]); - } - } - - /// - /// Frees a range of an object reference array. - /// - /// The offset to free the array up to. - /// The input array. - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FreeObjectRangeUnsafe(int offset, void** array) - { - for (int i = 0; i < offset; i++) - { - WindowsRuntimeUnknownMarshaller.Free(array[i]); - } - } -} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs index 58b79f2f3..ff19124bc 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/ExceptionArrayMarshaller.cs @@ -72,7 +72,7 @@ public static void ConvertToUnmanaged(ReadOnlySpan source, out uint /// public static void CopyToUnmanaged(ReadOnlySpan source, uint size, ABI.System.Exception* destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -90,7 +90,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, uint size, A /// public static void CopyToManaged(uint size, ABI.System.Exception* source, Span destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs index 3a7b2268d..14a06083d 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/HStringArrayMarshaller.cs @@ -80,7 +80,7 @@ public static string[] ConvertToManaged(uint size, void** value) /// public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -114,7 +114,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void /// public static void CopyToManaged(uint size, void** source, Span destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs index da13fa94a..b68e117da 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/TypeArrayMarshaller.cs @@ -80,7 +80,7 @@ public static void ConvertToUnmanaged(ReadOnlySpan source, out uint size, /// public static void CopyToUnmanaged(ReadOnlySpan source, uint size, ABI.System.Type* destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -114,7 +114,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, uint size, ABI.Sys /// public static void CopyToManaged(uint size, ABI.System.Type* source, Span destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeArrayMarshallerHelpers.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeArrayMarshallerHelpers.cs new file mode 100644 index 000000000..f8f75c654 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeArrayMarshallerHelpers.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +#pragma warning disable CA2208 + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A helper for marshaller types for Windows Runtime arrays. +/// +internal static unsafe class WindowsRuntimeArrayMarshallerHelpers +{ + /// + /// Validates that the specified destination span has the required number of elements. + /// + /// The expected number of elements in the destination span. + /// The length of the destination span. + /// Thrown if does not equal . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [StackTraceHidden] + public static void ValidateDestinationSize(uint sourceSize, int destinationSize) + { + if ((uint)destinationSize != sourceSize) + { + [StackTraceHidden] + static void ThrowArgumentException() => throw new ArgumentException("The destination array is too small.", "destination"); + + ThrowArgumentException(); + } + } + + /// + /// Validates that the specified destination span has the required number of elements. + /// + /// The length of the span to validate. + /// The expected number of elements in the destination span. + /// Thrown if does not equal . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [StackTraceHidden] + public static void ValidateDestinationSize(int sourceSize, uint destinationSize) + { + if ((uint)sourceSize != destinationSize) + { + [StackTraceHidden] + static void ThrowArgumentException() => throw new ArgumentException("The destination array is too small.", "destination"); + + ThrowArgumentException(); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs index d6c4cbd9e..bc9610774 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeBlittableValueTypeArrayMarshaller{T}.cs @@ -66,7 +66,7 @@ public static T[] ConvertToManaged(uint size, T* value) /// The destination array. public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -86,7 +86,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, uint size, T* destina /// The destination array. public static void CopyToManaged(uint size, T* source, Span destination) { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs index 2f9f7679a..ec3e5089c 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs @@ -89,7 +89,7 @@ public static KeyValuePair[] ConvertToManaged( public static void CopyToUnmanaged(ReadOnlySpan> source, uint size, void** destination) where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -125,7 +125,7 @@ public static void CopyToUnmanaged(ReadOnlySpan(uint size, void** source, Span> destination) where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs index 56f036726..209127fb6 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeManagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -91,7 +91,7 @@ public static T[] ConvertToManaged(uint size, TAbi* value) public static void CopyToUnmanaged(ReadOnlySpan source, uint size, TAbi* destination) where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -127,7 +127,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, u public static void CopyToManaged(uint size, TAbi* source, Span destination) where TElementMarshaller : IWindowsRuntimeManagedValueTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs index 8c8ce3f6f..70cff07fb 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -88,7 +88,7 @@ public static void ConvertToUnmanaged(ReadOnlySpan sourc public static void CopyToUnmanaged(ReadOnlySpan source, uint size, void** destination) where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -124,7 +124,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, public static void CopyToManaged(uint size, void** source, Span destination) where TElementMarshaller : IWindowsRuntimeReferenceTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs index a225ab099..f4d623391 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnmanagedValueTypeArrayMarshaller{T, TAbi}.cs @@ -85,7 +85,7 @@ public static T[] ConvertToManaged(uint size, TAbi* value) public static void CopyToUnmanaged(ReadOnlySpan source, uint size, TAbi* destination) where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(source, size); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(source.Length, size); if (size == 0) { @@ -105,7 +105,7 @@ public static void CopyToUnmanaged(ReadOnlySpan source, u public static void CopyToManaged(uint size, TAbi* source, Span destination) where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller { - WindowsRuntimeArrayHelpers.ValidateDestinationSize(size, destination); + WindowsRuntimeArrayMarshallerHelpers.ValidateDestinationSize(size, destination.Length); if (size == 0) { From 049ff0ccf295ae72b7f7bcfd09e9dd8ceb1bc93f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 11 Jan 2026 22:21:59 -0800 Subject: [PATCH 17/41] Add array marshaller references and methods Introduces TypeReference and MemberReference properties for various WindowsRuntime array marshallers and their related interfaces. Adds methods to retrieve references for marshalling operations such as ConvertToUnmanaged, ConvertToManaged, CopyToUnmanaged, CopyToManaged, and Free for array marshallers, including support for generic element and ABI types. --- .../References/InteropReferences.cs | 835 ++++++++++++++++++ 1 file changed, 835 insertions(+) diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 67b51c00d..009f31407 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -903,6 +903,66 @@ public InteropReferences( /// public TypeReference WindowsRuntimeArrayMarshaller => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeArrayMarshaller"u8); + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>. + /// + public TypeReference WindowsRuntimeBlittableValueTypeArrayMarshaller1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeBlittableValueTypeArrayMarshaller`1"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>. + /// + public TypeReference WindowsRuntimeManagedValueTypeArrayMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeManagedValueTypeArrayMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>. + /// + public TypeReference WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeUnmanagedValueTypeArrayMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>. + /// + public TypeReference WindowsRuntimeReferenceTypeArrayMarshaller1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeReferenceTypeArrayMarshaller`1"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>. + /// + public TypeReference WindowsRuntimeKeyValuePairTypeArrayMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeKeyValuePairTypeArrayMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeReferenceTypeArrayElementMarshaller<T>. + /// + public TypeReference IWindowsRuntimeReferenceTypeArrayElementMarshaller1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "IWindowsRuntimeReferenceTypeArrayElementMarshaller`1"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeArrayElementMarshaller<T, TAbi>. + /// + public TypeReference IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "IWindowsRuntimeManagedValueTypeArrayElementMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller<T, TAbi>. + /// + public TypeReference IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller<TKey, TValue>. + /// + public TypeReference IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller. + /// + public TypeReference TypeArrayMarshaller => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "TypeArrayMarshaller"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller. + /// + public TypeReference HStringArrayMarshaller => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "HStringArrayMarshaller"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller. + /// + public TypeReference ExceptionArrayMarshaller => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "ExceptionArrayMarshaller"u8); + /// /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeValueTypeMarshaller. /// @@ -1836,6 +1896,165 @@ public InteropReferences( _corLibTypeFactory.Void.MakePointerType(), Guid.ToValueTypeSignature().MakeByReferenceType()])); + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.ConvertToUnmanaged. + /// + public MemberReference TypeArrayMarshallerConvertToUnmanaged => field ??= TypeArrayMarshaller + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(Type.ToTypeSignature()), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + AbiType.ToValueTypeSignature().MakePointerType().MakeByReferenceType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.ConvertToManaged. + /// + public MemberReference TypeArrayMarshallerConvertToManaged => field ??= TypeArrayMarshaller + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: Type.ToReferenceTypeSignature().MakeSzArrayType(), + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiType.ToValueTypeSignature().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.CopyToUnmanaged. + /// + public MemberReference TypeArrayMarshallerCopyToUnmanaged => field ??= TypeArrayMarshaller + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(Type.ToTypeSignature()), + _corLibTypeFactory.UInt32, + AbiType.ToValueTypeSignature().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.CopyToManaged. + /// + public MemberReference TypeArrayMarshallerCopyToManaged => field ??= TypeArrayMarshaller + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiType.ToValueTypeSignature().MakePointerType(), + Span1.MakeGenericValueType(Type.ToTypeSignature())])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.Free. + /// + public MemberReference TypeArrayMarshallerFree => field ??= TypeArrayMarshaller + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiType.ToValueTypeSignature().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller.ConvertToUnmanaged. + /// + public MemberReference HStringArrayMarshallerConvertToUnmanaged => field ??= HStringArrayMarshaller + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(_corLibTypeFactory.String), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + _corLibTypeFactory.Void.MakePointerType().MakePointerType().MakeByReferenceType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller.ConvertToManaged. + /// + public MemberReference HStringArrayMarshallerConvertToManaged => field ??= HStringArrayMarshaller + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.String.MakeSzArrayType(), + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller.CopyToUnmanaged. + /// + public MemberReference HStringArrayMarshallerCopyToUnmanaged => field ??= HStringArrayMarshaller + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(_corLibTypeFactory.String), + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller.CopyToManaged. + /// + public MemberReference HStringArrayMarshallerCopyToManaged => field ??= HStringArrayMarshaller + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType(), + Span1.MakeGenericValueType(_corLibTypeFactory.String)])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.HStringArrayMarshaller.Free. + /// + public MemberReference HStringArrayMarshallerFree => field ??= HStringArrayMarshaller + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller.ConvertToUnmanaged. + /// + public MemberReference ExceptionArrayMarshallerConvertToUnmanaged => field ??= ExceptionArrayMarshaller + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(Exception.ToReferenceTypeSignature()), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + AbiException.ToValueTypeSignature().MakePointerType().MakeByReferenceType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller.ConvertToManaged. + /// + public MemberReference ExceptionArrayMarshallerConvertToManaged => field ??= ExceptionArrayMarshaller + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: Exception.ToReferenceTypeSignature().MakeSzArrayType(), + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiException.ToValueTypeSignature().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller.CopyToUnmanaged. + /// + public MemberReference ExceptionArrayMarshallerCopyToUnmanaged => field ??= ExceptionArrayMarshaller + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(Exception.ToReferenceTypeSignature()), + _corLibTypeFactory.UInt32, + AbiException.ToValueTypeSignature().MakePointerType()])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller.CopyToManaged. + /// + public MemberReference ExceptionArrayMarshallerCopyToManaged => field ??= ExceptionArrayMarshaller + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiException.ToValueTypeSignature().MakePointerType(), + Span1.MakeGenericValueType(Exception.ToReferenceTypeSignature())])); + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.ExceptionArrayMarshaller.Free. + /// + public MemberReference ExceptionArrayMarshallerFree => field ??= ExceptionArrayMarshaller + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + AbiException.ToValueTypeSignature().MakePointerType()])); + /// /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeValueTypeMarshaller.ConvertToUnmanagedUnsafe. /// @@ -1967,6 +2186,622 @@ public InteropReferences( returnType: _corLibTypeFactory.Int32, parameterTypes: [new TypeReference(_corLibTypeFactory.CorLibScope, "System"u8, "Exception"u8).ToReferenceTypeSignature()])); + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeReferenceTypeArrayElementMarshaller<T>.ConvertToUnmanaged. + /// + /// The input element type. + public MemberReference IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(TypeSignature elementType) + { + return IWindowsRuntimeReferenceTypeArrayElementMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeReferenceTypeArrayElementMarshaller<T>.ConvertToManaged. + /// + /// The input element type. + public MemberReference IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(TypeSignature elementType) + { + return IWindowsRuntimeReferenceTypeArrayElementMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeArrayElementMarshaller<T, TAbi>.ConvertToUnmanaged. + /// + /// The input element type. + /// The ABI type. + public MemberReference IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 1), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeArrayElementMarshaller<T, TAbi>.ConvertToManaged. + /// + /// The input element type. + /// The ABI type. + public MemberReference IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 1)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeArrayElementMarshaller<T, TAbi>.Dispose. + /// + /// The input element type. + /// The ABI type. + public MemberReference IWindowsRuntimeManagedValueTypeArrayElementMarshallerDispose(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("Dispose"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 1)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller<T, TAbi>.ConvertToUnmanaged. + /// + /// The input element type. + /// The ABI type. + public MemberReference IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 1), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller<T, TAbi>.ConvertToManaged. + /// + /// The input element type. + /// The ABI type. + public MemberReference IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 1)])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller<TKey, TValue>.ConvertToUnmanaged. + /// + /// The input key type. + /// The input value type. + public MemberReference IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(TypeSignature keyType, TypeSignature valueType) + { + return IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(), + parameterTypes: [ + KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1))])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller<TKey, TValue>.ConvertToManaged. + /// + /// The input key type. + /// The input value type. + public MemberReference IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(TypeSignature keyType, TypeSignature valueType) + { + return IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)), + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.ConvertToUnmanaged. + /// + /// The input element type. + public MemberReference WindowsRuntimeBlittableValueTypeArrayMarshallerConvertToUnmanaged(TypeSignature elementType) + { + return WindowsRuntimeBlittableValueTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + new GenericParameterSignature(GenericParameterType.Type, 0).MakePointerType().MakeByReferenceType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.ConvertToManaged. + /// + /// The input element type. + public MemberReference WindowsRuntimeBlittableValueTypeArrayMarshallerConvertToManaged(TypeSignature elementType) + { + return WindowsRuntimeBlittableValueTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType(), + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 0).MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.CopyToUnmanaged. + /// + /// The input element type. + public MemberReference WindowsRuntimeBlittableValueTypeArrayMarshallerCopyToUnmanaged(TypeSignature elementType) + { + return WindowsRuntimeBlittableValueTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 0).MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.CopyToManaged. + /// + /// The input element type. + public MemberReference WindowsRuntimeBlittableValueTypeArrayMarshallerCopyToManaged(TypeSignature elementType) + { + return WindowsRuntimeBlittableValueTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 0).MakePointerType(), + Span1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0))])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.Free. + /// + /// The input element type. + public MemberReference WindowsRuntimeBlittableValueTypeArrayMarshallerFree(TypeSignature elementType) + { + return WindowsRuntimeBlittableValueTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 0).MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>.ConvertToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeManagedValueTypeArrayMarshallerConvertToUnmanaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeManagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType().MakeByReferenceType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>.ConvertToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeManagedValueTypeArrayMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeManagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType(), + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>.CopyToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeManagedValueTypeArrayMarshallerCopyToUnmanaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeManagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>.CopyToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeManagedValueTypeArrayMarshallerCopyToManaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeManagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType(), + Span1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0))])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeManagedValueTypeArrayMarshaller<T, TAbi>.Free<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeManagedValueTypeArrayMarshallerFree(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeManagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>.ConvertToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeUnmanagedValueTypeArrayMarshallerConvertToUnmanaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType().MakeByReferenceType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>.ConvertToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeUnmanagedValueTypeArrayMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType(), + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>.CopyToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeUnmanagedValueTypeArrayMarshallerCopyToUnmanaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>.CopyToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeUnmanagedValueTypeArrayMarshallerCopyToManaged(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType(), + Span1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0))])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnmanagedValueTypeArrayMarshaller<T, TAbi>.Free. + /// + /// The input element type. + /// The ABI type. + public MemberReference WindowsRuntimeUnmanagedValueTypeArrayMarshallerFree(TypeSignature elementType, TypeSignature abiType) + { + return WindowsRuntimeUnmanagedValueTypeArrayMarshaller2 + .MakeGenericReferenceType(elementType, abiType) + .ToTypeDefOrRef() + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + new GenericParameterSignature(GenericParameterType.Type, 1).MakePointerType()])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.ConvertToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerConvertToUnmanaged(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeReferenceTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + _corLibTypeFactory.Void.MakePointerType().MakePointerType().MakeByReferenceType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.ConvertToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerConvertToManaged(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeReferenceTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType(), + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.CopyToUnmanaged<TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerCopyToUnmanaged(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeReferenceTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0)), + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.CopyToManaged<TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerCopyToManaged(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeReferenceTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType(), + Span1.MakeGenericValueType(new GenericParameterSignature(GenericParameterType.Type, 0))])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.Free<TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerFree(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeReferenceTypeArrayMarshaller1 + .MakeGenericReferenceType(elementType) + .ToTypeDefOrRef() + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.ConvertToUnmanaged<TElementMarshaller>. + /// + /// The input key type. + /// The input value type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToUnmanaged(TypeSignature keyType, TypeSignature valueType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType( + KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1))), + _corLibTypeFactory.UInt32.MakeByReferenceType(), + _corLibTypeFactory.Void.MakePointerType().MakePointerType().MakeByReferenceType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.ConvertToManaged<TElementMarshaller>. + /// + /// The input key type. + /// The input value type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToManaged(TypeSignature keyType, TypeSignature valueType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)).MakeSzArrayType(), + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.CopyToUnmanaged<TElementMarshaller>. + /// + /// The input key type. + /// The input value type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToUnmanaged(TypeSignature keyType, TypeSignature valueType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToUnmanaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + ReadOnlySpan1.MakeGenericValueType( + KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1))), + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.CopyToManaged<TElementMarshaller>. + /// + /// The input key type. + /// The input value type. + /// The element marshaller type. + public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToManaged(TypeSignature keyType, TypeSignature valueType, TypeSignature elementMarshallerType) + { + return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("CopyToManaged"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + genericParameterCount: 1, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType(), + Span1.MakeGenericValueType( + KeyValuePair2.MakeGenericValueType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)))])) + .MakeGenericInstanceMethod(elementMarshallerType); + } + /// /// Gets the for AddEventHandler for . /// From a08821822297ccc99e7b2f2d6455bae2f4bf8177 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 12 Jan 2026 16:50:25 -0800 Subject: [PATCH 18/41] Remove generic parameter from Free method The Free method in WindowsRuntimeReferenceTypeArrayMarshaller no longer requires a generic parameter TElementMarshaller. Updated InteropReferences to reflect this change and simplified method signature and reference creation. --- .../References/InteropReferences.cs | 9 +++------ .../WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 009f31407..c4a65ec07 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -2690,22 +2690,19 @@ public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerCopyToManag } /// - /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.Free<TElementMarshaller>. + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.Free. /// /// The input element type. - /// The element marshaller type. - public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerFree(TypeSignature elementType, TypeSignature elementMarshallerType) + public MemberReference WindowsRuntimeReferenceTypeArrayMarshallerFree(TypeSignature elementType) { return WindowsRuntimeReferenceTypeArrayMarshaller1 .MakeGenericReferenceType(elementType) .ToTypeDefOrRef() .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Void, - genericParameterCount: 1, parameterTypes: [ _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType().MakePointerType()])) - .MakeGenericInstanceMethod(elementMarshallerType); + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); } /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs index 70cff07fb..ff9d001e3 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -140,7 +140,7 @@ public static void CopyToManaged(uint size, void** source, S } /// - public static void Free(uint size, void** array) + public static void Free(uint size, void** array) { if (size == 0) { From e046b7a155952a625c3e3d6a1f0ed2a7d9b46a24 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 12 Jan 2026 16:52:43 -0800 Subject: [PATCH 19/41] Remove WindowsRuntimeArrayHelpers references and marshalling stubs Eliminated usage and references to WindowsRuntimeArrayHelpers and its marshalling stubs from InteropMethodDefinitionFactory.SzArrayMarshaller and InteropReferences. This simplifies the codebase and removes dependency on shared array helper methods. --- ...thodDefinitionFactory.SzArrayMarshaller.cs | 57 ------------------- .../References/InteropReferences.cs | 45 --------------- 2 files changed, 102 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs index a41eb897a..dfc4987d6 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs @@ -2,11 +2,9 @@ // Licensed under the MIT License. using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.References; -using static AsmResolver.PE.DotNet.Cil.CilOpCodes; namespace WindowsRuntime.InteropGenerator.Factories; @@ -43,61 +41,6 @@ public static MethodDefinition Free( module.CorLibTypeFactory.UInt32, elementType.GetAbiType(interopReferences).Import(module).MakePointerType()])); - // For 'string', 'Type', reference types and blittable types, we can reuse the shared stubs from the 'WindowsRuntimeArrayHelpers' - // type in WinRT.Runtime.dll, to simplify the code and reduce binary size (as we can reuse all these stubs for multiple types). - if (SignatureComparer.IgnoreVersion.Equals(elementType, interopReferences.CorLibTypeFactory.String)) - { - freeMethod.CilMethodBody = new CilMethodBody - { - Instructions = - { - { Ldarg_0 }, - { Ldarg_1 }, - { Call, interopReferences.WindowsRuntimeArrayHelpersFreeHStringArrayUnsafe.Import(module) }, - { Ret } - } - }; - } - else if (SignatureComparer.IgnoreVersion.Equals(elementType, interopReferences.Type)) - { - freeMethod.CilMethodBody = new CilMethodBody - { - Instructions = - { - { Ldarg_0 }, - { Ldarg_1 }, - { Call, interopReferences.WindowsRuntimeArrayHelpersFreeTypeArrayUnsafe.Import(module) }, - { Ret } - } - }; - } - else if (!elementType.Resolve()!.IsValueType || elementType.IsConstructedKeyValuePairType(interopReferences)) - { - freeMethod.CilMethodBody = new CilMethodBody - { - Instructions = - { - { Ldarg_0 }, - { Ldarg_1 }, - { Call, interopReferences.WindowsRuntimeArrayHelpersFreeObjectArrayUnsafe.Import(module) }, - { Ret } - } - }; - } - else if (elementType.Resolve()!.IsByRefLike) // TODO: check for blittable - { - freeMethod.CilMethodBody = new CilMethodBody - { - Instructions = - { - { Ldarg_0 }, - { Ldarg_1 }, - { Call, interopReferences.WindowsRuntimeArrayHelpersFreeBlittableArrayUnsafe.Import(module) }, - { Ret } - } - }; - } - return freeMethod; } } diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index c4a65ec07..161ea0f70 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -988,11 +988,6 @@ public InteropReferences( /// public TypeReference RestrictedErrorInfo => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "RestrictedErrorInfo"u8); - /// - /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeArrayHelpers. - /// - public TypeReference WindowsRuntimeArrayHelpers => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "WindowsRuntimeArrayHelpers"u8); - /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.RestrictedErrorInfoExceptionMarshaller. /// @@ -2138,46 +2133,6 @@ public InteropReferences( returnType: _corLibTypeFactory.Void, parameterTypes: [_corLibTypeFactory.Int32])); - /// - /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeArrayHelpers.FreeHStringArrayUnsafe. - /// - public MemberReference WindowsRuntimeArrayHelpersFreeHStringArrayUnsafe => field ??= WindowsRuntimeArrayHelpers - .CreateMemberReference("FreeHStringArrayUnsafe"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); - - /// - /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeArrayHelpers.FreeObjectArrayUnsafe. - /// - public MemberReference WindowsRuntimeArrayHelpersFreeObjectArrayUnsafe => field ??= WindowsRuntimeArrayHelpers - .CreateMemberReference("FreeObjectArrayUnsafe"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); - - /// - /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeArrayHelpers.FreeTypeArrayUnsafe. - /// - public MemberReference WindowsRuntimeArrayHelpersFreeTypeArrayUnsafe => field ??= WindowsRuntimeArrayHelpers - .CreateMemberReference("FreeTypeArrayUnsafe"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - AbiType.ToValueTypeSignature().MakePointerType()])); - - /// - /// Gets the for WindowsRuntime.InteropServices.WindowsRuntimeArrayHelpers.FreeBlittableArrayUnsafe. - /// - public MemberReference WindowsRuntimeArrayHelpersFreeBlittableArrayUnsafe => field ??= WindowsRuntimeArrayHelpers - .CreateMemberReference("FreeBlittableArrayUnsafe"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType()])); - /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(Exception). /// From e824ee75f575058269bfcfd2abbd1de9185e48a9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 00:48:45 -0800 Subject: [PATCH 20/41] Refactor class comments to use Replaced explicit summary comments with in partial class declarations for consistency and improved documentation inheritance. --- .../InteropMethodDefinitionFactory.IEnumerator1Impl.cs | 6 ++---- .../Factories/InteropMethodDefinitionFactory.IList1Impl.cs | 6 ++---- ...teropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs | 6 ++---- .../InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs | 6 ++---- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs index c1ddf2a59..b2234b61c 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs @@ -15,10 +15,8 @@ namespace WindowsRuntime.InteropGenerator.Factories; -/// -/// A factory for interop method definitions. -/// -internal static partial class InteropMethodDefinitionFactory +/// +internal partial class InteropMethodDefinitionFactory { /// /// Helpers for impl types for interfaces. diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IList1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IList1Impl.cs index fc7ee5629..de4097bc8 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IList1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IList1Impl.cs @@ -15,10 +15,8 @@ namespace WindowsRuntime.InteropGenerator.Factories; -/// -/// A factory for interop method definitions. -/// -internal static partial class InteropMethodDefinitionFactory +/// +internal partial class InteropMethodDefinitionFactory { /// /// Helpers for impl types for interfaces. diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs index 1a71cc127..3e20eee50 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs @@ -14,10 +14,8 @@ namespace WindowsRuntime.InteropGenerator.Factories; -/// -/// A factory for interop method definitions. -/// -internal static partial class InteropMethodDefinitionFactory +/// +internal partial class InteropMethodDefinitionFactory { /// /// Helpers for impl types for interfaces. diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs index a70f3fcd9..2133d8990 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs @@ -14,10 +14,8 @@ namespace WindowsRuntime.InteropGenerator.Factories; -/// -/// A factory for interop method definitions. -/// -internal static partial class InteropMethodDefinitionFactory +/// +internal partial class InteropMethodDefinitionFactory { /// /// Helpers for impl types for interfaces. From dbd2eb03df5a2080c01a544831086392c42c1972 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 00:51:20 -0800 Subject: [PATCH 21/41] Add SzArrayElementMarshaller for array element marshalling Introduces the SzArrayElementMarshaller static class to InteropTypeDefinitionFactory, providing factory methods for generating element marshaller type definitions for SZ array types. Supports unmanaged value types, managed value types, KeyValuePair types, and reference types, each with appropriate interface implementations and stubbed conversion methods. --- ...initionFactory.SzArrayElementMarshaller.cs | 349 ++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs new file mode 100644 index 000000000..b32d655ce --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -0,0 +1,349 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +/// A factory for interop type definitions. +/// +internal partial class InteropTypeDefinitionFactory +{ + /// + /// Helpers for element marshaller types for SZ array types. + /// + public static class SzArrayElementMarshaller + { + /// + /// Creates a for the element marshaller for an unmanaged value type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + public static TypeDefinition UnmanagedValueType( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + + // Get the constructed 'IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller' interface type + TypeSignature interfaceType = interopReferences + .IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, elementAbiType); + + // We're declaring an 'internal abstract class' type + TypeDefinition elementMarshallerType = new( + ns: InteropUtf8NameFactory.TypeNamespace(arrayType), + name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), + attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, + baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) + { + Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } + }; + + // Define the 'ConvertToUnmanaged' method as follows: + // + // public static ConvertToUnmanaged( value) + MethodDefinition convertToUnmanagedMethod = new( + name: "ConvertToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToUnmanaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), + method: convertToUnmanagedMethod); + + // Define the 'ConvertToManaged' method as follows: + // + // public static ConvertToManaged( value) + MethodDefinition convertToManagedMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToManaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), + method: convertToManagedMethod); + + return elementMarshallerType; + } + + /// + /// Creates a for the element marshaller for a managed value type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + public static TypeDefinition ManagedValueType( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + + // Get the constructed 'IWindowsRuntimeManagedValueTypeArrayElementMarshaller' interface type + TypeSignature interfaceType = interopReferences + .IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 + .MakeGenericReferenceType(elementType, elementAbiType); + + // We're declaring an 'internal abstract class' type + TypeDefinition elementMarshallerType = new( + ns: InteropUtf8NameFactory.TypeNamespace(arrayType), + name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), + attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, + baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) + { + Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } + }; + + // Define the 'ConvertToUnmanaged' method as follows: + // + // public static ConvertToUnmanaged( value) + MethodDefinition convertToUnmanagedMethod = new( + name: "ConvertToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToUnmanaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), + method: convertToUnmanagedMethod); + + // Define the 'ConvertToManaged' method as follows: + // + // public static ConvertToManaged( value) + MethodDefinition convertToManagedMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToManaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), + method: convertToManagedMethod); + + // Define the 'Dispose' method as follows: + // + // public static void Dispose( value) + MethodDefinition disposeMethod = new( + name: "Dispose"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic(module.CorLibTypeFactory.Void, elementAbiType.Import(module))) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'Dispose' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerDispose(elementType, elementAbiType).Import(module), + method: disposeMethod); + + return elementMarshallerType; + } + + /// + /// Creates a for the element marshaller for a type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + public static TypeDefinition KeyValuePair( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + GenericInstanceTypeSignature elementType = (GenericInstanceTypeSignature)arrayType.BaseType; + TypeSignature keyType = elementType.TypeArguments[0]; + TypeSignature valueType = elementType.TypeArguments[1]; + + // Get the constructed 'IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller' interface type + TypeSignature interfaceType = interopReferences + .IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 + .MakeGenericReferenceType(keyType, valueType); + + // We're declaring an 'internal abstract class' type + TypeDefinition elementMarshallerType = new( + ns: InteropUtf8NameFactory.TypeNamespace(arrayType), + name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), + attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, + baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) + { + Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } + }; + + TypeSignature keyValuePairType = interopReferences.KeyValuePair2.MakeGenericValueType(keyType, valueType); + + // Define the 'ConvertToUnmanaged' method as follows: + // + // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(KeyValuePair<, > value) + MethodDefinition convertToUnmanagedMethod = new( + name: "ConvertToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature(), + parameterTypes: [keyValuePairType.Import(module)])) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToUnmanaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType).Import(module), + method: convertToUnmanagedMethod); + + // Define the 'ConvertToManaged' method as follows: + // + // public static KeyValuePair<, > ConvertToManaged(void* value) + MethodDefinition convertToManagedMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: keyValuePairType.Import(module), + parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToManaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(keyType, valueType).Import(module), + method: convertToManagedMethod); + + return elementMarshallerType; + } + + /// + /// Creates a for the element marshaller for a reference type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + public static TypeDefinition ReferenceType( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + + // Get the constructed 'IWindowsRuntimeReferenceTypeArrayElementMarshaller' interface type + TypeSignature interfaceType = interopReferences + .IWindowsRuntimeReferenceTypeArrayElementMarshaller1 + .MakeGenericReferenceType(elementType); + + // We're declaring an 'internal abstract class' type + TypeDefinition elementMarshallerType = new( + ns: InteropUtf8NameFactory.TypeNamespace(arrayType), + name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), + attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, + baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) + { + Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } + }; + + // Define the 'ConvertToUnmanaged' method as follows: + // + // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( value) + MethodDefinition convertToUnmanagedMethod = new( + name: "ConvertToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature(), + parameterTypes: [elementType.Import(module)])) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToUnmanaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType).Import(module), + method: convertToUnmanagedMethod); + + // Define the 'ConvertToManaged' method as follows: + // + // public static ConvertToManaged(void* value) + MethodDefinition convertToManagedMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: elementType.Import(module), + parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])) + { + CilInstructions = + { + { Ldnull }, + { Throw } + } + }; + + // Add and implement the 'ConvertToManaged' method + elementMarshallerType.AddMethodImplementation( + declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(elementType).Import(module), + method: convertToManagedMethod); + + return elementMarshallerType; + } + } +} \ No newline at end of file From db83d26abb4a4eb85f2a0b7648bf1a38206f3cf7 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 10:11:22 -0800 Subject: [PATCH 22/41] Refactor ConvertToManaged methods to use Nop and Ret Replaces the previous implementation of ConvertToManaged methods, which threw exceptions, with a Nop followed by a Ret instruction. Adds tracking of managed parameter method rewrites using a marker instruction for later code generation or analysis. --- ...initionFactory.SzArrayElementMarshaller.cs | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index b32d655ce..4a2f7d65a 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -3,6 +3,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; @@ -71,18 +72,20 @@ public static TypeDefinition UnmanagedValueType( declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), method: convertToUnmanagedMethod); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToManaged' method as follows: // // public static ConvertToManaged( value) MethodDefinition convertToManagedMethod = new( name: "ConvertToManaged"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + signature: MethodSignature.CreateStatic(elementType.Import(module), elementAbiType.Import(module))) { CilInstructions = { - { Ldnull }, - { Throw } + { nop_convertToManaged }, + { Ret } } }; @@ -91,6 +94,13 @@ public static TypeDefinition UnmanagedValueType( declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), method: convertToManagedMethod); + // Track rewriting the managed value for 'ConvertToManaged' + emitState.TrackManagedParameterMethodRewrite( + parameterType: elementType, + method: convertToManagedMethod, + marker: nop_convertToManaged, + parameterIndex: 0); + return elementMarshallerType; } @@ -145,18 +155,20 @@ public static TypeDefinition ManagedValueType( declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), method: convertToUnmanagedMethod); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToManaged' method as follows: // // public static ConvertToManaged( value) MethodDefinition convertToManagedMethod = new( name: "ConvertToManaged"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) + signature: MethodSignature.CreateStatic(elementType.Import(module), elementAbiType.Import(module))) { CilInstructions = { - { Ldnull }, - { Throw } + { nop_convertToManaged }, + { Ret } } }; @@ -165,6 +177,13 @@ public static TypeDefinition ManagedValueType( declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), method: convertToManagedMethod); + // Track rewriting the managed value for 'ConvertToManaged' + emitState.TrackManagedParameterMethodRewrite( + parameterType: elementType, + method: convertToManagedMethod, + marker: nop_convertToManaged, + parameterIndex: 0); + // Define the 'Dispose' method as follows: // // public static void Dispose( value) @@ -244,6 +263,8 @@ public static TypeDefinition KeyValuePair( declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType).Import(module), method: convertToUnmanagedMethod); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToManaged' method as follows: // // public static KeyValuePair<, > ConvertToManaged(void* value) @@ -256,8 +277,8 @@ public static TypeDefinition KeyValuePair( { CilInstructions = { - { Ldnull }, - { Throw } + { nop_convertToManaged }, + { Ret } } }; @@ -266,6 +287,13 @@ public static TypeDefinition KeyValuePair( declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(keyType, valueType).Import(module), method: convertToManagedMethod); + // Track rewriting the managed value for 'ConvertToManaged' + emitState.TrackManagedParameterMethodRewrite( + parameterType: elementType, + method: convertToManagedMethod, + marker: nop_convertToManaged, + parameterIndex: 0); + return elementMarshallerType; } @@ -321,6 +349,8 @@ public static TypeDefinition ReferenceType( declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType).Import(module), method: convertToUnmanagedMethod); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToManaged' method as follows: // // public static ConvertToManaged(void* value) @@ -333,8 +363,8 @@ public static TypeDefinition ReferenceType( { CilInstructions = { - { Ldnull }, - { Throw } + { nop_convertToManaged }, + { Ret } } }; @@ -343,6 +373,13 @@ public static TypeDefinition ReferenceType( declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(elementType).Import(module), method: convertToManagedMethod); + // Track rewriting the managed value for 'ConvertToManaged' + emitState.TrackManagedParameterMethodRewrite( + parameterType: elementType, + method: convertToManagedMethod, + marker: nop_convertToManaged, + parameterIndex: 0); + return elementMarshallerType; } } From 644351ec42543a52a5007522b3af6e8da68bd5f2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 10:39:32 -0800 Subject: [PATCH 23/41] Add support for ConvertToUnmanaged method rewrites Introduces logic to emit direct calls to ConvertToUnmanaged for non-blittable parameter types during interop method generation. Adds a new MethodRewriteInfo.ConvertToUnmanaged type, tracking and handling these rewrites in InteropGeneratorEmitState and InteropGenerator.Emit. Also includes a minor comment correction in ManagedValue.cs. --- ...MethodRewriteFactory.ConvertToUnmanaged.cs | 67 +++++++++++++++++++ ...nteropMethodRewriteFactory.ManagedValue.cs | 2 +- .../Generation/InteropGenerator.Emit.cs | 11 +++ .../Generation/InteropGeneratorEmitState.cs | 21 ++++++ .../MethodRewriteInfo.ConvertToUnmanaged.cs | 24 +++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs create mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs new file mode 100644 index 000000000..3622d5ce4 --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using WindowsRuntime.InteropGenerator.Resolvers; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +#pragma warning disable CS1573 + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +internal partial class InteropMethodRewriteFactory +{ + /// + /// Contains the logic for emitting direct calls to ConvertToUnmanaged for a given value (already on the stack). + /// + public static class ConvertToUnmanaged + { + /// + /// Performs two-pass code generation on a target method to emit a direct call to ConvertToUnmanaged. + /// + /// The parameter type that needs to be marshalled. + /// The target method to perform two-pass code generation on. + /// The target IL instruction to replace with the right set of specialized instructions. + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static void RewriteMethod( + TypeSignature parameterType, + MethodDefinition method, + CilInstruction marker, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + // Validate that we do have some IL body for the input method (this should always be the case) + if (method.CilMethodBody is not CilMethodBody body) + { + throw WellKnownInteropExceptions.MethodRewriteMissingBodyError(method); + } + + // If we didn't find the marker, it means the target method is either invalid + if (!body.Instructions.ReferenceContains(marker)) + { + throw WellKnownInteropExceptions.MethodRewriteMarkerInstructionNotFoundError(marker, method); + } + + // If the parameter type is blittable, we have nothing else to do (the value is already loaded) + if (parameterType.IsBlittable(interopReferences)) + { + return; + } + + // For any types, we always just forward directly to 'ConvertToUnmanaged', without any other changes + InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState); + + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.ConvertToUnmanaged().Import(module))); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs index d4d0fe9b1..5fe7d0d56 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs @@ -54,7 +54,7 @@ public static void RewriteMethod( if (parameterType.IsValueType) { - // If the return type is blittable, we have nothing else to do (the value is already loaded) + // If the parameter type is blittable, we have nothing else to do (the value is already loaded) if (parameterType.IsBlittable(interopReferences)) { return; diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 4a92f1f3c..43a4c2710 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -2017,6 +2017,17 @@ private static void RewriteMethodDefinitions( { switch (rewriteInfo) { + // Rewrite direct calls to 'ConvertToUnmanaged' + case MethodRewriteInfo.ConvertToUnmanaged convertToUnmanagedValue: + InteropMethodRewriteFactory.ConvertToUnmanaged.RewriteMethod( + parameterType: convertToUnmanagedValue.Type, + method: convertToUnmanagedValue.Method, + marker: convertToUnmanagedValue.Marker, + interopReferences: interopReferences, + emitState: emitState, + module: module); + break; + // Rewrite return values for managed types case MethodRewriteInfo.ReturnValue returnValueInfo: InteropMethodRewriteFactory.ReturnValue.RewriteMethod( diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs index 1009530b2..99cff9ebd 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -87,6 +87,27 @@ public TypeDefinition LookupTypeDefinition(TypeSignature typeSignature, string k throw WellKnownInteropExceptions.TrackedTypeDefinitionLookupError(typeSignature, key); } + /// + /// Tracks a method rewrite that involves emitting direct calls to ConvertToUnmanaged in the specified method. + /// + /// + /// + /// + public void TrackConvertToUnmanagedMethodRewrite( + TypeSignature parameterType, + MethodDefinition method, + CilInstruction marker) + { + ThrowIfReadOnly(); + + _methodRewriteInfos.Add(new MethodRewriteInfo.ConvertToUnmanaged + { + Type = parameterType, + Method = method, + Marker = marker + }); + } + /// /// Tracks a method rewrite that involves returning a value from the specified method at a given marker instruction. /// diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs new file mode 100644 index 000000000..c5d8c3f9e --- /dev/null +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsRuntime.InteropGenerator.Models; + +/// +internal partial class MethodRewriteInfo +{ + /// + /// Contains info for a target method for two-pass IL generation, for for emitting direct calls to ConvertToUnmanaged. + /// + /// + public sealed class ConvertToUnmanaged : MethodRewriteInfo + { + /// + public override int CompareTo(MethodRewriteInfo? other) + { + // 'ConvertToUnmanaged' objects have no additional state, so just compare with the base state + return ReferenceEquals(this, other) + ? 0 + : CompareByMethodRewriteInfo(other); + } + } +} From 8ab19129e486a70b3b1a550779a12afb96b13a32 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 10:48:17 -0800 Subject: [PATCH 24/41] Refactor ConvertToUnmanaged method generation Replaces throwing implementations of ConvertToUnmanaged with stubs that load the argument and return, and introduces NOP markers for later rewriting. Adds tracking of these methods for further processing by emitState. --- ...initionFactory.SzArrayElementMarshaller.cs | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index 4a2f7d65a..62f3d3dc2 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -52,6 +52,10 @@ public static TypeDefinition UnmanagedValueType( Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } }; + // Rewriting labels + CilInstruction nop_convertToUnmanaged = new(Nop); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToUnmanaged' method as follows: // // public static ConvertToUnmanaged( value) @@ -62,8 +66,9 @@ public static TypeDefinition UnmanagedValueType( { CilInstructions = { - { Ldnull }, - { Throw } + { Ldarg_0 }, + { nop_convertToUnmanaged }, + { Ret } } }; @@ -72,7 +77,11 @@ public static TypeDefinition UnmanagedValueType( declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), method: convertToUnmanagedMethod); - CilInstruction nop_convertToManaged = new(Nop); + // Track rewriting the native value for 'ConvertToUnmanaged' + emitState.TrackConvertToUnmanagedMethodRewrite( + parameterType: elementType, + method: convertToUnmanagedMethod, + marker: nop_convertToUnmanaged); // Define the 'ConvertToManaged' method as follows: // @@ -135,6 +144,10 @@ public static TypeDefinition ManagedValueType( Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } }; + // Rewriting labels + CilInstruction nop_convertToUnmanaged = new(Nop); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToUnmanaged' method as follows: // // public static ConvertToUnmanaged( value) @@ -145,8 +158,9 @@ public static TypeDefinition ManagedValueType( { CilInstructions = { - { Ldnull }, - { Throw } + { Ldarg_0 }, + { nop_convertToUnmanaged }, + { Ret } } }; @@ -155,7 +169,11 @@ public static TypeDefinition ManagedValueType( declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), method: convertToUnmanagedMethod); - CilInstruction nop_convertToManaged = new(Nop); + // Track rewriting the native value for 'ConvertToUnmanaged' + emitState.TrackConvertToUnmanagedMethodRewrite( + parameterType: elementType, + method: convertToUnmanagedMethod, + marker: nop_convertToUnmanaged); // Define the 'ConvertToManaged' method as follows: // @@ -241,6 +259,10 @@ public static TypeDefinition KeyValuePair( TypeSignature keyValuePairType = interopReferences.KeyValuePair2.MakeGenericValueType(keyType, valueType); + // Rewriting labels + CilInstruction nop_convertToUnmanaged = new(Nop); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToUnmanaged' method as follows: // // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(KeyValuePair<, > value) @@ -253,8 +275,9 @@ public static TypeDefinition KeyValuePair( { CilInstructions = { - { Ldnull }, - { Throw } + { Ldarg_0 }, + { nop_convertToUnmanaged }, + { Ret } } }; @@ -263,7 +286,11 @@ public static TypeDefinition KeyValuePair( declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType).Import(module), method: convertToUnmanagedMethod); - CilInstruction nop_convertToManaged = new(Nop); + // Track rewriting the native value for 'ConvertToUnmanaged' + emitState.TrackConvertToUnmanagedMethodRewrite( + parameterType: elementType, + method: convertToUnmanagedMethod, + marker: nop_convertToUnmanaged); // Define the 'ConvertToManaged' method as follows: // @@ -327,6 +354,10 @@ public static TypeDefinition ReferenceType( Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } }; + // Rewriting labels + CilInstruction nop_convertToUnmanaged = new(Nop); + CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToUnmanaged' method as follows: // // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( value) @@ -339,8 +370,9 @@ public static TypeDefinition ReferenceType( { CilInstructions = { - { Ldnull }, - { Throw } + { Ldarg_0 }, + { nop_convertToUnmanaged }, + { Ret } } }; @@ -349,7 +381,11 @@ public static TypeDefinition ReferenceType( declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType).Import(module), method: convertToUnmanagedMethod); - CilInstruction nop_convertToManaged = new(Nop); + // Track rewriting the native value for 'ConvertToUnmanaged' + emitState.TrackConvertToUnmanagedMethodRewrite( + parameterType: elementType, + method: convertToUnmanagedMethod, + marker: nop_convertToUnmanaged); // Define the 'ConvertToManaged' method as follows: // From b42004ebac8956eae5f09aebda97d4029df91d4e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 10:53:55 -0800 Subject: [PATCH 25/41] Refactor ConvertToUnmanaged rewrite to RawRetVal Renamed ConvertToUnmanaged method rewrite logic and related types to RawRetVal, updating all references and tracking methods accordingly. This change clarifies the rewrite's purpose and adds support for nullable value types by calling BoxToUnmanaged when appropriate. --- ... InteropMethodRewriteFactory.RawRetVal.cs} | 21 ++++++++-- ...initionFactory.SzArrayElementMarshaller.cs | 8 ++-- .../Generation/InteropGenerator.Emit.cs | 12 +++--- .../Generation/InteropGeneratorEmitState.cs | 42 +++++++++---------- ...aged.cs => MethodRewriteInfo.RawRetVal.cs} | 8 ++-- 5 files changed, 52 insertions(+), 39 deletions(-) rename src/WinRT.Interop.Generator/Factories/{InteropMethodRewriteFactory.ConvertToUnmanaged.cs => InteropMethodRewriteFactory.RawRetVal.cs} (71%) rename src/WinRT.Interop.Generator/Models/MethodRewriteInfo/{MethodRewriteInfo.ConvertToUnmanaged.cs => MethodRewriteInfo.RawRetVal.cs} (63%) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs similarity index 71% rename from src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs rename to src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs index 3622d5ce4..7d9bc252c 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ConvertToUnmanaged.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs @@ -21,7 +21,10 @@ internal partial class InteropMethodRewriteFactory /// /// Contains the logic for emitting direct calls to ConvertToUnmanaged for a given value (already on the stack). /// - public static class ConvertToUnmanaged + /// + /// This is similar to , but without protected regions or any unwrapping. + /// + public static class RawRetVal { /// /// Performs two-pass code generation on a target method to emit a direct call to ConvertToUnmanaged. @@ -58,10 +61,20 @@ public static void RewriteMethod( return; } - // For any types, we always just forward directly to 'ConvertToUnmanaged', without any other changes - InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState); + // For nullable values, we actually need to call 'BoxToUnmanaged' + if (parameterType.IsConstructedNullableValueType(interopReferences)) + { + InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState); - body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.ConvertToUnmanaged().Import(module))); + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.BoxToUnmanaged().Import(module))); + } + else + { + // For any other types, we always just forward directly to 'ConvertToUnmanaged', without any other changes + InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState); + + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.ConvertToUnmanaged().Import(module))); + } } } } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index 62f3d3dc2..7ac2bae5a 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -78,7 +78,7 @@ public static TypeDefinition UnmanagedValueType( method: convertToUnmanagedMethod); // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackConvertToUnmanagedMethodRewrite( + emitState.TrackRawRetValMethodRewrite( parameterType: elementType, method: convertToUnmanagedMethod, marker: nop_convertToUnmanaged); @@ -170,7 +170,7 @@ public static TypeDefinition ManagedValueType( method: convertToUnmanagedMethod); // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackConvertToUnmanagedMethodRewrite( + emitState.TrackRawRetValMethodRewrite( parameterType: elementType, method: convertToUnmanagedMethod, marker: nop_convertToUnmanaged); @@ -287,7 +287,7 @@ public static TypeDefinition KeyValuePair( method: convertToUnmanagedMethod); // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackConvertToUnmanagedMethodRewrite( + emitState.TrackRawRetValMethodRewrite( parameterType: elementType, method: convertToUnmanagedMethod, marker: nop_convertToUnmanaged); @@ -382,7 +382,7 @@ public static TypeDefinition ReferenceType( method: convertToUnmanagedMethod); // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackConvertToUnmanagedMethodRewrite( + emitState.TrackRawRetValMethodRewrite( parameterType: elementType, method: convertToUnmanagedMethod, marker: nop_convertToUnmanaged); diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 43a4c2710..21c95c6df 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -2017,12 +2017,12 @@ private static void RewriteMethodDefinitions( { switch (rewriteInfo) { - // Rewrite direct calls to 'ConvertToUnmanaged' - case MethodRewriteInfo.ConvertToUnmanaged convertToUnmanagedValue: - InteropMethodRewriteFactory.ConvertToUnmanaged.RewriteMethod( - parameterType: convertToUnmanagedValue.Type, - method: convertToUnmanagedValue.Method, - marker: convertToUnmanagedValue.Marker, + // Rewrite direct calls to 'ConvertToUnmanaged' (or 'BoxToUnmanaged') + case MethodRewriteInfo.RawRetVal rawRetValInfo: + InteropMethodRewriteFactory.RawRetVal.RewriteMethod( + parameterType: rawRetValInfo.Type, + method: rawRetValInfo.Method, + marker: rawRetValInfo.Marker, interopReferences: interopReferences, emitState: emitState, module: module); diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs index 99cff9ebd..a22754592 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -87,27 +87,6 @@ public TypeDefinition LookupTypeDefinition(TypeSignature typeSignature, string k throw WellKnownInteropExceptions.TrackedTypeDefinitionLookupError(typeSignature, key); } - /// - /// Tracks a method rewrite that involves emitting direct calls to ConvertToUnmanaged in the specified method. - /// - /// - /// - /// - public void TrackConvertToUnmanagedMethodRewrite( - TypeSignature parameterType, - MethodDefinition method, - CilInstruction marker) - { - ThrowIfReadOnly(); - - _methodRewriteInfos.Add(new MethodRewriteInfo.ConvertToUnmanaged - { - Type = parameterType, - Method = method, - Marker = marker - }); - } - /// /// Tracks a method rewrite that involves returning a value from the specified method at a given marker instruction. /// @@ -153,6 +132,27 @@ public void TrackRetValValueMethodRewrite( }); } + /// + /// Tracks a method rewrite that involves emitting direct calls to ConvertToUnmanaged in the specified method. + /// + /// + /// + /// + public void TrackRawRetValMethodRewrite( + TypeSignature parameterType, + MethodDefinition method, + CilInstruction marker) + { + ThrowIfReadOnly(); + + _methodRewriteInfos.Add(new MethodRewriteInfo.RawRetVal + { + Type = parameterType, + Method = method, + Marker = marker + }); + } + /// /// Tracks a method rewrite that involves loading a managed parameter in the specified method. /// diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs similarity index 63% rename from src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs rename to src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs index c5d8c3f9e..619cee954 100644 --- a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ConvertToUnmanaged.cs +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs @@ -9,16 +9,16 @@ internal partial class MethodRewriteInfo /// /// Contains info for a target method for two-pass IL generation, for for emitting direct calls to ConvertToUnmanaged. /// - /// - public sealed class ConvertToUnmanaged : MethodRewriteInfo + /// + public sealed class RawRetVal : MethodRewriteInfo { /// public override int CompareTo(MethodRewriteInfo? other) { - // 'ConvertToUnmanaged' objects have no additional state, so just compare with the base state + // 'RawRetVal' objects have no additional state, so just compare with the base state return ReferenceEquals(this, other) ? 0 - : CompareByMethodRewriteInfo(other); + : CompareByMethodRewriteInfo(other); } } } From b6d86df29236d78b6350c0546d0c5342483b2b32 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 12:56:12 -0800 Subject: [PATCH 26/41] Add support for Dispose method rewrite in interop generator Introduces logic to track and emit IL for disposing or releasing values in generated interop methods. Adds MethodRewriteInfo.Dispose, corresponding factory logic, error handling, and integration into the emit pipeline to handle managed, string, and native types appropriately. --- .../Errors/WellKnownInteropExceptions.cs | 8 ++ .../InteropMethodRewriteFactory.Dispose.cs | 80 +++++++++++++++++++ .../Generation/InteropGenerator.Emit.cs | 11 +++ .../Generation/InteropGeneratorEmitState.cs | 21 +++++ .../MethodRewriteInfo.Dispose.cs | 24 ++++++ 5 files changed, 144 insertions(+) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.Dispose.cs create mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.Dispose.cs diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs index b7a32c0d8..d3f52d08d 100644 --- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs +++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs @@ -597,6 +597,14 @@ public static WellKnownInteropException MethodRewriteSourceParameterTypeMismatch return Exception(69, $"Parameter variable of type '{parameterType}' cannot be used to marshal a value of type '{returnType}' in generated interop method '{method}'."); } + /// + /// A type doesn't have the Dispose method available. + /// + public static WellKnownInteropException MethodRewriteDisposeNotAvailableError(TypeSignature parameterType, MethodDefinition method) + { + return Exception(70, $"Value of type '{parameterType}' in generated interop method '{method}' cannot be disposed, as it is an unmanaged (or blittable) value type."); + } + /// /// Creates a new exception with the specified id and message. /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.Dispose.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.Dispose.cs new file mode 100644 index 000000000..09b2397e0 --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.Dispose.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using WindowsRuntime.InteropGenerator.Resolvers; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +internal partial class InteropMethodRewriteFactory +{ + /// + /// Contains the logic for disposing (or releasing) a given value (already on the stack). + /// + public static class Dispose + { + /// + /// Performs two-pass code generation on a target method to dispose (or release) a given value. + /// + /// The parameter type that needs to be marshalled. + /// The target method to perform two-pass code generation on. + /// The target IL instruction to replace with the right set of specialized instructions. + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static void RewriteMethod( + TypeSignature parameterType, + MethodDefinition method, + CilInstruction marker, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + // Validate that we do have some IL body for the input method (this should always be the case) + if (method.CilMethodBody is not CilMethodBody body) + { + throw WellKnownInteropExceptions.MethodRewriteMissingBodyError(method); + } + + // If we didn't find the marker, it means the target method is either invalid + if (!body.Instructions.ReferenceContains(marker)) + { + throw WellKnownInteropExceptions.MethodRewriteMarkerInstructionNotFoundError(marker, method); + } + + // For unmanaged (or blittable) value types, this call is not valid, as they can't be disposed. + // We also need to check whether the type is 'Exception', as its ABI type is blittable as well. + if ((parameterType.IsValueType && !parameterType.IsManagedValueType(interopReferences)) || parameterType.IsTypeOfException(interopReferences)) + { + throw WellKnownInteropExceptions.MethodRewriteDisposeNotAvailableError(parameterType, method); + } + + // For managed value types, we call 'Dispose' on their marshaller type + if (parameterType.IsManagedValueType(interopReferences)) + { + InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState); + + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.Dispose().Import(module))); + } + else if (parameterType.IsTypeOfString()) + { + // When disposing 'string' values, we must use 'HStringMarshaller' (the ABI type is not actually a COM object) + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, interopReferences.HStringMarshallerFree.Import(module))); + } + else + { + // For everything else, we just release the native object. This also applies to generic value types, + // such as 'KeyValuePair', because they are actually interface types at the ABI level. + body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, interopReferences.WindowsRuntimeUnknownMarshallerFree.Import(module))); + } + } + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 21c95c6df..8a760a8f2 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -2087,6 +2087,17 @@ private static void RewriteMethodDefinitions( emitState: emitState, module: module); break; + + // Rewrite direct calls to 'Dispose' (or the appropriate 'Free' method) + case MethodRewriteInfo.Dispose disposeInfo: + InteropMethodRewriteFactory.Dispose.RewriteMethod( + parameterType: disposeInfo.Type, + method: disposeInfo.Method, + marker: disposeInfo.Marker, + interopReferences: interopReferences, + emitState: emitState, + module: module); + break; default: throw new UnreachableException(); } } diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs index a22754592..495204504 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -228,6 +228,27 @@ public void TrackNativeParameterMethodRewrite( }); } + /// + /// Tracks a method rewrite that involves emitting calls to dispose (or release) a given value in the specified method. + /// + /// + /// + /// + public void TrackDisposeRewrite( + TypeSignature parameterType, + MethodDefinition method, + CilInstruction marker) + { + ThrowIfReadOnly(); + + _methodRewriteInfos.Add(new MethodRewriteInfo.Dispose + { + Type = parameterType, + Method = method, + Marker = marker + }); + } + /// /// Enumerates all instances with info on two-pass code generation steps to perform. /// diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.Dispose.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.Dispose.cs new file mode 100644 index 000000000..e3f8cbb25 --- /dev/null +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.Dispose.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsRuntime.InteropGenerator.Models; + +/// +internal partial class MethodRewriteInfo +{ + /// + /// Contains info for a target method for two-pass IL generation, to dispose (or release) a given value. + /// + /// + public sealed class Dispose : MethodRewriteInfo + { + /// + public override int CompareTo(MethodRewriteInfo? other) + { + // 'Dispose' objects have no additional state, so just compare with the base state + return ReferenceEquals(this, other) + ? 0 + : CompareByMethodRewriteInfo(other); + } + } +} From ed8f817b68fa1842a57caba9894efdc616e78e20 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 12:56:23 -0800 Subject: [PATCH 27/41] Remove marker reference for blittable parameters Added calls to ReferenceRemove for markers when parameter types are blittable in ManagedValue and RawRetVal factories. Also fixed a typo in a comment and removed an unnecessary pragma warning directive. --- .../Factories/InteropMethodRewriteFactory.ManagedValue.cs | 2 ++ .../Factories/InteropMethodRewriteFactory.RawRetVal.cs | 5 +++-- .../Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs index 5fe7d0d56..881c7e1fc 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedValue.cs @@ -57,6 +57,8 @@ public static void RewriteMethod( // If the parameter type is blittable, we have nothing else to do (the value is already loaded) if (parameterType.IsBlittable(interopReferences)) { + _ = body.Instructions.ReferenceRemove(marker); + return; } diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs index 7d9bc252c..cfad78c8c 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RawRetVal.cs @@ -11,8 +11,6 @@ using WindowsRuntime.InteropGenerator.Resolvers; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; -#pragma warning disable CS1573 - namespace WindowsRuntime.InteropGenerator.Factories; /// @@ -44,6 +42,7 @@ public static void RewriteMethod( ModuleDefinition module) { // Validate that we do have some IL body for the input method (this should always be the case) + // Validate that we do have some IL body for the input method (this should always be thevv case) if (method.CilMethodBody is not CilMethodBody body) { throw WellKnownInteropExceptions.MethodRewriteMissingBodyError(method); @@ -58,6 +57,8 @@ public static void RewriteMethod( // If the parameter type is blittable, we have nothing else to do (the value is already loaded) if (parameterType.IsBlittable(interopReferences)) { + _ = body.Instructions.ReferenceRemove(marker); + return; } diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs index 619cee954..730ce5362 100644 --- a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RawRetVal.cs @@ -7,7 +7,7 @@ namespace WindowsRuntime.InteropGenerator.Models; internal partial class MethodRewriteInfo { /// - /// Contains info for a target method for two-pass IL generation, for for emitting direct calls to ConvertToUnmanaged. + /// Contains info for a target method for two-pass IL generation, for emitting direct calls to ConvertToUnmanaged. /// /// public sealed class RawRetVal : MethodRewriteInfo From 4d1ace3c88f43bb560c75c88c8193d567e092a28 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 12:58:01 -0800 Subject: [PATCH 28/41] Refactor disposal logic in SzArrayElementMarshaller Introduces a NOP marker for disposal and updates the 'Dispose' method to use it. Also adds tracking for disposal rewrites to improve clarity and maintainability of the marshaller's disposal process. --- ...TypeDefinitionFactory.SzArrayElementMarshaller.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index 7ac2bae5a..72d4963a6 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -147,6 +147,7 @@ public static TypeDefinition ManagedValueType( // Rewriting labels CilInstruction nop_convertToUnmanaged = new(Nop); CilInstruction nop_convertToManaged = new(Nop); + CilInstruction nop_dispose = new(Nop); // Define the 'ConvertToUnmanaged' method as follows: // @@ -212,8 +213,9 @@ public static TypeDefinition ManagedValueType( { CilInstructions = { - { Ldnull }, - { Throw } + { Ldarg_0 }, + { nop_dispose }, + { Ret } } }; @@ -222,6 +224,12 @@ public static TypeDefinition ManagedValueType( declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerDispose(elementType, elementAbiType).Import(module), method: disposeMethod); + // Track rewriting the disposal for 'Dispose' + emitState.TrackDisposeRewrite( + parameterType: elementType, + method: disposeMethod, + marker: nop_dispose); + return elementMarshallerType; } From 93697d134a0c48186f319d8b8397e9538de84d18 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 16:11:03 -0800 Subject: [PATCH 29/41] Add Free method to KeyValuePair array marshaller Introduces a static Free method to WindowsRuntimeKeyValuePairTypeArrayMarshaller for releasing memory of marshalled arrays. Also adds a corresponding MemberReference accessor in InteropReferences for code generation and interop support. --- .../References/InteropReferences.cs | 17 +++++++++++++++++ ...luePairTypeArrayMarshaller{TKey, TValue}.cs | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 161ea0f70..f07893567 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -2754,6 +2754,23 @@ public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToMa .MakeGenericInstanceMethod(elementMarshallerType); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.Free. + /// + /// The input key type. + /// The input value type. + public MemberReference WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(TypeSignature keyType, TypeSignature valueType) + { + return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); + } + /// /// Gets the for AddEventHandler for . /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs index ec3e5089c..c58077028 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs @@ -139,4 +139,22 @@ public static void CopyToManaged(uint size, void** source, S destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); } } + + /// + public static void Free(uint size, void** array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + WindowsRuntimeUnknownMarshaller.Free(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } } \ No newline at end of file From 3dd06c125af6d16a03d33e3a10a90dbec61e3ff1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 16:11:38 -0800 Subject: [PATCH 30/41] Add SzArrayMarshaller factory and update docs Introduces InteropTypeDefinitionFactory.SzArrayMarshaller for generating marshaller types for SZ arrays, supporting various element types. Also adds documentation to SzArrayElementMarshaller methods for clarity. --- ...initionFactory.SzArrayElementMarshaller.cs | 4 + ...TypeDefinitionFactory.SzArrayMarshaller.cs | 331 ++++++++++++++++++ 2 files changed, 335 insertions(+) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index 72d4963a6..0c9ab1268 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -28,6 +28,7 @@ public static class SzArrayElementMarshaller /// The instance to use. /// The emit state for this invocation. /// The module that will contain the type being created. + /// The resulting element marshaller type. public static TypeDefinition UnmanagedValueType( SzArrayTypeSignature arrayType, InteropReferences interopReferences, @@ -120,6 +121,7 @@ public static TypeDefinition UnmanagedValueType( /// The instance to use. /// The emit state for this invocation. /// The module that will contain the type being created. + /// The resulting element marshaller type. public static TypeDefinition ManagedValueType( SzArrayTypeSignature arrayType, InteropReferences interopReferences, @@ -240,6 +242,7 @@ public static TypeDefinition ManagedValueType( /// The instance to use. /// The emit state for this invocation. /// The module that will contain the type being created. + /// The resulting element marshaller type. public static TypeDefinition KeyValuePair( SzArrayTypeSignature arrayType, InteropReferences interopReferences, @@ -339,6 +342,7 @@ public static TypeDefinition KeyValuePair( /// The instance to use. /// The emit state for this invocation. /// The module that will contain the type being created. + /// The resulting element marshaller type. public static TypeDefinition ReferenceType( SzArrayTypeSignature arrayType, InteropReferences interopReferences, diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs new file mode 100644 index 000000000..6d03f45c2 --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs @@ -0,0 +1,331 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +/// A factory for interop type definitions. +/// +internal partial class InteropTypeDefinitionFactory +{ + /// + /// Helpers for marshaller types for SZ array types. + /// + public static class SzArrayMarshaller + { + /// + /// Creates a for the marshaller for a blittable value type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition BlittableValueType( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.WindowsRuntimeBlittableValueTypeArrayMarshallerConvertToUnmanaged(elementType), + convertToManagedMethod: interopReferences.WindowsRuntimeBlittableValueTypeArrayMarshallerConvertToManaged(elementType), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeBlittableValueTypeArrayMarshallerCopyToUnmanaged(elementType), + copyToManagedMethod: interopReferences.WindowsRuntimeBlittableValueTypeArrayMarshallerCopyToManaged(elementType), + freeMethod: interopReferences.WindowsRuntimeBlittableValueTypeArrayMarshallerFree(elementType), + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for an unmanaged value type. + /// + /// The for the SZ array type. + /// The element marshaller type produced by . + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition UnmanagedValueType( + SzArrayTypeSignature arrayType, + TypeDefinition elementMarshallerType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); + + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.WindowsRuntimeUnmanagedValueTypeArrayMarshallerConvertToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + convertToManagedMethod: interopReferences.WindowsRuntimeUnmanagedValueTypeArrayMarshallerConvertToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeUnmanagedValueTypeArrayMarshallerCopyToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToManagedMethod: interopReferences.WindowsRuntimeUnmanagedValueTypeArrayMarshallerCopyToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + freeMethod: interopReferences.WindowsRuntimeUnmanagedValueTypeArrayMarshallerFree(elementType, elementAbiType), + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for a managed value type. + /// + /// The for the SZ array type. + /// The element marshaller type produced by . + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition ManagedValueType( + SzArrayTypeSignature arrayType, + TypeDefinition elementMarshallerType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); + + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.WindowsRuntimeManagedValueTypeArrayMarshallerConvertToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + convertToManagedMethod: interopReferences.WindowsRuntimeManagedValueTypeArrayMarshallerConvertToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeManagedValueTypeArrayMarshallerCopyToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToManagedMethod: interopReferences.WindowsRuntimeManagedValueTypeArrayMarshallerCopyToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + freeMethod: interopReferences.WindowsRuntimeManagedValueTypeArrayMarshallerFree(elementType, elementAbiType, elementMarshallerTypeSignature), + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for a type. + /// + /// The for the SZ array type. + /// The element marshaller type produced by . + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition KeyValuePair( + SzArrayTypeSignature arrayType, + TypeDefinition elementMarshallerType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); + + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + convertToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), + copyToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), + freeMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(elementType, elementAbiType), + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for a reference type. + /// + /// The for the SZ array type. + /// The element marshaller type produced by . + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition ReferenceType( + SzArrayTypeSignature arrayType, + TypeDefinition elementMarshallerType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); + + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerConvertToUnmanaged(elementType, elementMarshallerTypeSignature), + convertToManagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerConvertToManaged(elementType, elementMarshallerTypeSignature), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerCopyToUnmanaged(elementType, elementMarshallerTypeSignature), + copyToManagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerCopyToManaged(elementType, elementMarshallerTypeSignature), + freeMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerFree(elementType), + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a new type definition for the marshaller for some SZ array type. + /// + /// The for the SZ array type. + /// The ConvertToUnmanaged implementation method to call. + /// The ConvertToManaged implementation method to call. + /// The CopyToUnmanaged implementation method to call. + /// The CopyToManaged implementation method to call. + /// The Free implementation method to call. + /// The instance to use. + /// The module that will contain the type being created. + /// The resulting marshaller type. + private static TypeDefinition Marshaller( + SzArrayTypeSignature arrayType, + IMethodDescriptor convertToUnmanagedMethod, + IMethodDescriptor convertToManagedMethod, + IMethodDescriptor copyToUnmanagedMethod, + IMethodDescriptor copyToManagedMethod, + IMethodDescriptor freeMethod, + InteropReferences interopReferences, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + + // We're declaring an 'internal static class' type + TypeDefinition marshallerType = new( + ns: InteropUtf8NameFactory.TypeNamespace(arrayType), + name: InteropUtf8NameFactory.TypeName(arrayType, "Marshaller"), + attributes: TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, + baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()); + + // Define the 'ConvertToUnmanaged' method as follows: + // + // public static void ConvertToUnmanaged(ReadOnlySpan<>, out uint size, out * array) + MethodDefinition convertToUnmanagedForwarderMethod = new( + name: "ConvertToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Void, + parameterTypes: [ + interopReferences.ReadOnlySpan1.MakeGenericValueType(elementType).Import(module), + module.CorLibTypeFactory.UInt32.MakeByReferenceType(), + elementAbiType.Import(module).MakePointerType().MakeByReferenceType()])) + { + CilOutParameterIndices = [2, 3], + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Ldarg_2 }, + { Call, convertToUnmanagedMethod.Import(module) }, + { Ret } + } + }; + + marshallerType.Methods.Add(convertToUnmanagedForwarderMethod); + + // Define the 'ConvertToManaged' method as follows: + // + // public static [] ConvertToManaged(uint size, * value) + MethodDefinition convertToManagedForwarderMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: arrayType.Import(module), + parameterTypes: [ + module.CorLibTypeFactory.UInt32, + elementAbiType.Import(module).MakePointerType()])) + { + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Call, convertToManagedMethod.Import(module) }, + { Ret } + } + }; + + marshallerType.Methods.Add(convertToManagedForwarderMethod); + + // Define the 'CopyToManaged' method as follows: + // + // public static void CopyToManaged(uint size, * value, Span<> destination) + MethodDefinition copyToManagedForwarderMethod = new( + name: "CopyToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Void, + parameterTypes: [ + module.CorLibTypeFactory.UInt32, + elementAbiType.Import(module).MakePointerType(), + interopReferences.Span1.MakeGenericValueType(elementType).Import(module)])) + { + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Ldarg_2 }, + { Call, copyToManagedMethod.Import(module) }, + { Ret } + } + }; + + marshallerType.Methods.Add(copyToManagedForwarderMethod); + + // Define the 'CopyToUnmanaged' method as follows: + // + // public static void CopyToUnmanaged(ReadOnlySpan<> value, uint size, * destination) + MethodDefinition copyToUnmanagedForwarderMethod = new( + name: "CopyToUnmanaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Void, + parameterTypes: [ + interopReferences.ReadOnlySpan1.MakeGenericValueType(elementType).Import(module), + module.CorLibTypeFactory.UInt32, + elementAbiType.Import(module).MakePointerType()])) + { + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Ldarg_2 }, + { Call, copyToUnmanagedMethod.Import(module) }, + { Ret } + } + }; + + marshallerType.Methods.Add(copyToUnmanagedForwarderMethod); + + // Define the 'Free' method as follows: + // + // public static void Free(uint size, * destination) + MethodDefinition freeForwarderMethod = new( + name: "Free"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Void, + parameterTypes: [ + module.CorLibTypeFactory.UInt32, + elementAbiType.Import(module).MakePointerType()])) + { + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Call, freeMethod.Import(module) }, + { Ret } + } + }; + + marshallerType.Methods.Add(freeForwarderMethod); + + return marshallerType; + } + } +} \ No newline at end of file From e75ef8a6d0bc196e415a397178be0bb2a8ff7bd3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 16:14:42 -0800 Subject: [PATCH 31/41] Add marshaller methods for string, Type, and Exception arrays Introduces static methods to create marshaller TypeDefinitions for string, System.Type, and System.Exception SZ array types, utilizing corresponding interop reference methods. Also removes unused elementAbiType variable from the existing method. --- ...TypeDefinitionFactory.SzArrayMarshaller.cs | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs index 6d03f45c2..a6bd8e6da 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs @@ -157,7 +157,6 @@ public static TypeDefinition ReferenceType( ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; - TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); return Marshaller( @@ -171,6 +170,75 @@ public static TypeDefinition ReferenceType( module: module); } + /// + /// Creates a for the marshaller for the type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition String( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + ModuleDefinition module) + { + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.HStringArrayMarshallerConvertToUnmanaged, + convertToManagedMethod: interopReferences.HStringArrayMarshallerConvertToManaged, + copyToUnmanagedMethod: interopReferences.HStringArrayMarshallerCopyToUnmanaged, + copyToManagedMethod: interopReferences.HStringArrayMarshallerCopyToManaged, + freeMethod: interopReferences.HStringArrayMarshallerFree, + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for the type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition Type( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + ModuleDefinition module) + { + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.TypeArrayMarshallerConvertToUnmanaged, + convertToManagedMethod: interopReferences.TypeArrayMarshallerConvertToManaged, + copyToUnmanagedMethod: interopReferences.TypeArrayMarshallerCopyToUnmanaged, + copyToManagedMethod: interopReferences.TypeArrayMarshallerCopyToManaged, + freeMethod: interopReferences.TypeArrayMarshallerFree, + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the marshaller for the type. + /// + /// The for the SZ array type. + /// The instance to use. + /// The module that will contain the type being created. + /// The resulting marshaller type. + public static TypeDefinition Exception( + SzArrayTypeSignature arrayType, + InteropReferences interopReferences, + ModuleDefinition module) + { + return Marshaller( + arrayType: arrayType, + convertToUnmanagedMethod: interopReferences.ExceptionArrayMarshallerConvertToUnmanaged, + convertToManagedMethod: interopReferences.ExceptionArrayMarshallerConvertToManaged, + copyToUnmanagedMethod: interopReferences.ExceptionArrayMarshallerCopyToUnmanaged, + copyToManagedMethod: interopReferences.ExceptionArrayMarshallerCopyToManaged, + freeMethod: interopReferences.ExceptionArrayMarshallerFree, + interopReferences: interopReferences, + module: module); + } + /// /// Creates a new type definition for the marshaller for some SZ array type. /// From f910f4619ba2319dafd827035dfd1948cee6960d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 16:27:58 -0800 Subject: [PATCH 32/41] Refactor SZ array marshaller to use emit state Updated the SZ array marshaller builder and related factory methods to accept and use an InteropGeneratorEmitState parameter. This enables more flexible and context-aware marshaller generation for different element types, and simplifies the method signatures in the marshaller factory by removing unused emitState parameters where not needed. --- .../InteropTypeDefinitionBuilder.SzArray.cs | 211 +++++++++--------- ...TypeDefinitionFactory.SzArrayMarshaller.cs | 11 - .../Generation/InteropGenerator.Emit.cs | 5 +- 3 files changed, 114 insertions(+), 113 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs index c47349485..20b75c28c 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs @@ -8,6 +8,7 @@ using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Factories; +using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; @@ -26,121 +27,129 @@ public static class SzArray /// /// The for the SZ array type. /// The instance to use. + /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static void Marshaller( SzArrayTypeSignature arrayType, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition marshallerType) { TypeSignature elementType = arrayType.BaseType; - TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); - // We're declaring an 'internal static class' type - marshallerType = new( - ns: InteropUtf8NameFactory.TypeNamespace(arrayType), - name: InteropUtf8NameFactory.TypeName(arrayType, "Marshaller"), - attributes: TypeAttributes.AutoLayout | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, - baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()); - - module.TopLevelTypes.Add(marshallerType); - - // Define the 'ConvertToUnmanaged' method as follows: - // - // public static void ConvertToUnmanaged(ReadOnlySpan<>, out uint size, out * array) - MethodDefinition convertToUnmanagedMethod = new( - name: "ConvertToUnmanaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Void, - parameterTypes: [ - interopReferences.ReadOnlySpan1.MakeGenericValueType(elementType).Import(module), - module.CorLibTypeFactory.UInt32.MakeByReferenceType(), - elementAbiType.Import(module).MakePointerType().MakeByReferenceType()])) + // Emit the right marshaller based on the element type. We special case all different + // kinds of element types because some need specialized marshallers, and some need + // a generated element marshaller type. The marshaller itself just forwards all calls. + if (elementType.IsBlittable(interopReferences)) { - CilOutParameterIndices = [2, 3], - CilInstructions = - { - { Ldnull }, - { Throw } // TODO - } - }; - - marshallerType.Methods.Add(convertToUnmanagedMethod); - - // Define the 'ConvertToManaged' method as follows: - // - // public static [] ConvertToManaged(uint size, * value) - MethodDefinition convertToManagedMethod = new( - name: "ConvertToManaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: arrayType.Import(module), - parameterTypes: [ - module.CorLibTypeFactory.UInt32, - elementAbiType.Import(module).MakePointerType()])) + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.BlittableValueType( + arrayType: arrayType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsConstructedKeyValuePairType(interopReferences)) { - CilInstructions = - { - { Ldnull }, - { Throw } // TODO - } - }; - - marshallerType.Methods.Add(convertToManagedMethod); - - // Define the 'CopyToManaged' method as follows: - // - // public static void CopyToManaged(uint size, * value, Span<> destination) - MethodDefinition copyToManagedMethod = new( - name: "CopyToManaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Void, - parameterTypes: [ - module.CorLibTypeFactory.UInt32, - elementAbiType.Import(module).MakePointerType(), - interopReferences.Span1.MakeGenericValueType(elementType).Import(module)])) + TypeDefinition elementMarshallerType = InteropTypeDefinitionFactory.SzArrayElementMarshaller.KeyValuePair( + arrayType: arrayType, + interopReferences: interopReferences, + emitState: emitState, + module: module); + + module.TopLevelTypes.Add(elementMarshallerType); + + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.KeyValuePair( + arrayType: arrayType, + elementMarshallerType: elementMarshallerType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsManagedValueType(interopReferences)) { - CilInstructions = - { - { Ldnull }, - { Throw } // TODO - } - }; - - marshallerType.Methods.Add(copyToManagedMethod); - - // Define the 'CopyToUnmanaged' method as follows: - // - // public static void CopyToUnmanaged(ReadOnlySpan<> value, uint size, * destination) - MethodDefinition copyToUnmanagedMethod = new( - name: "CopyToUnmanaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Void, - parameterTypes: [ - interopReferences.ReadOnlySpan1.MakeGenericValueType(elementType).Import(module), - module.CorLibTypeFactory.UInt32, - elementAbiType.Import(module).MakePointerType()])) + TypeDefinition elementMarshallerType = InteropTypeDefinitionFactory.SzArrayElementMarshaller.ManagedValueType( + arrayType: arrayType, + interopReferences: interopReferences, + emitState: emitState, + module: module); + + module.TopLevelTypes.Add(elementMarshallerType); + + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.ManagedValueType( + arrayType: arrayType, + elementMarshallerType: elementMarshallerType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsValueType) { - CilInstructions = - { - { Ldnull }, - { Throw } // TODO - } - }; - - marshallerType.Methods.Add(copyToUnmanagedMethod); - - // Define the 'Free' method - MethodDefinition freeMethod = InteropMethodDefinitionFactory.SzArrayMarshaller.Free( - arrayType, - interopReferences, - module); - - marshallerType.Methods.Add(freeMethod); + TypeDefinition elementMarshallerType = InteropTypeDefinitionFactory.SzArrayElementMarshaller.UnmanagedValueType( + arrayType: arrayType, + interopReferences: interopReferences, + emitState: emitState, + module: module); + + module.TopLevelTypes.Add(elementMarshallerType); + + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.UnmanagedValueType( + arrayType: arrayType, + elementMarshallerType: elementMarshallerType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsTypeOfString()) + { + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.String( + arrayType: arrayType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsTypeOfType(interopReferences)) + { + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.Type( + arrayType: arrayType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else if (elementType.IsTypeOfException(interopReferences)) + { + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.Exception( + arrayType: arrayType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } + else + { + TypeDefinition elementMarshallerType = InteropTypeDefinitionFactory.SzArrayElementMarshaller.ReferenceType( + arrayType: arrayType, + interopReferences: interopReferences, + emitState: emitState, + module: module); + + module.TopLevelTypes.Add(elementMarshallerType); + + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.UnmanagedValueType( + arrayType: arrayType, + elementMarshallerType: elementMarshallerType, + interopReferences: interopReferences, + module: module); + + module.TopLevelTypes.Add(marshallerType); + } } /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs index a6bd8e6da..ba5b7b03f 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs @@ -4,7 +4,6 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; -using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; @@ -25,13 +24,11 @@ public static class SzArrayMarshaller /// /// The for the SZ array type. /// The instance to use. - /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static TypeDefinition BlittableValueType( SzArrayTypeSignature arrayType, InteropReferences interopReferences, - InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; @@ -53,14 +50,12 @@ public static TypeDefinition BlittableValueType( /// The for the SZ array type. /// The element marshaller type produced by . /// The instance to use. - /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static TypeDefinition UnmanagedValueType( SzArrayTypeSignature arrayType, TypeDefinition elementMarshallerType, InteropReferences interopReferences, - InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; @@ -84,14 +79,12 @@ public static TypeDefinition UnmanagedValueType( /// The for the SZ array type. /// The element marshaller type produced by . /// The instance to use. - /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static TypeDefinition ManagedValueType( SzArrayTypeSignature arrayType, TypeDefinition elementMarshallerType, InteropReferences interopReferences, - InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; @@ -115,14 +108,12 @@ public static TypeDefinition ManagedValueType( /// The for the SZ array type. /// The element marshaller type produced by . /// The instance to use. - /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static TypeDefinition KeyValuePair( SzArrayTypeSignature arrayType, TypeDefinition elementMarshallerType, InteropReferences interopReferences, - InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; @@ -146,14 +137,12 @@ public static TypeDefinition KeyValuePair( /// The for the SZ array type. /// The element marshaller type produced by . /// The instance to use. - /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. public static TypeDefinition ReferenceType( SzArrayTypeSignature arrayType, TypeDefinition elementMarshallerType, InteropReferences interopReferences, - InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 8a760a8f2..260bf0324 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -135,7 +135,7 @@ private static void Emit(InteropGeneratorArgs args, InteropGeneratorDiscoverySta args.Token.ThrowIfCancellationRequested(); // Emit interop types for SZ array types - DefineSzArrayTypes(args, discoveryState, interopDefinitions, interopReferences, module); + DefineSzArrayTypes(args, discoveryState, emitState, interopDefinitions, interopReferences, module); args.Token.ThrowIfCancellationRequested(); @@ -1905,12 +1905,14 @@ private static void DefineIAsyncOperationWithProgressTypes( /// /// /// + /// The emit state for this invocation. /// The instance to use. /// The instance to use. /// The interop module being built. private static void DefineSzArrayTypes( InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, + InteropGeneratorEmitState emitState, InteropDefinitions interopDefinitions, InteropReferences interopReferences, ModuleDefinition module) @@ -1932,6 +1934,7 @@ private static void DefineSzArrayTypes( InteropTypeDefinitionBuilder.SzArray.Marshaller( arrayType: typeSignature, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); From f60773e2e55ae86c813a36a67becac5ddf95bff4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 13 Jan 2026 16:28:01 -0800 Subject: [PATCH 33/41] Remove SzArrayMarshaller helper class Deleted the InteropMethodDefinitionFactory.SzArrayMarshaller.cs file, removing the static helper for generating 'Free' marshaller methods for SZ array types. This may be part of a refactor or cleanup to consolidate marshalling logic. --- ...thodDefinitionFactory.SzArrayMarshaller.cs | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs deleted file mode 100644 index dfc4987d6..000000000 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.SzArrayMarshaller.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using AsmResolver.DotNet; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet.Metadata.Tables; -using WindowsRuntime.InteropGenerator.References; - -namespace WindowsRuntime.InteropGenerator.Factories; - -/// -internal partial class InteropMethodDefinitionFactory -{ - /// - /// Helpers for marshaller types for SZ array types. - /// - public static class SzArrayMarshaller - { - /// - /// Creates a for the Free marshaller method. - /// - /// The for the SZ array type. - /// The instance to use. - /// The module that will contain the type being created. - public static MethodDefinition Free( - SzArrayTypeSignature arrayType, - InteropReferences interopReferences, - ModuleDefinition module) - { - TypeSignature elementType = arrayType.BaseType; - - // Define the 'Free' method as follows: - // - // public static void Free(uint size, * destination) - MethodDefinition freeMethod = new( - name: "Free"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Void, - parameterTypes: [ - module.CorLibTypeFactory.UInt32, - elementType.GetAbiType(interopReferences).Import(module).MakePointerType()])); - - return freeMethod; - } - } -} \ No newline at end of file From 04cf3e5d61c40bd9aeebf0be02b155eee1536630 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 14:06:27 -0800 Subject: [PATCH 34/41] Refactor key-value pair array marshaller argument handling Updated the marshaller factory to extract key and value types from GenericInstanceTypeSignature instead of using the base type directly. Adjusted method calls to pass keyType and valueType separately, improving type safety and clarity. --- ...eropTypeDefinitionFactory.SzArrayMarshaller.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs index ba5b7b03f..388bd8abe 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs @@ -116,17 +116,18 @@ public static TypeDefinition KeyValuePair( InteropReferences interopReferences, ModuleDefinition module) { - TypeSignature elementType = arrayType.BaseType; - TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); + GenericInstanceTypeSignature elementType = (GenericInstanceTypeSignature)arrayType.BaseType; + TypeSignature keyType = elementType.TypeArguments[0]; + TypeSignature valueType = elementType.TypeArguments[1]; TypeSignature elementMarshallerTypeSignature = elementMarshallerType.ToTypeSignature(); return Marshaller( arrayType: arrayType, - convertToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), - convertToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), - copyToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToUnmanaged(elementType, elementAbiType, elementMarshallerTypeSignature), - copyToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToManaged(elementType, elementAbiType, elementMarshallerTypeSignature), - freeMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(elementType, elementAbiType), + convertToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToUnmanaged(keyType, valueType, elementMarshallerTypeSignature), + convertToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToManaged(keyType, valueType, elementMarshallerTypeSignature), + copyToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToUnmanaged(keyType, valueType, elementMarshallerTypeSignature), + copyToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToManaged(keyType, valueType, elementMarshallerTypeSignature), + freeMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(keyType, valueType), interopReferences: interopReferences, module: module); } From ac007e3c0b9552aba8f1b6d77c6b12fcd88eb44b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 14:27:16 -0800 Subject: [PATCH 35/41] Add GetRawAbiType method to WindowsRuntimeExtensions Introduces GetRawAbiType to retrieve the raw ABI type for a given type without unwrapping. Handles special case for 'void*' by returning WindowsRuntimeObjectReferenceValue to support proper lifetime management. --- .../Extensions/WindowsRuntimeExtensions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 3a7805e0e..a9b14fe0e 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -583,6 +583,22 @@ public TypeSignature GetAbiType(InteropReferences interopReferences) // For all other cases (e.g. interfaces, classes, delegates, etc.), the ABI type is always a pointer return interopReferences.CorLibTypeFactory.Void.MakePointerType(); } + + /// + /// Gets the raw ABI type for a given type (without unwrapping). + /// + /// The instance to use. + /// The raw ABI type for the input type. + public TypeSignature GetRawAbiType(InteropReferences interopReferences) + { + TypeSignature abiType = type.GetAbiType(interopReferences); + + // If the ABI type is 'void*', the marshaller types return it as 'WindowsRuntimeObjectReferenceValue'. + // This allows callers to do proper lifetime management. For all other cases, the ABI type is the same. + return abiType.IsTypeOfVoidPointer() + ? interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature() + : abiType; + } } extension(TypeDefinition type) From 27969c2117f23f69a433ebc7fbbb22168ced456c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 14:27:29 -0800 Subject: [PATCH 36/41] Refactor element marshaller creation logic Extracted common element marshaller creation into a new static ElementMarshaller method to reduce code duplication and improve maintainability. Updated all marshaller factory methods to use the new helper, simplifying method implementations and centralizing marshaller logic. --- ...initionFactory.SzArrayElementMarshaller.cs | 278 ++++-------------- 1 file changed, 63 insertions(+), 215 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index 0c9ab1268..a3cf4734b 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -43,75 +43,14 @@ public static TypeDefinition UnmanagedValueType( .IWindowsRuntimeUnmanagedValueTypeArrayElementMarshaller2 .MakeGenericReferenceType(elementType, elementAbiType); - // We're declaring an 'internal abstract class' type - TypeDefinition elementMarshallerType = new( - ns: InteropUtf8NameFactory.TypeNamespace(arrayType), - name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), - attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, - baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) - { - Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } - }; - - // Rewriting labels - CilInstruction nop_convertToUnmanaged = new(Nop); - CilInstruction nop_convertToManaged = new(Nop); - - // Define the 'ConvertToUnmanaged' method as follows: - // - // public static ConvertToUnmanaged( value) - MethodDefinition convertToUnmanagedMethod = new( - name: "ConvertToUnmanaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) - { - CilInstructions = - { - { Ldarg_0 }, - { nop_convertToUnmanaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToUnmanaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), - method: convertToUnmanagedMethod); - - // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackRawRetValMethodRewrite( - parameterType: elementType, - method: convertToUnmanagedMethod, - marker: nop_convertToUnmanaged); - - // Define the 'ConvertToManaged' method as follows: - // - // public static ConvertToManaged( value) - MethodDefinition convertToManagedMethod = new( - name: "ConvertToManaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementType.Import(module), elementAbiType.Import(module))) - { - CilInstructions = - { - { nop_convertToManaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToManaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), - method: convertToManagedMethod); - - // Track rewriting the managed value for 'ConvertToManaged' - emitState.TrackManagedParameterMethodRewrite( - parameterType: elementType, - method: convertToManagedMethod, - marker: nop_convertToManaged, - parameterIndex: 0); - - return elementMarshallerType; + return ElementMarshaller( + arrayType: arrayType, + interfaceType: interfaceType, + convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType), + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType), + interopReferences: interopReferences, + emitState: emitState, + module: module); } /// @@ -136,75 +75,19 @@ public static TypeDefinition ManagedValueType( .IWindowsRuntimeManagedValueTypeArrayElementMarshaller2 .MakeGenericReferenceType(elementType, elementAbiType); - // We're declaring an 'internal abstract class' type - TypeDefinition elementMarshallerType = new( - ns: InteropUtf8NameFactory.TypeNamespace(arrayType), - name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), - attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, - baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) - { - Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } - }; + // Get the element marshaller type with the common method implementations + TypeDefinition elementMarshallerType = ElementMarshaller( + arrayType: arrayType, + interfaceType: interfaceType, + convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType), + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType), + interopReferences: interopReferences, + emitState: emitState, + module: module); // Rewriting labels - CilInstruction nop_convertToUnmanaged = new(Nop); - CilInstruction nop_convertToManaged = new(Nop); CilInstruction nop_dispose = new(Nop); - // Define the 'ConvertToUnmanaged' method as follows: - // - // public static ConvertToUnmanaged( value) - MethodDefinition convertToUnmanagedMethod = new( - name: "ConvertToUnmanaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementAbiType.Import(module), elementType.Import(module))) - { - CilInstructions = - { - { Ldarg_0 }, - { nop_convertToUnmanaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToUnmanaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType).Import(module), - method: convertToUnmanagedMethod); - - // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackRawRetValMethodRewrite( - parameterType: elementType, - method: convertToUnmanagedMethod, - marker: nop_convertToUnmanaged); - - // Define the 'ConvertToManaged' method as follows: - // - // public static ConvertToManaged( value) - MethodDefinition convertToManagedMethod = new( - name: "ConvertToManaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic(elementType.Import(module), elementAbiType.Import(module))) - { - CilInstructions = - { - { nop_convertToManaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToManaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType).Import(module), - method: convertToManagedMethod); - - // Track rewriting the managed value for 'ConvertToManaged' - emitState.TrackManagedParameterMethodRewrite( - parameterType: elementType, - method: convertToManagedMethod, - marker: nop_convertToManaged, - parameterIndex: 0); - // Define the 'Dispose' method as follows: // // public static void Dispose( value) @@ -258,81 +141,14 @@ public static TypeDefinition KeyValuePair( .IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 .MakeGenericReferenceType(keyType, valueType); - // We're declaring an 'internal abstract class' type - TypeDefinition elementMarshallerType = new( - ns: InteropUtf8NameFactory.TypeNamespace(arrayType), - name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), - attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, - baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) - { - Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } - }; - - TypeSignature keyValuePairType = interopReferences.KeyValuePair2.MakeGenericValueType(keyType, valueType); - - // Rewriting labels - CilInstruction nop_convertToUnmanaged = new(Nop); - CilInstruction nop_convertToManaged = new(Nop); - - // Define the 'ConvertToUnmanaged' method as follows: - // - // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(KeyValuePair<, > value) - MethodDefinition convertToUnmanagedMethod = new( - name: "ConvertToUnmanaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature(), - parameterTypes: [keyValuePairType.Import(module)])) - { - CilInstructions = - { - { Ldarg_0 }, - { nop_convertToUnmanaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToUnmanaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType).Import(module), - method: convertToUnmanagedMethod); - - // Track rewriting the native value for 'ConvertToUnmanaged' - emitState.TrackRawRetValMethodRewrite( - parameterType: elementType, - method: convertToUnmanagedMethod, - marker: nop_convertToUnmanaged); - - // Define the 'ConvertToManaged' method as follows: - // - // public static KeyValuePair<, > ConvertToManaged(void* value) - MethodDefinition convertToManagedMethod = new( - name: "ConvertToManaged"u8, - attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, - signature: MethodSignature.CreateStatic( - returnType: keyValuePairType.Import(module), - parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])) - { - CilInstructions = - { - { nop_convertToManaged }, - { Ret } - } - }; - - // Add and implement the 'ConvertToManaged' method - elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(keyType, valueType).Import(module), - method: convertToManagedMethod); - - // Track rewriting the managed value for 'ConvertToManaged' - emitState.TrackManagedParameterMethodRewrite( - parameterType: elementType, - method: convertToManagedMethod, - marker: nop_convertToManaged, - parameterIndex: 0); - - return elementMarshallerType; + return ElementMarshaller( + arrayType: arrayType, + interfaceType: interfaceType, + convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType), + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(keyType, valueType), + interopReferences: interopReferences, + emitState: emitState, + module: module); } /// @@ -356,6 +172,38 @@ public static TypeDefinition ReferenceType( .IWindowsRuntimeReferenceTypeArrayElementMarshaller1 .MakeGenericReferenceType(elementType); + return ElementMarshaller( + arrayType: arrayType, + interfaceType: interfaceType, + convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType), + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(elementType), + interopReferences: interopReferences, + emitState: emitState, + module: module); + } + + /// + /// Creates a for the element marshaller for some element type. + /// + /// The for the SZ array type. + /// The interface type the element marshaller type should implement. + /// The ConvertToUnmanaged interface method being implemented. + /// The ConvertToManaged interface method being implemented. + /// The instance to use. + /// The emit state for this invocation. + /// The module that will contain the type being created. + /// The resulting element marshaller type. + public static TypeDefinition ElementMarshaller( + SzArrayTypeSignature arrayType, + TypeSignature interfaceType, + MemberReference convertToUnmanagedInterfaceMethod, + MemberReference convertToManagedInterfaceMethod, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature elementType = arrayType.BaseType; + // We're declaring an 'internal abstract class' type TypeDefinition elementMarshallerType = new( ns: InteropUtf8NameFactory.TypeNamespace(arrayType), @@ -372,12 +220,12 @@ public static TypeDefinition ReferenceType( // Define the 'ConvertToUnmanaged' method as follows: // - // public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( value) + // public static ConvertToUnmanaged( value) MethodDefinition convertToUnmanagedMethod = new( name: "ConvertToUnmanaged"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, signature: MethodSignature.CreateStatic( - returnType: interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature(), + returnType: elementType.GetRawAbiType(interopReferences).Import(module), parameterTypes: [elementType.Import(module)])) { CilInstructions = @@ -390,7 +238,7 @@ public static TypeDefinition ReferenceType( // Add and implement the 'ConvertToUnmanaged' method elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType).Import(module), + declaration: convertToUnmanagedInterfaceMethod.Import(module), method: convertToUnmanagedMethod); // Track rewriting the native value for 'ConvertToUnmanaged' @@ -401,13 +249,13 @@ public static TypeDefinition ReferenceType( // Define the 'ConvertToManaged' method as follows: // - // public static ConvertToManaged(void* value) + // public static ConvertToManaged( value) MethodDefinition convertToManagedMethod = new( name: "ConvertToManaged"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, signature: MethodSignature.CreateStatic( returnType: elementType.Import(module), - parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])) + parameterTypes: [elementType.GetAbiType(interopReferences).Import(module)])) { CilInstructions = { @@ -418,7 +266,7 @@ public static TypeDefinition ReferenceType( // Add and implement the 'ConvertToManaged' method elementMarshallerType.AddMethodImplementation( - declaration: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(elementType).Import(module), + declaration: convertToManagedInterfaceMethod.Import(module), method: convertToManagedMethod); // Track rewriting the managed value for 'ConvertToManaged' From 8e1865c409a33f6b1e941a4853cd902964bbd493 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 15:08:11 -0800 Subject: [PATCH 37/41] Refactor type name generation for arrays and generics Simplifies and restructures the logic for generating type names, especially for SZ arrays and generic types. Introduces a helper method to handle generic type arguments and ensures consistent formatting for arrays with generic element types. --- .../Factories/InteropUtf8NameFactory.cs | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropUtf8NameFactory.cs b/src/WinRT.Interop.Generator/Factories/InteropUtf8NameFactory.cs index 10b05f77b..fd47ba213 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropUtf8NameFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropUtf8NameFactory.cs @@ -74,62 +74,41 @@ static void AppendTypeName( return; } - // SZ arrays are enclosed in angle brackets - if (typeSignature is SzArrayTypeSignature) + // SZ arrays are enclosed in angle brackets, and have the 'Array' suffix at the end + if (typeSignature is SzArrayTypeSignature arrayTypeSignature) { interpolatedStringHandler.AppendLiteral("<"); - } - // Each type name uses this format: 'TYPE_NAME' - interpolatedStringHandler.AppendLiteral("<"); - interpolatedStringHandler.AppendFormatted(AssemblyNameOrWellKnownIdentifier(typeSignature.Scope!.GetAssembly()!.Name, typeSignature)); - interpolatedStringHandler.AppendLiteral(">"); + AppendTypeName(ref interpolatedStringHandler, arrayTypeSignature.BaseType, depth); - // If the type is generic, append the definition name and the type arguments - if (typeSignature is GenericInstanceTypeSignature genericInstanceTypeSignature) + interpolatedStringHandler.AppendLiteral(">Array"); + } + else { - // Append the name of the generic type, without any type arguments (those are appended below) - AppendRawTypeName(ref interpolatedStringHandler, genericInstanceTypeSignature.GenericType, depth); - + // Each type name uses this format: 'TYPE_NAME' interpolatedStringHandler.AppendLiteral("<"); + interpolatedStringHandler.AppendFormatted(AssemblyNameOrWellKnownIdentifier(typeSignature.Scope!.GetAssembly()!.Name, typeSignature)); + interpolatedStringHandler.AppendLiteral(">"); - foreach ((int i, TypeSignature typeArgumentSignature) in genericInstanceTypeSignature.TypeArguments.Index()) - { - // We use '|' to separate generic type arguments - if (i > 0) - { - interpolatedStringHandler.AppendLiteral("|"); - } - - // Append the type argument with the same format as the root type. This is - // important to ensure that nested generic types will be handled correctly. - AppendTypeName(ref interpolatedStringHandler, typeArgumentSignature, depth: depth + 1); - } + // Extract the generic type if we have a generic signature, or use the type signature directly + ITypeDescriptor originalTypeDescriptor = ((typeSignature as GenericInstanceTypeSignature)?.GenericType) ?? (ITypeDescriptor)typeSignature; - interpolatedStringHandler.AppendLiteral(">"); - } - else if (typeSignature is SzArrayTypeSignature arrayTypeSignature) - { - // Same as below, but we use the element type name, not the array type name - AppendRawTypeName(ref interpolatedStringHandler, arrayTypeSignature.BaseType, depth); - } - else - { - // Simple case for normal type signatures - AppendRawTypeName(ref interpolatedStringHandler, typeSignature, depth); - } + // Append the original type name first, regardless of whether it's an array or a constructed generic + AppendRawTypeName(ref interpolatedStringHandler, originalTypeDescriptor, depth); - // Complete the name mangling for SZ arrays - if (typeSignature is SzArrayTypeSignature) - { - interpolatedStringHandler.AppendLiteral(">Array"); + // If the type is generic, append the definition name and the type arguments. We pass the original + // type descriptor here, because we also want to detect arrays with an element type that's generic. + if (typeSignature is GenericInstanceTypeSignature genericInstanceTypeSignature) + { + AppendTypeArguments(ref interpolatedStringHandler, genericInstanceTypeSignature, depth); + } } } // Helper to recursively build the type name (to handle nested generic types too) static void AppendRawTypeName( ref DefaultInterpolatedStringHandler interpolatedStringHandler, - IMemberDescriptor type, + ITypeDescriptor type, int depth) { // We can skip the namespace when the indentation level is '0', as that means @@ -166,6 +145,30 @@ static void AppendRawTypeName( } } + // Helper to iteratively print the type arguments for a constructed type + static void AppendTypeArguments( + ref DefaultInterpolatedStringHandler interpolatedStringHandler, + GenericInstanceTypeSignature type, + int depth) + { + interpolatedStringHandler.AppendLiteral("<"); + + foreach ((int i, TypeSignature typeArgumentSignature) in type.TypeArguments.Index()) + { + // We use '|' to separate generic type arguments + if (i > 0) + { + interpolatedStringHandler.AppendLiteral("|"); + } + + // Append the type argument with the same format as the root type. This is + // important to ensure that nested generic types will be handled correctly. + AppendTypeName(ref interpolatedStringHandler, typeArgumentSignature, depth: depth + 1); + } + + interpolatedStringHandler.AppendLiteral(">"); + } + // Append the full type name first AppendTypeName(ref interpolatedStringHandler, typeSignature, depth: 0); From afffd887692304db0a8c5afd55d42e0eabd6864e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 15:10:51 -0800 Subject: [PATCH 38/41] Refactor array marshaller Free logic to shared class Moved the Free method for releasing arrays of unknown COM interfaces from WindowsRuntimeKeyValuePairTypeArrayMarshaller and WindowsRuntimeReferenceTypeArrayMarshaller into a new WindowsRuntimeUnknownArrayMarshaller class. This reduces code duplication and centralizes the array cleanup logic. --- ...uePairTypeArrayMarshaller{TKey, TValue}.cs | 18 --------- ...sRuntimeReferenceTypeArrayMarshaller{T}.cs | 18 --------- .../WindowsRuntimeUnknownArrayMarshaller.cs | 39 +++++++++++++++++++ 3 files changed, 39 insertions(+), 36 deletions(-) create mode 100644 src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnknownArrayMarshaller.cs diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs index c58077028..ec3e5089c 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeKeyValuePairTypeArrayMarshaller{TKey, TValue}.cs @@ -139,22 +139,4 @@ public static void CopyToManaged(uint size, void** source, S destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); } } - - /// - public static void Free(uint size, void** array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (uint i = 0; i < size; i++) - { - WindowsRuntimeUnknownMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } } \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs index ff9d001e3..0ca314f15 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeReferenceTypeArrayMarshaller{T}.cs @@ -138,22 +138,4 @@ public static void CopyToManaged(uint size, void** source, S destination[(int)i] = TElementMarshaller.ConvertToManaged(source[i]); } } - - /// - public static void Free(uint size, void** array) - { - if (size == 0) - { - return; - } - - ArgumentNullException.ThrowIfNull(array); - - for (uint i = 0; i < size; i++) - { - WindowsRuntimeUnknownMarshaller.Free(array[i]); - } - - Marshal.FreeCoTaskMem((nint)array); - } } \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnknownArrayMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnknownArrayMarshaller.cs new file mode 100644 index 000000000..9ee3ecb2b --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/SzArrays/WindowsRuntimeUnknownArrayMarshaller.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace WindowsRuntime.InteropServices.Marshalling; + +/// +/// A marshaller for arrays of some Windows Runtime types represented as unknown COM interfaces. +/// +/// +/// This type mirrors , but for arrays. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class WindowsRuntimeUnknownArrayMarshaller +{ + /// + public static void Free(uint size, void** array) + { + if (size == 0) + { + return; + } + + ArgumentNullException.ThrowIfNull(array); + + for (uint i = 0; i < size; i++) + { + WindowsRuntimeUnknownMarshaller.Free(array[i]); + } + + Marshal.FreeCoTaskMem((nint)array); + } +} \ No newline at end of file From 6d5efd46711ddd5d395831d4aac80020cb4f02df Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 15:25:27 -0800 Subject: [PATCH 39/41] Refactor array marshaller free method references Replaces type-specific Free method references for key-value pair and reference type array marshallers with a unified WindowsRuntimeUnknownArrayMarshaller.Free reference. Removes now-unused methods and type references from InteropReferences to simplify and centralize array marshaller cleanup logic. --- ...TypeDefinitionFactory.SzArrayMarshaller.cs | 4 +- .../References/InteropReferences.cs | 48 ++++++------------- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs index 388bd8abe..ff7843205 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayMarshaller.cs @@ -127,7 +127,7 @@ public static TypeDefinition KeyValuePair( convertToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerConvertToManaged(keyType, valueType, elementMarshallerTypeSignature), copyToUnmanagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToUnmanaged(keyType, valueType, elementMarshallerTypeSignature), copyToManagedMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToManaged(keyType, valueType, elementMarshallerTypeSignature), - freeMethod: interopReferences.WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(keyType, valueType), + freeMethod: interopReferences.WindowsRuntimeUnknownArrayMarshallerFree, interopReferences: interopReferences, module: module); } @@ -155,7 +155,7 @@ public static TypeDefinition ReferenceType( convertToManagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerConvertToManaged(elementType, elementMarshallerTypeSignature), copyToUnmanagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerCopyToUnmanaged(elementType, elementMarshallerTypeSignature), copyToManagedMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerCopyToManaged(elementType, elementMarshallerTypeSignature), - freeMethod: interopReferences.WindowsRuntimeReferenceTypeArrayMarshallerFree(elementType), + freeMethod: interopReferences.WindowsRuntimeUnknownArrayMarshallerFree, interopReferences: interopReferences, module: module); } diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index f07893567..0cd5ae3ad 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -923,6 +923,11 @@ public InteropReferences( /// public TypeReference WindowsRuntimeReferenceTypeArrayMarshaller1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeReferenceTypeArrayMarshaller`1"u8); + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnknownArrayMarshaller. + /// + public TypeReference WindowsRuntimeUnknownArrayMarshaller => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices.Marshalling"u8, "WindowsRuntimeUnknownArrayMarshaller"u8); + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>. /// @@ -1891,6 +1896,16 @@ public InteropReferences( _corLibTypeFactory.Void.MakePointerType(), Guid.ToValueTypeSignature().MakeByReferenceType()])); + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeUnknownArrayMarshaller.Free. + /// + public MemberReference WindowsRuntimeUnknownArrayMarshallerFree => field ??= WindowsRuntimeUnknownArrayMarshaller + .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + _corLibTypeFactory.UInt32, + _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.TypeArrayMarshaller.ConvertToUnmanaged. /// @@ -2644,22 +2659,6 @@ public MethodSpecification WindowsRuntimeReferenceTypeArrayMarshallerCopyToManag .MakeGenericInstanceMethod(elementMarshallerType); } - /// - /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeReferenceTypeArrayMarshaller<T>.Free. - /// - /// The input element type. - public MemberReference WindowsRuntimeReferenceTypeArrayMarshallerFree(TypeSignature elementType) - { - return WindowsRuntimeReferenceTypeArrayMarshaller1 - .MakeGenericReferenceType(elementType) - .ToTypeDefOrRef() - .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); - } - /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.ConvertToUnmanaged<TElementMarshaller>. /// @@ -2754,23 +2753,6 @@ public MethodSpecification WindowsRuntimeKeyValuePairTypeArrayMarshallerCopyToMa .MakeGenericInstanceMethod(elementMarshallerType); } - /// - /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeKeyValuePairTypeArrayMarshaller<TKey, TValue>.Free. - /// - /// The input key type. - /// The input value type. - public MemberReference WindowsRuntimeKeyValuePairTypeArrayMarshallerFree(TypeSignature keyType, TypeSignature valueType) - { - return WindowsRuntimeKeyValuePairTypeArrayMarshaller2 - .MakeGenericReferenceType(keyType, valueType) - .ToTypeDefOrRef() - .CreateMemberReference("Free"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Void, - parameterTypes: [ - _corLibTypeFactory.UInt32, - _corLibTypeFactory.Void.MakePointerType().MakePointerType()])); - } - /// /// Gets the for AddEventHandler for . /// From abf1bab2d7ef322788f3a6ebfb03e70e1caa4ad3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 15:44:03 -0800 Subject: [PATCH 40/41] Add isValueType parameter to element marshaller creation Introduces an isValueType parameter to control whether element marshaller types are emitted as value types or reference types. This enables full specialization and inlining for value type arrays and key-value pairs, improving performance and reducing code size when both key and value are value types. --- ...initionFactory.SzArrayElementMarshaller.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs index a3cf4734b..75ee7c6a7 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.SzArrayElementMarshaller.cs @@ -48,6 +48,7 @@ public static TypeDefinition UnmanagedValueType( interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType), convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType), + isValueType: true, interopReferences: interopReferences, emitState: emitState, module: module); @@ -81,6 +82,7 @@ public static TypeDefinition ManagedValueType( interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToUnmanaged(elementType, elementAbiType), convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeManagedValueTypeArrayElementMarshallerConvertToManaged(elementType, elementAbiType), + isValueType: true, interopReferences: interopReferences, emitState: emitState, module: module); @@ -141,11 +143,18 @@ public static TypeDefinition KeyValuePair( .IWindowsRuntimeKeyValuePairTypeArrayElementMarshaller2 .MakeGenericReferenceType(keyType, valueType); + // If both the key and the value types are value types, it means the whole marshaller will be specialized. + // In that case we can emit the element marshaller type as a value type as well, so the whole thing can be + // fully specialized and inlined. We don't do this if either type is a reference type, because that means + // the generic instantiation could still be shared between different types, so we prefer to save size there. + bool isValueType = keyType.IsValueType && valueType.IsValueType; + return ElementMarshaller( arrayType: arrayType, interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToUnmanaged(keyType, valueType), convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeArrayElementMarshallerConvertToManaged(keyType, valueType), + isValueType: isValueType, interopReferences: interopReferences, emitState: emitState, module: module); @@ -177,6 +186,7 @@ public static TypeDefinition ReferenceType( interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToUnmanaged(elementType), convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeArrayElementMarshallerConvertToManaged(elementType), + isValueType: false, interopReferences: interopReferences, emitState: emitState, module: module); @@ -189,6 +199,7 @@ public static TypeDefinition ReferenceType( /// The interface type the element marshaller type should implement. /// The ConvertToUnmanaged interface method being implemented. /// The ConvertToManaged interface method being implemented. + /// Indicates whether the element marshaller type should be emitted as a value type. /// The instance to use. /// The emit state for this invocation. /// The module that will contain the type being created. @@ -198,18 +209,24 @@ public static TypeDefinition ElementMarshaller( TypeSignature interfaceType, MemberReference convertToUnmanagedInterfaceMethod, MemberReference convertToManagedInterfaceMethod, + bool isValueType, InteropReferences interopReferences, InteropGeneratorEmitState emitState, ModuleDefinition module) { TypeSignature elementType = arrayType.BaseType; + // Select the attributes and base type depending on whether we want a value type or not + (TypeAttributes attributes, ITypeDefOrRef baseType) = isValueType + ? (TypeAttributes.SequentialLayout | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, interopReferences.ValueType) + : (TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, module.CorLibTypeFactory.Object.ToTypeDefOrRef()); + // We're declaring an 'internal abstract class' type TypeDefinition elementMarshallerType = new( ns: InteropUtf8NameFactory.TypeNamespace(arrayType), name: InteropUtf8NameFactory.TypeName(arrayType, "ElementMarshaller"), - attributes: TypeAttributes.AutoLayout | TypeAttributes.Abstract | TypeAttributes.BeforeFieldInit, - baseType: module.CorLibTypeFactory.Object.ToTypeDefOrRef()) + attributes: attributes, + baseType: baseType) { Interfaces = { new InterfaceImplementation(interfaceType.Import(module).ToTypeDefOrRef()) } }; From 6a2978d217dc644557f7daf2622a7368ff38ac89 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 14 Jan 2026 15:49:08 -0800 Subject: [PATCH 41/41] Use ReferenceType for SzArrayMarshaller Replaces the use of UnmanagedValueType with ReferenceType when creating the marshallerType for SzArrayMarshaller. This change likely aligns the marshaller behavior with expected reference type semantics. --- .../Builders/InteropTypeDefinitionBuilder.SzArray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs index 20b75c28c..c2b009cea 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.SzArray.cs @@ -142,7 +142,7 @@ public static void Marshaller( module.TopLevelTypes.Add(elementMarshallerType); - marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.UnmanagedValueType( + marshallerType = InteropTypeDefinitionFactory.SzArrayMarshaller.ReferenceType( arrayType: arrayType, elementMarshallerType: elementMarshallerType, interopReferences: interopReferences,