From 4d86540766b69db11f5d400d43b9888c910a4cd5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:12:32 -0800 Subject: [PATCH 01/38] Update ICommand TypeReference creation method Replaces the use of _corLibTypeFactory.CorLibScope with SystemObjectModel for creating the TypeReference for System.Windows.Input.ICommand. This change may improve consistency or correctness in type reference creation. --- src/WinRT.Interop.Generator/References/InteropReferences.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 821893b33..348103964 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -251,7 +251,7 @@ public InteropReferences( /// /// Gets the for . /// - public TypeReference ICommand => field ??= _corLibTypeFactory.CorLibScope.CreateTypeReference("System.Windows.Input"u8, "ICommand"u8); + public TypeReference ICommand => field ??= SystemObjectModel.CreateTypeReference("System.Windows.Input"u8, "ICommand"u8); /// /// Gets the for . From aca6336e18b2597cc5635ea58613bf6c9bcef14a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:12:47 -0800 Subject: [PATCH 02/38] Add IVectorChangedEventArgs to WellKnownInterfaceIIDs Added support for mapping the IVectorChangedEventArgs interface to its well-known IID string in WellKnownInterfaceIIDs. This improves interface recognition for Windows.Foundation.Collections.IVectorChangedEventArgs. --- .../References/WellKnownInterfaceIIDs.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index a040c1244..a090a10e2 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -86,6 +86,8 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I => "Windows_Foundation_IAsyncInfo", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IAsyncAction) => "Windows_Foundation_IAsyncAction", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IVectorChangedEventArgs) + => "Windows_Foundation_Collections_IVectorChangedEventArgs", // XAML types _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) && useWindowsUIXamlProjections From c48be70a5185f7738b3ff907f91650de1bea1db4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:25:45 -0800 Subject: [PATCH 03/38] Move IDisposable and IServiceProvider IID mappings Relocated the IID mappings for IDisposable and IServiceProvider to an earlier position in the switch statement within WellKnownInterfaceIIDs.cs. This change ensures these interfaces are matched before others, likely to address precedence or matching issues. --- .../References/WellKnownInterfaceIIDs.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index a090a10e2..6e9ed6ebd 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -127,6 +127,10 @@ public static bool TryGetGUID( { guid = interfaceType switch { + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IDisposable) + => new Guid("1BFCA4F6-2C4E-5174-9869-B39D35848FCC"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IServiceProvider) + => new Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.EventHandler) => new Guid("9DE1C535-6AE1-11E0-84E1-18A905BCC53F"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.EventHandler1) @@ -181,8 +185,6 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.A => new Guid("C261D8D0-71BA-5F38-A239-872342253A18"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IVectorChangedEventArgs) => new Guid("575933DF-34FE-4480-AF15-07691F3D5D9B"), - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IDisposable) - => new Guid("1BFCA4F6-2C4E-5174-9869-B39D35848FCC"), _ => Guid.Empty }; From 386504ed10a7e44e50f198c63e53fbe7f101b0dc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:45:41 -0800 Subject: [PATCH 04/38] Add useWindowsUIXamlProjections to IID resolution Updated GuidGenerator and WellKnownInterfaceIIDs to support resolving interface IIDs based on the useWindowsUIXamlProjections flag. This enables correct GUID selection for interfaces that differ between Windows.UI.Xaml and Microsoft.UI.Xaml projections, improving compatibility and correctness in generated interop signatures. --- .../Helpers/GuidGenerator.cs | 13 ++++++-- .../Helpers/SignatureGenerator.Projections.cs | 30 +++++++++++++++---- .../References/WellKnownInterfaceIIDs.cs | 29 ++++++++++++++---- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/WinRT.Interop.Generator/Helpers/GuidGenerator.cs b/src/WinRT.Interop.Generator/Helpers/GuidGenerator.cs index 32261265f..49493ab62 100644 --- a/src/WinRT.Interop.Generator/Helpers/GuidGenerator.cs +++ b/src/WinRT.Interop.Generator/Helpers/GuidGenerator.cs @@ -44,13 +44,22 @@ public static Guid CreateIID(TypeSignature type, InteropReferences interopRefere /// interfaces and, if necessary, the type's . /// /// The type descriptor to try to get the IID for. + /// Whether to use Windows.UI.Xaml projections. /// The instance to use. /// The resulting value, if found. /// Whether was succesfully retrieved. - public static bool TryGetIIDFromWellKnownInterfaceIIDsOrAttribute(ITypeDescriptor type, InteropReferences interopReferences, out Guid iid) + public static bool TryGetIIDFromWellKnownInterfaceIIDsOrAttribute( + ITypeDescriptor type, + bool useWindowsUIXamlProjections, + InteropReferences interopReferences, + out Guid iid) { // First try to get the IID from the custom-mapped types mapping - if (WellKnownInterfaceIIDs.TryGetGUID(type, interopReferences, out iid)) + if (WellKnownInterfaceIIDs.TryGetGUID( + interfaceType: type, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + guid: out iid)) { return true; } diff --git a/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.Projections.cs b/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.Projections.cs index a314c959b..0ebce8c72 100644 --- a/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.Projections.cs +++ b/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.Projections.cs @@ -24,7 +24,11 @@ internal partial class SignatureGenerator private static string? GenericInstance(GenericInstanceTypeSignature typeSignature, InteropReferences interopReferences, bool useWindowsUIXamlProjections) { // If we fail to get the IID of the generic interface, we can't do anything else - if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute(typeSignature.GenericType, interopReferences, out Guid interfaceIid)) + if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute( + type: typeSignature.GenericType, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + iid: out Guid interfaceIid)) { return null; } @@ -129,7 +133,11 @@ internal partial class SignatureGenerator private static string? Delegate(TypeDefinition typeDefinition, InteropReferences interopReferences, bool useWindowsUIXamlProjections) { // Just like for generic instantiations, we need to resolve the IID for the type first - if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute(typeDefinition, interopReferences, out Guid iid)) + if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute( + type: typeDefinition, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + iid: out Guid iid)) { return null; } @@ -159,7 +167,11 @@ internal partial class SignatureGenerator } // Otherwise, get the IID from the type definition and use it - if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute(typeDefinition, interopReferences, out Guid iid)) + if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute( + type: typeDefinition, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + iid: out Guid iid)) { return null; } @@ -176,7 +188,11 @@ internal partial class SignatureGenerator private static string? Interface(TypeDefinition typeDefinition, InteropReferences interopReferences, bool useWindowsUIXamlProjections) { // For all interface types, we should always be able to resolve their IID - if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute(typeDefinition, interopReferences, out Guid iid)) + if (!GuidGenerator.TryGetIIDFromWellKnownInterfaceIIDsOrAttribute( + type: typeDefinition, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + iid: out Guid iid)) { return null; } @@ -206,7 +222,11 @@ internal partial class SignatureGenerator } // Get the IID for 'Nullable', which is the one we use for 'IReference' - _ = WellKnownInterfaceIIDs.TryGetGUID(interopReferences.Nullable1, interopReferences, out Guid iid); + _ = WellKnownInterfaceIIDs.TryGetGUID( + interfaceType: interopReferences.Nullable1, + useWindowsUIXamlProjections: useWindowsUIXamlProjections, + interopReferences: interopReferences, + guid: out Guid iid); // Construct the signature for the boxed delegate (the base type will be the possibly constructed delegate) return $"pinterface({{{iid}}};{GetSignature(typeSignature.BaseType, interopReferences, useWindowsUIXamlProjections)})"; diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index 6e9ed6ebd..2a4ebbe32 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -67,7 +67,9 @@ internal static class WellKnownInterfaceIIDs /// The for the get_IID_... method for . /// /// - /// The types handled by this method should be kept in sync with . + /// The types handled by this method should be kept in sync with + /// and + /// . /// public static MemberReference get_IID( ITypeDescriptor interfaceType, @@ -80,8 +82,6 @@ public static MemberReference get_IID( // Shared types _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IDisposable) => "Windows_Foundation_IClosable", - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IServiceProvider) - => "Microsoft_UI_Xaml_IXamlServiceProvider", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IAsyncInfo) => "Windows_Foundation_IAsyncInfo", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IAsyncAction) @@ -104,6 +104,8 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I => "Microsoft_UI_Xaml_Input_ICommand", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyDataErrorInfo) => "Microsoft_UI_Xaml_Data_INotifyDataErrorInfo", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IServiceProvider) + => "Microsoft_UI_Xaml_IXamlServiceProvider", _ => throw WellKnownInteropExceptions.InvalidCustomMappedTypeForWellKnownInterfaceIIDs(interfaceType) }; @@ -117,20 +119,21 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I /// Attempts to resolve a well-known Windows Runtime interface GUID for the specified type signature. /// /// The representing the managed type to inspect + /// Whether to use Windows.UI.Xaml projections. /// The instance to use. /// Out parameter for the resolved of the type. /// true if a matching GUID was found; otherwise, false. public static bool TryGetGUID( ITypeDescriptor interfaceType, + bool useWindowsUIXamlProjections, InteropReferences interopReferences, out Guid guid) { guid = interfaceType switch { + // Shared types _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IDisposable) => new Guid("1BFCA4F6-2C4E-5174-9869-B39D35848FCC"), - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IServiceProvider) - => new Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.EventHandler) => new Guid("9DE1C535-6AE1-11E0-84E1-18A905BCC53F"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.EventHandler1) @@ -185,6 +188,22 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.A => new Guid("C261D8D0-71BA-5F38-A239-872342253A18"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IVectorChangedEventArgs) => new Guid("575933DF-34FE-4480-AF15-07691F3D5D9B"), + + // XAML types + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) && useWindowsUIXamlProjections + => new Guid("28B167D5-1A31-465B-9B25-D5C3AE686C40"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) + => new Guid("530155E1-28A5-5693-87CE-30724D95A06D"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyPropertyChanged) && useWindowsUIXamlProjections + => new Guid("CF75D69C-F2F4-486B-B302-BB4C09BAEBFA"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyPropertyChanged) + => new Guid("90B17601-B065-586E-83D9-9ADC3A695284"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.ICommand) + => new Guid("E5AF3542-CA67-4081-995B-709DD13792DF"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyDataErrorInfo) + => new Guid("0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IServiceProvider) + => new Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB"), _ => Guid.Empty }; From 2c87941d34d5a7842060ba08f5b930b1301af2b7 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 13 Dec 2025 14:22:13 -0800 Subject: [PATCH 05/38] Add Guid attributes to WinRT interface definitions Added [Guid] attributes to several Windows.Foundation and Windows.Foundation.Collections interface definitions to specify their interface IDs. This change improves COM interop and aligns the interfaces with their corresponding WinRT specifications. --- .../Windows.Foundation/Collections/IMapChangedEventArgs{K}.cs | 2 ++ .../Windows.Foundation/Collections/IObservableMap{K, V}.cs | 2 ++ .../Windows.Foundation/Collections/IObservableVector{T}.cs | 2 ++ .../Windows.Foundation/Collections/IVectorChangedEventArgs.cs | 2 ++ src/WinRT.Runtime2/Windows.Foundation/IAsyncAction.cs | 2 ++ .../Windows.Foundation/IAsyncActionWithProgress{TProgress}.cs | 2 ++ src/WinRT.Runtime2/Windows.Foundation/IAsyncInfo.cs | 2 ++ .../IAsyncOperationWithProgress{TResult, TProgress}.cs | 2 ++ .../Windows.Foundation/IAsyncOperation{TResult}.cs | 2 ++ 9 files changed, 18 insertions(+) diff --git a/src/WinRT.Runtime2/Windows.Foundation/Collections/IMapChangedEventArgs{K}.cs b/src/WinRT.Runtime2/Windows.Foundation/Collections/IMapChangedEventArgs{K}.cs index 3fd530e26..10f8bad5d 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Collections/IMapChangedEventArgs{K}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Collections/IMapChangedEventArgs{K}.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -11,6 +12,7 @@ namespace Windows.Foundation.Collections; /// /// The type of keys in the map. [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("9939F4DF-050A-4C0F-AA60-77075F9C4777")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IMapChangedEventArgs { diff --git a/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableMap{K, V}.cs b/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableMap{K, V}.cs index 0ccb2c01b..921e0fd22 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableMap{K, V}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableMap{K, V}.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -14,6 +15,7 @@ namespace Windows.Foundation.Collections; /// The type of keys in the observable map. /// The type of values in the observable map. [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("65DF2BF5-BF39-41B5-AEBC-5A9D865E472B")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IObservableMap : IDictionary, ICollection>, IEnumerable>, IEnumerable { diff --git a/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableVector{T}.cs b/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableVector{T}.cs index f82edbcbc..31df8a627 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableVector{T}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Collections/IObservableVector{T}.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -13,6 +14,7 @@ namespace Windows.Foundation.Collections; /// /// The type of elements in the observable vector. [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("5917EB53-50B4-4A0D-B309-65862B3F1DBC")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IObservableVector : IList, ICollection, IEnumerable, IEnumerable { diff --git a/src/WinRT.Runtime2/Windows.Foundation/Collections/IVectorChangedEventArgs.cs b/src/WinRT.Runtime2/Windows.Foundation/Collections/IVectorChangedEventArgs.cs index 239e48693..9f07ae237 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Collections/IVectorChangedEventArgs.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Collections/IVectorChangedEventArgs.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -10,6 +11,7 @@ namespace Windows.Foundation.Collections; /// Provides data for the changed event of a vector. /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("575933DF-34FE-4480-AF15-07691F3D5D9B")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IVectorChangedEventArgs { diff --git a/src/WinRT.Runtime2/Windows.Foundation/IAsyncAction.cs b/src/WinRT.Runtime2/Windows.Foundation/IAsyncAction.cs index b77cba5c1..3fac041f9 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/IAsyncAction.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/IAsyncAction.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -17,6 +18,7 @@ namespace Windows.Foundation; /// /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("5A648006-843A-4DA9-865B-9D26E5DFAD7B")] [ContractVersion(typeof(FoundationContract), 65536u)] [ABI.Windows.Foundation.IAsyncActionComWrappersMarshaller] public interface IAsyncAction : IAsyncInfo diff --git a/src/WinRT.Runtime2/Windows.Foundation/IAsyncActionWithProgress{TProgress}.cs b/src/WinRT.Runtime2/Windows.Foundation/IAsyncActionWithProgress{TProgress}.cs index a5c066def..7c4c0a86d 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/IAsyncActionWithProgress{TProgress}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/IAsyncActionWithProgress{TProgress}.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -18,6 +19,7 @@ namespace Windows.Foundation; /// /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("1F6DB258-E803-48A1-9546-EB7353398884")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IAsyncActionWithProgress : IAsyncInfo { diff --git a/src/WinRT.Runtime2/Windows.Foundation/IAsyncInfo.cs b/src/WinRT.Runtime2/Windows.Foundation/IAsyncInfo.cs index caea59409..5bb3b01a9 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/IAsyncInfo.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/IAsyncInfo.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -11,6 +12,7 @@ namespace Windows.Foundation; /// Provides support for asynchronous operations. /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("00000036-0000-0000-C000-000000000046")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IAsyncInfo { diff --git a/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperationWithProgress{TResult, TProgress}.cs b/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperationWithProgress{TResult, TProgress}.cs index 25fe3cfb8..99c857573 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperationWithProgress{TResult, TProgress}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperationWithProgress{TResult, TProgress}.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -20,6 +21,7 @@ namespace Windows.Foundation; /// /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("B5D036D7-E297-498F-BA60-0289E76E23DD")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IAsyncOperationWithProgress : IAsyncInfo { diff --git a/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperation{TResult}.cs b/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperation{TResult}.cs index 104f4e6e9..a6ec54b5f 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperation{TResult}.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/IAsyncOperation{TResult}.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Runtime.InteropServices; using Windows.Foundation.Metadata; using WindowsRuntime; @@ -18,6 +19,7 @@ namespace Windows.Foundation; /// /// [WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")] +[Guid("9FC2B0BB-E446-44E2-AA61-9CAB8F636AF2")] [ContractVersion(typeof(FoundationContract), 65536u)] public interface IAsyncOperation : IAsyncInfo { From fe460463da629eefeea1faf680d0a417d993b2df Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 13 Dec 2025 11:24:30 -0800 Subject: [PATCH 06/38] Add method to enumerate all implemented interfaces Introduces EnumerateAllInterfaces to TypeSignatureExtensions, allowing traversal of all interfaces implemented by a type, including those from base types. This method handles generic types and ensures robust traversal even with incomplete type hierarchies. --- .../Extensions/TypeSignatureExtensions.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs index be76c72f1..fafd04cc3 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Metadata.Tables; namespace WindowsRuntime.InteropGenerator; @@ -43,5 +45,64 @@ public bool IsFullyResolvable([NotNullWhen(true)] out TypeDefinition? definition return true; } + + /// + /// Enumerates all interface types implementation by the specified type, including those implemented by base types. + /// + /// The sequence of interface types implemented by the input type. + /// + /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy. + /// + public IEnumerable EnumerateAllInterfaces() + { + TypeSignature currentSignature = signature; + + while (currentSignature is not null) + { + // If we can't resolve the current type signature, we have to stop. + // Callers should validate the type hierarchy before calling this. + if (!currentSignature.IsFullyResolvable(out TypeDefinition? currentDefinition)) + { + yield break; + } + + GenericContext context = new(currentSignature as GenericInstanceTypeSignature, null); + + // Go over all interfaces implemented on the current type. We don't need + // to recurse on them, as classes always declare the full transitive set. + foreach (InterfaceImplementation interfaceImplementation in currentDefinition.Interfaces) + { + // Ignore this interface if we can't actually retrieve the interface type. + // This should never happen for valid .NET assemblies, but just in case. + if (interfaceImplementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature) + { + continue; + } + + // Return either the current non-generic interface, or the constructed generic one. + // We don't have to check: if the interface is not generic, this will be a no-op. + yield return interfaceSignature.InstantiateGenericTypes(context); + + // Also recurse on the base interfaces + foreach (TypeSignature baseInterface in interfaceSignature.EnumerateAllInterfaces()) + { + yield return baseInterface.InstantiateGenericTypes(context); + } + } + + ITypeDefOrRef? baseType = currentDefinition.BaseType; + + // Stop if we have no available base type or if we reached 'object' + if (baseType is null or CorLibTypeSignature { ElementType: ElementType.Object }) + { + yield break; + } + + // Get the signature for the base type, adding back any generic context. + // Note that the base type will always be a reference type, even for + // struct types (in that case, the base type will be 'System.ValueType'). + currentSignature = baseType.ToReferenceTypeSignature().InstantiateGenericTypes(context); + } + } } } \ No newline at end of file From 551a176d72a8a4e5317f8dfb834d2d4ec8736ef5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 13 Dec 2025 11:37:52 -0800 Subject: [PATCH 07/38] Avoid redundant instantiation of base interfaces Updated the logic to yield base interfaces directly without re-instantiating their generic types, as they are already instantiated when returned. This prevents unnecessary operations and improves code clarity. --- .../Extensions/TypeSignatureExtensions.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs index fafd04cc3..cc0a7fbcc 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs @@ -83,10 +83,11 @@ public IEnumerable EnumerateAllInterfaces() // We don't have to check: if the interface is not generic, this will be a no-op. yield return interfaceSignature.InstantiateGenericTypes(context); - // Also recurse on the base interfaces + // Also recurse on the base interfaces (no need to instantiate the returned interface type + // signatures for base interfaces here: they will be already instantiate when returned). foreach (TypeSignature baseInterface in interfaceSignature.EnumerateAllInterfaces()) { - yield return baseInterface.InstantiateGenericTypes(context); + yield return baseInterface; } } From c01c04924beb6ac97e303d9bc7670100e0ff9572 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:12:10 -0800 Subject: [PATCH 08/38] Track type definition in IObservableVector1 builder Added a call to emitState.TrackTypeDefinition for implType and vectorType in the InteropTypeDefinitionBuilder.IObservableVector1 implementation. This ensures the type is tracked, which may be required for COM interface entries involving user-defined types. --- .../InteropTypeDefinitionBuilder.IObservableVector1.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs index 215b6a7cc..d70160642 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs @@ -486,6 +486,9 @@ public static void ImplType( implType.Methods.Add(makeVectorChangedMethod); implType.Methods.Add(get_VectorChangedTableMethod); implType.Properties.Add(vectorChangedTableProperty); + + // Track the type (it may be needed by COM interface entries for user-defined types) + emitState.TrackTypeDefinition(implType, vectorType, "Impl"); } /// From eecb4b0e071766fb42f74e04b8b176d4f7d3696b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:47:11 -0800 Subject: [PATCH 09/38] Refactor and clarify interface resolution warnings Renamed and updated warning methods in WellKnownInteropExceptions for improved clarity regarding interface and base type resolution failures. Refactored InteropGenerator.Discover.cs to use the new warning methods, streamline interface discovery, and ensure correct handling of generic interface instantiations. --- .../Errors/WellKnownInteropExceptions.cs | 20 ++----- .../Generation/InteropGenerator.Discover.cs | 56 +++++++++++-------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs index 42357863e..50d273efe 100644 --- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs +++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs @@ -430,11 +430,11 @@ public static WellKnownInteropException CustomMappedTypeComWrappersMarshallerAtt } /// - /// Failed to resolve a '[GeneratedComInterface]' type. + /// Failed to resolve the type of an implemented interface. /// - public static WellKnownInteropWarning GeneratedComInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) + public static WellKnownInteropWarning InterfaceImplementationTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) { - return Warning(49, $"Failed to resolve the '[GeneratedComInterface]' type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); + return Warning(49, $"Failed to resolve interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); } /// @@ -534,11 +534,11 @@ public static WellKnownInteropWarning GeneratedComInterfaceGuidAttributeNotFound } /// - /// Failed to resolve a Windows Runtime interface type. + /// Failed to resolve a base type for a user-defined type. /// - public static WellKnownInteropWarning WindowsRuntimeInterfaceTypeNotResolvedWarning(TypeSignature interfaceType, TypeDefinition type) + public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type) { - return Warning(62, $"Failed to resolve the Windows Runtime interface type '{interfaceType}' while processing type '{type}': the interface will not be included in the set of available COM interface entries."); + return Warning(62, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated."); } /// @@ -573,14 +573,6 @@ public static WellKnownInteropWarning WindowsRuntimeClassTypeNotResolvedWarning( return Warning(66, $"Failed to resolve the base type '{baseType}' for Windows Runtime class type '{classType}': runtime casts to the base type will not work if the type is trimmed."); } - /// - /// Failed to resolve a base type for a user-defined type. - /// - public static WellKnownInteropWarning UserDefinedTypeNotFullyResolvedWarning(ITypeDefOrRef baseType, TypeDefinition type) - { - return Warning(67, $"Failed to resolve the base type '{baseType}' in the type hierarchy for user-defined type '{type}': marshalling code for it will not be generated."); - } - /// /// Creates a new exception with the specified id and message. /// diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index a417b9d33..6cc499d42 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -249,41 +249,42 @@ private static void DiscoverExposedUserDefinedTypes( bool hasAnyProjectedWindowsRuntimeInterfaces = false; // Gather all implemented Windows Runtime interfaces for the current type - foreach (InterfaceImplementation implementation in type.EnumerateAllInterfaces()) + foreach (TypeSignature interfaceSignature in type.ToTypeSignature().EnumerateAllInterfaces()) { - // If the current implementation has no valid interface, skip it. - // This should never really happen for valid .NET assemblies. - if (implementation.Interface?.ToReferenceTypeSignature() is not TypeSignature interfaceSignature) + // Make sure we can resolve the interface type fully, which we should always be able to do. + // This can really only fail for some constructed generics, for invalid type arguments. + if (!interfaceSignature.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) { + WellKnownInteropExceptions.InterfaceImplementationTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); + continue; } // Check for projected Windows Runtime interfaces first if (interfaceSignature.IsWindowsRuntimeType(interopReferences)) { - // Make sure we can resolve the interface type fully, which we should always be able to do. - // This can really only fail for some constructed generics, for invalid type arguments. - if (!interfaceSignature.IsFullyResolvable(out _)) - { - WellKnownInteropExceptions.WindowsRuntimeInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - hasAnyProjectedWindowsRuntimeInterfaces = true; interfaces.Add(interfaceSignature); - } - else if (implementation.Interface.IsGeneratedComInterfaceType) - { - // To properly track '[GeneratedComInterface]' implementations, we need to be able to resolve those interface types - if (!implementation.Interface.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) - { - WellKnownInteropExceptions.GeneratedComInterfaceTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); - continue; + // If the current interface is generic, also make sure that it's tracked. This is needed + // to fully cover all possible constructed generic interface types that might be needed. + // For instance, consider this case: + // + // class A : IEnumerable; + // class B : A; + // + // While processing 'B', we'll discover the constructed 'IEnumerable' interface. + // This interface would not have been discovered when processing 'A', as it's not + // in the 'TypeSpec' metadata table, and only appears as unconstructed on 'A'. + // So the discovery logic for generic instantiations below would otherwise miss it. + if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) + { + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); } - + } + else if (interfaceDefinition.IsGeneratedComInterfaceType) + { // We can only gather this type if we can find the generated 'InterfaceInformation' type. // If we can't find it, we can't add the interface to the list of interface entries. We // should warn if that's the (unlikely) case, so users can at least know that something @@ -416,8 +417,15 @@ private static void DiscoverGenericTypeInstantiations( discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); // We also want to crawl base interfaces - foreach (GenericInstanceTypeSignature interfaceSignature in typeDefinition.EnumerateGenericInstanceInterfaceSignatures(typeSignature)) + foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) { + // Filter out just constructed generic interfaces, since we only care about those here. + // The non-generic ones are only useful when gathering interfaces for user-defined types. + if (interfaceSignature is not GenericInstanceTypeSignature constructedSignature) + { + continue; + } + if (!interfaceSignature.IsFullyResolvable(out _)) { // Also log a warning the first time we fail to resolve one of the recursively discovered generic @@ -432,7 +440,7 @@ private static void DiscoverGenericTypeInstantiations( continue; } - discoveryState.TrackGenericInterfaceType(interfaceSignature, interopReferences); + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); } continue; From a4b4c97e166e4e7a49548c863176b3b798e00e00 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 14 Dec 2025 12:47:17 -0800 Subject: [PATCH 10/38] Remove EnumerateAllInterfaces method from TypeDefinitionExtensions Deleted the EnumerateAllInterfaces method, which enumerated all interface implementations for a type and its base types. This cleanup may be due to redundancy, lack of use, or a refactor in interface enumeration logic. --- .../Extensions/TypeDefinitionExtensions.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs index 515518250..d03c841f6 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs @@ -241,24 +241,6 @@ public IEnumerable EnumerateBaseTypesAndSelf() } } - /// - /// Enumerates all interface types implementation by the specified type, including those implemented by base types. - /// - /// The sequence of interface types implemented by the input type. - /// - /// This method might return the same interface types multiple times, if implemented by multiple types in the hierarchy. - /// - public IEnumerable EnumerateAllInterfaces() - { - foreach (TypeDefinition currentType in type.EnumerateBaseTypesAndSelf()) - { - foreach (InterfaceImplementation implementation in currentType.Interfaces) - { - yield return implementation; - } - } - } - /// /// Enumerates all generic instance type signatures for base interfaces, from a given starting interface. /// From f7640f11ed7ec0ea6689c270cf55dbe398cf9bf8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 12:13:10 -0800 Subject: [PATCH 11/38] Refactor base type checks in type hierarchy utilities Introduced HasBaseType extension to encapsulate base type checks and refactored related logic in TypeDefinitionExtensions and InteropGenerator.Discover. This improves code clarity and reduces duplication when handling type hierarchies and base type resolution. --- .../Extensions/TypeDefinitionExtensions.cs | 29 ++++++++++++------- .../Generation/InteropGenerator.Discover.cs | 6 ++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs index d03c841f6..e831a8e68 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeDefinitionExtensions.cs @@ -25,6 +25,17 @@ internal static class TypeDefinitionExtensions /// public bool IsStatic => type.IsAbstract && type.IsSealed; + /// + /// Gets whether a given type has a base type other than . + /// + /// The resulting base type, if available. + public bool HasBaseType([NotNullWhen(true)] out ITypeDefOrRef? baseType) + { + baseType = type.BaseType; + + return baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object }); + } + /// /// Gets a value indicating whether a given 's type hierarchy can be fully resolved to type definitions. /// @@ -32,18 +43,16 @@ internal static class TypeDefinitionExtensions /// Whether the input 's type hierarchy can be fully resolved to type definitions. public bool IsTypeHierarchyFullyResolvable([NotNullWhen(false)] out ITypeDefOrRef? failedResolutionBaseType) { - ITypeDefOrRef? baseType = type.BaseType; + TypeDefinition currentDefinition = type; - while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object })) + while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType)) { - if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) + if (!baseType.IsFullyResolvable(out currentDefinition!)) { failedResolutionBaseType = baseType; return false; } - - baseType = baseDefinition.BaseType; } failedResolutionBaseType = null; @@ -224,20 +233,18 @@ public IEnumerable EnumerateBaseTypesAndSelf() { yield return type; - ITypeDefOrRef? baseType = type.BaseType; + TypeDefinition currentDefinition = type; - while (baseType is not (null or CorLibTypeSignature { ElementType: ElementType.Object })) + while (currentDefinition.HasBaseType(out ITypeDefOrRef? baseType)) { // If we can't resolve the current base type, we have to stop. // Callers should validate the type hierarchy before calling this. - if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) + if (!baseType.IsFullyResolvable(out currentDefinition!)) { yield break; } - yield return baseDefinition; - - baseType = baseDefinition.BaseType; + yield return currentDefinition; } } diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 6cc499d42..ec6c09145 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -162,15 +162,15 @@ private static void DiscoverTypeHierarchyTypes( } // Ignore types that don't have another base class - if (type.BaseType is null || SignatureComparer.IgnoreVersion.Equals(type.BaseType, module.CorLibTypeFactory.Object)) + if (!type.HasBaseType(out ITypeDefOrRef? baseType)) { continue; } // We need to resolve the base type to be able to look up attributes on it - if (!type.BaseType.IsFullyResolvable(out TypeDefinition? baseType)) + if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) { - WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(type.BaseType, type).LogOrThrow(args.TreatWarningsAsErrors); + WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, type).LogOrThrow(args.TreatWarningsAsErrors); continue; } From 3ddd63d4080387a8bdc497b63becb2106375c348 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 12:29:30 -0800 Subject: [PATCH 12/38] Refactor user-defined type discovery logic Moved the logic for tracking exposed user-defined types from InteropGenerator.Discover.cs into a new InteropTypeDiscovery helper class. This improves code organization and reusability by encapsulating the discovery process in a dedicated static method. --- .../Discovery/InteropTypeDiscovery.cs | 156 ++++++++++++++++++ .../Generation/InteropGenerator.Discover.cs | 115 +------------ 2 files changed, 164 insertions(+), 107 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs new file mode 100644 index 000000000..b31da198e --- /dev/null +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.Models; +using WindowsRuntime.InteropGenerator.References; + +namespace WindowsRuntime.InteropGenerator.Discovery; + +/// +/// A discovery helper type for interop types. +/// +internal static partial class InteropTypeDiscovery +{ + /// + /// A thread-local instance that can be reused by discovery logic. + /// + [ThreadStatic] + private static TypeSignatureEquatableSet.Builder? TypeSignatures; + + /// + /// Tries to track an exposed user-defined type. + /// + /// The for the type to analyze. + /// The for the type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// + /// This method expects to either be non-generic, or + /// to have be a fully constructed signature for it. + /// + public static bool TryTrackExposedUserDefinedType( + TypeDefinition typeDefinition, + TypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences) + { + // We can skip all projected Windows Runtime types early, as they don't need CCW support + if (typeDefinition.IsProjectedWindowsRuntimeType) + { + return false; + } + + // We'll need to look up attributes and enumerate interfaces across the entire type + // hierarchy for this type, so make sure that we can resolve all types from it first. + if (!typeDefinition.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType)) + { + WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + return false; + } + + // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime + if (!typeDefinition.IsPossiblyWindowsRuntimeExposedType || typeDefinition.IsWindowsRuntimeManagedOnlyType(interopReferences)) + { + return false; + } + + // Reuse the thread-local builder to track all implemented interfaces for the current type + TypeSignatureEquatableSet.Builder interfaces = TypeSignatures ??= new TypeSignatureEquatableSet.Builder(); + + // Since we're reusing the builder for all types, make sure to clear it first + interfaces.Clear(); + + // We want to explicitly track whether the type implements any projected Windows Runtime + // interfaces, as we are only interested in such types. We want to also gather all + // implemented '[GeneratedComInterface]' interfaces, but if a type only implements + // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly. + bool hasAnyProjectedWindowsRuntimeInterfaces = false; + + // Gather all implemented Windows Runtime interfaces for the current type + foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) + { + // Make sure we can resolve the interface type fully, which we should always be able to do. + // This can really only fail for some constructed generics, for invalid type arguments. + if (!interfaceSignature.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) + { + WellKnownInteropExceptions.InterfaceImplementationTypeNotResolvedWarning(interfaceSignature, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Check for projected Windows Runtime interfaces first + if (interfaceSignature.IsWindowsRuntimeType(interopReferences)) + { + hasAnyProjectedWindowsRuntimeInterfaces = true; + + interfaces.Add(interfaceSignature); + + // If the current interface is generic, also make sure that it's tracked. This is needed + // to fully cover all possible constructed generic interface types that might be needed. + // For instance, consider this case: + // + // class A : IEnumerable; + // class B : A; + // + // While processing 'B', we'll discover the constructed 'IEnumerable' interface. + // This interface would not have been discovered when processing 'A', as it's not + // in the 'TypeSpec' metadata table, and only appears as unconstructed on 'A'. + // So the discovery logic for generic instantiations below would otherwise miss it. + if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) + { + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + } + } + else if (interfaceDefinition.IsGeneratedComInterfaceType) + { + // We can only gather this type if we can find the generated 'InterfaceInformation' type. + // If we can't find it, we can't add the interface to the list of interface entries. We + // should warn if that's the (unlikely) case, so users can at least know that something + // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures. + if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _)) + { + WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time + // so we can check against some specific IID which might affect how we construct the COM + // interface entries. For instance, we need to check whether 'IMarshal' is implemented. + if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid)) + { + WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + continue; + } + + // Validate that the current interface isn't trying to implement a reserved interface. + // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'. + if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName)) + { + throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, typeDefinition, iid, interfaceName); + } + + // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below) + interfaces.Add(interfaceSignature); + } + } + + // If the user-defined type implements at least a Windows Runtime interface, then it's considered exposed. + // We don't want to handle marshalling code for types with only '[GeneratedComInterface]' interfaces. + if (hasAnyProjectedWindowsRuntimeInterfaces) + { + discoveryState.TrackUserDefinedType(typeSignature, interfaces.ToEquatableSet()); + } + + return true; + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index ec6c09145..0a089f8c8 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Discovery; using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Models; using WindowsRuntime.InteropGenerator.References; @@ -218,113 +219,13 @@ private static void DiscoverExposedUserDefinedTypes( continue; } - // We can skip all projected Windows Runtime types early, as they don't need CCW support - if (type.IsProjectedWindowsRuntimeType) - { - continue; - } - - // We'll need to look up attributes and enumerate interfaces across the entire type - // hierarchy for this type, so make sure that we can resolve all types from it first. - if (!type.IsTypeHierarchyFullyResolvable(out ITypeDefOrRef? failedResolutionBaseType)) - { - WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime - if (!type.IsPossiblyWindowsRuntimeExposedType || type.IsWindowsRuntimeManagedOnlyType(interopReferences)) - { - continue; - } - - // Since we're reusing the builder for all types, make sure to clear it first - interfaces.Clear(); - - // We want to explicitly track whether the type implements any projected Windows Runtime - // interfaces, as we are only interested in such types. We want to also gather all - // implemented '[GeneratedComInterface]' interfaces, but if a type only implements - // those, we will ignore it. Such types should be marshalled via 'ComWrappers' directly. - bool hasAnyProjectedWindowsRuntimeInterfaces = false; - - // Gather all implemented Windows Runtime interfaces for the current type - foreach (TypeSignature interfaceSignature in type.ToTypeSignature().EnumerateAllInterfaces()) - { - // Make sure we can resolve the interface type fully, which we should always be able to do. - // This can really only fail for some constructed generics, for invalid type arguments. - if (!interfaceSignature.IsFullyResolvable(out TypeDefinition? interfaceDefinition)) - { - WellKnownInteropExceptions.InterfaceImplementationTypeNotResolvedWarning(interfaceSignature, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // Check for projected Windows Runtime interfaces first - if (interfaceSignature.IsWindowsRuntimeType(interopReferences)) - { - hasAnyProjectedWindowsRuntimeInterfaces = true; - - interfaces.Add(interfaceSignature); - - // If the current interface is generic, also make sure that it's tracked. This is needed - // to fully cover all possible constructed generic interface types that might be needed. - // For instance, consider this case: - // - // class A : IEnumerable; - // class B : A; - // - // While processing 'B', we'll discover the constructed 'IEnumerable' interface. - // This interface would not have been discovered when processing 'A', as it's not - // in the 'TypeSpec' metadata table, and only appears as unconstructed on 'A'. - // So the discovery logic for generic instantiations below would otherwise miss it. - if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) - { - discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); - } - } - else if (interfaceDefinition.IsGeneratedComInterfaceType) - { - // We can only gather this type if we can find the generated 'InterfaceInformation' type. - // If we can't find it, we can't add the interface to the list of interface entries. We - // should warn if that's the (unlikely) case, so users can at least know that something - // is wrong. Otherwise we'd just silently ignore these types, resulting in runtime failures. - if (!interfaceDefinition.TryGetInterfaceInformationType(interopReferences, out _)) - { - WellKnownInteropExceptions.GeneratedComInterfaceImplementationTypeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // Ensure we can get the '[GuidAttribute]' from the interface. We need this at compile time - // so we can check against some specific IID which might affect how we construct the COM - // interface entries. For instance, we need to check whether 'IMarshal' is implemented. - if (!interfaceDefinition.TryGetGuidAttribute(interopReferences, out Guid iid)) - { - WellKnownInteropExceptions.GeneratedComInterfaceGuidAttributeNotFoundWarning(interfaceDefinition, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // Validate that the current interface isn't trying to implement a reserved interface. - // For instance, it's not allowed to try to explicitly implement 'IUnknown' or 'IInspectable'. - if (WellKnownInterfaceIIDs.ReservedIIDsMap.TryGetValue(iid, out string? interfaceName)) - { - throw WellKnownInteropExceptions.GeneratedComInterfaceReservedGuidError(interfaceDefinition, type, iid, interfaceName); - } - - // Also track all '[GeneratedComInterface]' interfaces too, and filter them later (below) - interfaces.Add(interfaceSignature); - } - } - - // If the user-defined type doesn't implement any Windows Runtime interfaces, it's not considered exposed - if (!hasAnyProjectedWindowsRuntimeInterfaces) - { - continue; - } - - discoveryState.TrackUserDefinedType(type.ToTypeSignature(), interfaces.ToEquatableSet()); + // Track the type (if it's not applicable, we just ignore it) + _ = InteropTypeDiscovery.TryTrackExposedUserDefinedType( + typeDefinition: type, + typeSignature: type.ToTypeSignature(), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences); } } catch (Exception e) From acf29066e85eb62361161aa6ee09bf39609890e7 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 13:39:22 -0800 Subject: [PATCH 13/38] Refactor generic type tracking into separate file Moved logic for tracking constructed generic types from InteropGenerator.Discover.cs into a new InteropTypeDiscovery.Generics.cs file. This refactoring centralizes and organizes generic type discovery, improving maintainability and separation of concerns. --- .../InteropTypeDiscovery.Generics.cs | 177 ++++++++++++++++++ .../Discovery/InteropTypeDiscovery.cs | 1 + .../Generation/InteropGenerator.Discover.cs | 93 +-------- 3 files changed, 185 insertions(+), 86 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs new file mode 100644 index 000000000..75211a7b9 --- /dev/null +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Signatures; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; + +namespace WindowsRuntime.InteropGenerator.Discovery; + +/// +internal partial class InteropTypeDiscovery +{ + /// + /// Tries to track a constructed generic type. + /// + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + /// Whether the input type was actually tracked, or ignored. + public static bool TryTrackGenericTypeInstance( + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Ignore types that are not fully resolvable (this likely means a .dll is missing) + if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition)) + { + // Log a warning the first time we fail to resolve this generic instantiation in this module + if (discoveryState.TrackFailedResolutionType(typeSignature, module)) + { + WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + return false; + } + + // If the current type signature represents a Windows Runtime type, track it + if (typeSignature.IsWindowsRuntimeType(interopReferences)) + { + return TryTrackWindowsRuntimeGenericTypeInstance( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences, + module); + } + + // Otherwise, try to track information for some constructed managed type + return TryTrackManagedGenericTypeInstance( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences); + } + + /// + /// Tries to track a constructed generic Windows Runtime type. + /// + /// The for the type to analyze. + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + /// Whether the input type was actually tracked, or ignored. + private static bool TryTrackWindowsRuntimeGenericTypeInstance( + TypeDefinition typeDefinition, + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Gather all 'KeyValuePair<,>' instances + if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences)) + { + discoveryState.TrackKeyValuePairType(typeSignature); + + return true; + } + + // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus + // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler'). + // The filtering is already done above, so here we can rely the type will be of one of those kinds. + if (typeDefinition.IsDelegate) + { + discoveryState.TrackGenericDelegateType(typeSignature); + + return true; + } + + // Track all projected Windows Runtime generic interfaces + if (typeDefinition.IsInterface) + { + discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); + + // We also want to crawl base interfaces + foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) + { + // Filter out just constructed generic interfaces, since we only care about those here. + // The non-generic ones are only useful when gathering interfaces for user-defined types. + if (interfaceSignature is not GenericInstanceTypeSignature constructedSignature) + { + continue; + } + + if (!interfaceSignature.IsFullyResolvable(out _)) + { + // Also log a warning the first time we fail to resolve one of the recursively discovered generic + // instantiations from this module. The enumeration also yields back interfaces that couldn't be + // resolved, as that step is performed after yielding. This is done so we can have our own logic + // to log warnings or throw errors from here while we're processing interfaces in this module. + if (discoveryState.TrackFailedResolutionType(interfaceSignature, module)) + { + WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + continue; + } + + discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + } + + return true; + } + + // This is technically unreachable, assuming the input type is a Windows Runtime type + return false; + } + + /// + /// Tries to track a constructed generic user-defined type. + /// + /// The for the type to analyze. + /// The for the constructed type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// Whether the input type was actually tracked, or ignored. + private static bool TryTrackManagedGenericTypeInstance( + TypeDefinition typeDefinition, + GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences) + { + // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types. + // This is because "pass-array" and "fill-array" parameters are projected using spans, but + // those projections require the marshalling code produced when discovering SZ array types. + // So if we see any of these spans where the element type is a Windows Runtime type, we + // manually construct an SZ array type for it and add it to the set of tracked array types. + if (typeSignature.IsValueType && + typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) && + typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences)) + { + discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType()); + + return false; + } + + // Otherwise, try to track a constructed user-defined type + return TryTrackExposedUserDefinedType( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences); + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index b31da198e..b884d3e3b 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -30,6 +30,7 @@ internal static partial class InteropTypeDiscovery /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. + /// Whether the input type was actually tracked, or ignored. /// /// This method expects to either be non-generic, or /// to have be a fully constructed signature for it. diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 0a089f8c8..091bea428 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -260,92 +260,13 @@ private static void DiscoverGenericTypeInstantiations( continue; } - // Ignore types that are not fully resolvable (this likely means a .dll is missing) - if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition)) - { - // Log a warning the first time we fail to resolve this generic instantiation in this module - if (discoveryState.TrackFailedResolutionType(typeSignature, module)) - { - WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types. - // This is because "pass-array" and "fill-array" parameters are projected using spans, but - // those projections require the marshalling code produced when discovering SZ array types. - // So if we see any of these spans where the element type is a Windows Runtime type, we - // manually construct an SZ array type for it and add it to the set of tracked array types. - if (typeSignature.IsValueType && - typeSignature.IsConstructedSpanOrReadOnlySpanType(interopReferences) && - typeSignature.TypeArguments[0].IsWindowsRuntimeType(interopReferences)) - { - discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType()); - - continue; - } - - // Ignore generic instantiations that are not Windows Runtime types. That is, those that - // have a generic type definition that's not a Windows Runtime type, or that have any type - // arguments that are not Windows Runtime types. - if (!typeSignature.IsWindowsRuntimeType(interopReferences)) - { - continue; - } - - // Gather all 'KeyValuePair<,>' instances - if (typeSignature.IsValueType && typeSignature.IsConstructedKeyValuePairType(interopReferences)) - { - discoveryState.TrackKeyValuePairType(typeSignature); - - continue; - } - - // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus - // any custom-mapped ones (e.g. 'EventHandler' and 'EventHandler'). - // The filtering is already done above, so here we can rely the type will be of one of those kinds. - if (typeDefinition.IsDelegate) - { - discoveryState.TrackGenericDelegateType(typeSignature); - - continue; - } - - // Track all projected Windows Runtime generic interfaces - if (typeDefinition.IsInterface) - { - discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); - - // We also want to crawl base interfaces - foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) - { - // Filter out just constructed generic interfaces, since we only care about those here. - // The non-generic ones are only useful when gathering interfaces for user-defined types. - if (interfaceSignature is not GenericInstanceTypeSignature constructedSignature) - { - continue; - } - - if (!interfaceSignature.IsFullyResolvable(out _)) - { - // Also log a warning the first time we fail to resolve one of the recursively discovered generic - // instantiations from this module. The enumeration also yields back interfaces that couldn't be - // resolved, as that step is performed after yielding. This is done so we can have our own logic - // to log warnings or throw errors from here while we're processing interfaces in this module. - if (discoveryState.TrackFailedResolutionType(interfaceSignature, module)) - { - WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(interfaceSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); - } - - continue; - } + // Track the constructed generic type (ignore it if not applicable) + _ = InteropTypeDiscovery.TryTrackGenericTypeInstance( + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } catch (Exception e) From 599abe35775e624d7e58e71101682504f790deac Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 14:39:23 -0800 Subject: [PATCH 14/38] Handle newobj and newarr instructions in signature enumeration Extended the EnumerateTypeSignatures method to process 'newobj' and 'newarr' CIL instructions, ensuring that object and array instantiations are included in the signature enumeration. This improves coverage of type signatures encountered in method bodies. --- .../Extensions/ModuleDefinitionExtensions.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs index ac8ffeaac..aeee73acb 100644 --- a/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/ModuleDefinitionExtensions.cs @@ -8,6 +8,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Helpers; using WindowsRuntime.InteropGenerator.Visitors; @@ -230,6 +231,58 @@ static IEnumerable EnumerateTypeSignatures( yield return result; } } + + IReadOnlyList instructions = specification.Method!.Resolve()?.CilMethodBody?.Instructions ?? (IReadOnlyList)[]; + + // Go through instruction to look for new objects + foreach (CilInstruction instruction in instructions) + { + // We only care for 'newobj' instructions + if (instruction.OpCode != CilOpCodes.Newobj) + { + continue; + } + + // Check that we can retrieve the target object type + if (instruction.Operand is not IMethodDefOrRef { DeclaringType: ITypeDefOrRef objectType }) + { + continue; + } + + // Instantiate the object type and enumerate all signatures + foreach (TResult result in EnumerateTypeSignatures( + objectType.ToTypeSignature().InstantiateGenericTypes(genericContext), + results, + visitor)) + { + yield return result; + } + } + + // Go through instruction to look for new arrays + foreach (CilInstruction instruction in instructions) + { + // We only care for 'newarr' instructions + if (instruction.OpCode != CilOpCodes.Newarr) + { + continue; + } + + // Check that we can retrieve the target object type + if (instruction.Operand is not ITypeDefOrRef arrayType) + { + continue; + } + + // Instantiate the object type and enumerate all signatures + foreach (TResult result in EnumerateTypeSignatures( + arrayType.ToTypeSignature().InstantiateGenericTypes(genericContext), + results, + visitor)) + { + yield return result; + } + } } } From cc7704be84448ab3c87d36b4f1c483a9b2d11caf Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 14:45:00 -0800 Subject: [PATCH 15/38] Refactor type tracking logic into helper methods Moved SZ array and type hierarchy tracking logic from InteropGenerator.Discover.cs into dedicated helper methods in InteropTypeDiscovery. This improves code reuse and maintainability by centralizing the logic for type analysis and tracking. --- .../InteropTypeDiscovery.Generics.cs | 40 ++++++++++++++ .../Discovery/InteropTypeDiscovery.cs | 43 +++++++++++++++ .../Generation/InteropGenerator.Discover.cs | 53 +++---------------- 3 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index 75211a7b9..07b460218 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -61,6 +61,46 @@ public static bool TryTrackGenericTypeInstance( interopReferences); } + /// + /// Tries to track an SZ array type. + /// + /// The for the SZ array type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// The instance to use. + /// The module currently being analyzed. + /// Whether the input type was actually tracked, or ignored. + public static bool TryTrackSzArrayType( + SzArrayTypeSignature typeSignature, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Ignore types that are not fully resolvable (this likely means a .dll is missing) + if (!typeSignature.IsFullyResolvable(out _)) + { + // Log a warning the first time we fail to resolve this SZ array in this module + if (discoveryState.TrackFailedResolutionType(typeSignature, module)) + { + WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); + } + + return false; + } + + // Ignore array types that are not Windows Runtime types + if (!typeSignature.IsWindowsRuntimeType(interopReferences)) + { + return false; + } + + // Track all SZ array types, as we'll need to emit marshalling code for them + discoveryState.TrackSzArrayType(typeSignature); + + return true; + } + /// /// Tries to track a constructed generic Windows Runtime type. /// diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index b884d3e3b..5a3d0dcb8 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -22,6 +22,49 @@ internal static partial class InteropTypeDiscovery [ThreadStatic] private static TypeSignatureEquatableSet.Builder? TypeSignatures; + /// + /// Tries to track a given composable Windows Runtime type. + /// + /// The for the type to analyze. + /// The arguments for this invocation. + /// The discovery state for this invocation. + /// Whether the input type was actually tracked, or ignored. + public static bool TryTrackTypeHierarchyType( + TypeDefinition typeDefinition, + InteropGeneratorArgs args, + InteropGeneratorDiscoveryState discoveryState) + { + // We only care about projected Windows Runtime classes + if (!typeDefinition.IsProjectedWindowsRuntimeClassType) + { + return false; + } + + // Ignore types that don't have another base class + if (!typeDefinition.HasBaseType(out ITypeDefOrRef? baseType)) + { + return false; + } + + // We need to resolve the base type to be able to look up attributes on it + if (!baseType.IsFullyResolvable(out _)) + { + WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); + + return false; + } + + // If the base type is also a projected Windows Runtime type, track it + if (baseType.IsProjectedWindowsRuntimeType) + { + discoveryState.TrackTypeHierarchyEntry(typeDefinition.FullName, baseType.FullName); + + return true; + } + + return false; + } + /// /// Tries to track an exposed user-defined type. /// diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 091bea428..0d6059f6b 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -156,31 +156,7 @@ private static void DiscoverTypeHierarchyTypes( { args.Token.ThrowIfCancellationRequested(); - // We only care about projected Windows Runtime classes - if (!type.IsProjectedWindowsRuntimeClassType) - { - continue; - } - - // Ignore types that don't have another base class - if (!type.HasBaseType(out ITypeDefOrRef? baseType)) - { - continue; - } - - // We need to resolve the base type to be able to look up attributes on it - if (!baseType.IsFullyResolvable(out TypeDefinition? baseDefinition)) - { - WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, type).LogOrThrow(args.TreatWarningsAsErrors); - - continue; - } - - // If the base type is also a projected Windows Runtime type, track it - if (baseType.IsProjectedWindowsRuntimeType) - { - discoveryState.TrackTypeHierarchyEntry(type.FullName, baseType.FullName); - } + _ = InteropTypeDiscovery.TryTrackTypeHierarchyType(type, args, discoveryState); } } catch (Exception e) @@ -301,26 +277,13 @@ private static void DiscoverSzArrayTypes( continue; } - // Ignore types that are not fully resolvable (this likely means a .dll is missing) - if (!typeSignature.IsFullyResolvable(out _)) - { - // Log a warning the first time we fail to resolve this SZ array in this module - if (discoveryState.TrackFailedResolutionType(typeSignature, module)) - { - WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); - } - - continue; - } - - // Ignore array types that are not Windows Runtime types - if (!typeSignature.IsWindowsRuntimeType(interopReferences)) - { - continue; - } - - // Track all SZ array types, as we'll need to emit marshalling code for them - discoveryState.TrackSzArrayType(typeSignature); + // Track the SZ array type + _ = InteropTypeDiscovery.TryTrackSzArrayType( + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } catch (Exception e) From 1e45e58ab48f88b36f0f9d262941f28bb31bc82a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 14:47:41 -0800 Subject: [PATCH 16/38] Refactor tracking methods to return void instead of bool Updated InteropTypeDiscovery tracking methods to return void instead of bool, as their return values were not used. Adjusted all call sites and removed related comments and unreachable code for clarity. --- .../InteropTypeDiscovery.Generics.cs | 53 ++++++++----------- .../Discovery/InteropTypeDiscovery.cs | 24 +++------ .../Generation/InteropGenerator.Discover.cs | 14 ++--- 3 files changed, 37 insertions(+), 54 deletions(-) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index 07b460218..ee1774c15 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -20,8 +20,7 @@ internal partial class InteropTypeDiscovery /// The discovery state for this invocation. /// The instance to use. /// The module currently being analyzed. - /// Whether the input type was actually tracked, or ignored. - public static bool TryTrackGenericTypeInstance( + public static void TryTrackGenericTypeInstance( GenericInstanceTypeSignature typeSignature, InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, @@ -37,13 +36,13 @@ public static bool TryTrackGenericTypeInstance( WellKnownInteropExceptions.GenericTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); } - return false; + return; } // If the current type signature represents a Windows Runtime type, track it if (typeSignature.IsWindowsRuntimeType(interopReferences)) { - return TryTrackWindowsRuntimeGenericTypeInstance( + TryTrackWindowsRuntimeGenericTypeInstance( typeDefinition, typeSignature, args, @@ -51,14 +50,16 @@ public static bool TryTrackGenericTypeInstance( interopReferences, module); } - - // Otherwise, try to track information for some constructed managed type - return TryTrackManagedGenericTypeInstance( - typeDefinition, - typeSignature, - args, - discoveryState, - interopReferences); + else + { + // Otherwise, try to track information for some constructed managed type + TryTrackManagedGenericTypeInstance( + typeDefinition, + typeSignature, + args, + discoveryState, + interopReferences); + } } /// @@ -69,8 +70,7 @@ public static bool TryTrackGenericTypeInstance( /// The discovery state for this invocation. /// The instance to use. /// The module currently being analyzed. - /// Whether the input type was actually tracked, or ignored. - public static bool TryTrackSzArrayType( + public static void TryTrackSzArrayType( SzArrayTypeSignature typeSignature, InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, @@ -86,19 +86,17 @@ public static bool TryTrackSzArrayType( WellKnownInteropExceptions.SzArrayTypeSignatureNotResolvedError(typeSignature, module).LogOrThrow(args.TreatWarningsAsErrors); } - return false; + return; } // Ignore array types that are not Windows Runtime types if (!typeSignature.IsWindowsRuntimeType(interopReferences)) { - return false; + return; } // Track all SZ array types, as we'll need to emit marshalling code for them discoveryState.TrackSzArrayType(typeSignature); - - return true; } /// @@ -110,8 +108,7 @@ public static bool TryTrackSzArrayType( /// The discovery state for this invocation. /// The instance to use. /// The module currently being analyzed. - /// Whether the input type was actually tracked, or ignored. - private static bool TryTrackWindowsRuntimeGenericTypeInstance( + private static void TryTrackWindowsRuntimeGenericTypeInstance( TypeDefinition typeDefinition, GenericInstanceTypeSignature typeSignature, InteropGeneratorArgs args, @@ -124,7 +121,7 @@ private static bool TryTrackWindowsRuntimeGenericTypeInstance( { discoveryState.TrackKeyValuePairType(typeSignature); - return true; + return; } // Gather all Windows Runtime delegate types. We want to gather all projected delegate types, plus @@ -134,7 +131,7 @@ private static bool TryTrackWindowsRuntimeGenericTypeInstance( { discoveryState.TrackGenericDelegateType(typeSignature); - return true; + return; } // Track all projected Windows Runtime generic interfaces @@ -168,12 +165,7 @@ private static bool TryTrackWindowsRuntimeGenericTypeInstance( discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); } - - return true; } - - // This is technically unreachable, assuming the input type is a Windows Runtime type - return false; } /// @@ -184,8 +176,7 @@ private static bool TryTrackWindowsRuntimeGenericTypeInstance( /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. - /// Whether the input type was actually tracked, or ignored. - private static bool TryTrackManagedGenericTypeInstance( + private static void TryTrackManagedGenericTypeInstance( TypeDefinition typeDefinition, GenericInstanceTypeSignature typeSignature, InteropGeneratorArgs args, @@ -203,11 +194,11 @@ private static bool TryTrackManagedGenericTypeInstance( { discoveryState.TrackSzArrayType(typeSignature.TypeArguments[0].MakeSzArrayType()); - return false; + return; } // Otherwise, try to track a constructed user-defined type - return TryTrackExposedUserDefinedType( + TryTrackExposedUserDefinedType( typeDefinition, typeSignature, args, diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index 5a3d0dcb8..13005bd03 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -28,8 +28,7 @@ internal static partial class InteropTypeDiscovery /// The for the type to analyze. /// The arguments for this invocation. /// The discovery state for this invocation. - /// Whether the input type was actually tracked, or ignored. - public static bool TryTrackTypeHierarchyType( + public static void TryTrackTypeHierarchyType( TypeDefinition typeDefinition, InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState) @@ -37,13 +36,13 @@ public static bool TryTrackTypeHierarchyType( // We only care about projected Windows Runtime classes if (!typeDefinition.IsProjectedWindowsRuntimeClassType) { - return false; + return; } // Ignore types that don't have another base class if (!typeDefinition.HasBaseType(out ITypeDefOrRef? baseType)) { - return false; + return; } // We need to resolve the base type to be able to look up attributes on it @@ -51,18 +50,14 @@ public static bool TryTrackTypeHierarchyType( { WellKnownInteropExceptions.WindowsRuntimeClassTypeNotResolvedWarning(baseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); - return false; + return; } // If the base type is also a projected Windows Runtime type, track it if (baseType.IsProjectedWindowsRuntimeType) { discoveryState.TrackTypeHierarchyEntry(typeDefinition.FullName, baseType.FullName); - - return true; } - - return false; } /// @@ -73,12 +68,11 @@ public static bool TryTrackTypeHierarchyType( /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. - /// Whether the input type was actually tracked, or ignored. /// /// This method expects to either be non-generic, or /// to have be a fully constructed signature for it. /// - public static bool TryTrackExposedUserDefinedType( + public static void TryTrackExposedUserDefinedType( TypeDefinition typeDefinition, TypeSignature typeSignature, InteropGeneratorArgs args, @@ -88,7 +82,7 @@ public static bool TryTrackExposedUserDefinedType( // We can skip all projected Windows Runtime types early, as they don't need CCW support if (typeDefinition.IsProjectedWindowsRuntimeType) { - return false; + return; } // We'll need to look up attributes and enumerate interfaces across the entire type @@ -97,13 +91,13 @@ public static bool TryTrackExposedUserDefinedType( { WellKnownInteropExceptions.UserDefinedTypeNotFullyResolvedWarning(failedResolutionBaseType, typeDefinition).LogOrThrow(args.TreatWarningsAsErrors); - return false; + return; } // We only want to process non-generic user-defined types that are potentially exposed to Windows Runtime if (!typeDefinition.IsPossiblyWindowsRuntimeExposedType || typeDefinition.IsWindowsRuntimeManagedOnlyType(interopReferences)) { - return false; + return; } // Reuse the thread-local builder to track all implemented interfaces for the current type @@ -194,7 +188,5 @@ public static bool TryTrackExposedUserDefinedType( { discoveryState.TrackUserDefinedType(typeSignature, interfaces.ToEquatableSet()); } - - return true; } } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 0d6059f6b..7a58c4ed2 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -156,7 +156,7 @@ private static void DiscoverTypeHierarchyTypes( { args.Token.ThrowIfCancellationRequested(); - _ = InteropTypeDiscovery.TryTrackTypeHierarchyType(type, args, discoveryState); + InteropTypeDiscovery.TryTrackTypeHierarchyType(type, args, discoveryState); } } catch (Exception e) @@ -195,8 +195,8 @@ private static void DiscoverExposedUserDefinedTypes( continue; } - // Track the type (if it's not applicable, we just ignore it) - _ = InteropTypeDiscovery.TryTrackExposedUserDefinedType( + // Track the type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackExposedUserDefinedType( typeDefinition: type, typeSignature: type.ToTypeSignature(), args: args, @@ -236,8 +236,8 @@ private static void DiscoverGenericTypeInstantiations( continue; } - // Track the constructed generic type (ignore it if not applicable) - _ = InteropTypeDiscovery.TryTrackGenericTypeInstance( + // Track the constructed generic type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackGenericTypeInstance( typeSignature: typeSignature, args: args, discoveryState: discoveryState, @@ -277,8 +277,8 @@ private static void DiscoverSzArrayTypes( continue; } - // Track the SZ array type - _ = InteropTypeDiscovery.TryTrackSzArrayType( + // Track the SZ array type (if it's not applicable, it will be a no-op) + InteropTypeDiscovery.TryTrackSzArrayType( typeSignature: typeSignature, args: args, discoveryState: discoveryState, From 90877658220854f443530c3e4b0f55db13580067 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 15:32:18 -0800 Subject: [PATCH 17/38] Filter unconstructed generic types during discovery Added checks to skip unconstructed generic type definitions during interop type discovery and generation. This ensures only constructed generic types are considered for marshalling code, improving accuracy and avoiding unnecessary processing of generic definitions. --- .../Discovery/InteropTypeDiscovery.Generics.cs | 15 +++++++++++++++ .../Discovery/InteropTypeDiscovery.cs | 7 +++++++ .../Generation/InteropGenerator.Discover.cs | 8 -------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index ee1774c15..ac66c8781 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -6,6 +6,7 @@ using WindowsRuntime.InteropGenerator.Errors; using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.References; +using WindowsRuntime.InteropGenerator.Visitors; namespace WindowsRuntime.InteropGenerator.Discovery; @@ -27,6 +28,13 @@ public static void TryTrackGenericTypeInstance( InteropReferences interopReferences, ModuleDefinition module) { + // Filter all constructed generic type signatures we have. We don't care about generic type + // definitions (eg. 'TypedEventHandler`1') for the purposes of marshalling code. + if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) + { + return; + } + // Ignore types that are not fully resolvable (this likely means a .dll is missing) if (!typeSignature.IsFullyResolvable(out TypeDefinition? typeDefinition)) { @@ -77,6 +85,13 @@ public static void TryTrackSzArrayType( InteropReferences interopReferences, ModuleDefinition module) { + // Filter all constructed generic type signatures we have. We don't care about + // generic type definitions (eg. '!0[]') for the purposes of marshalling code. + if (!typeSignature.AcceptVisitor(IsConstructedGenericTypeVisitor.Instance)) + { + return; + } + // Ignore types that are not fully resolvable (this likely means a .dll is missing) if (!typeSignature.IsFullyResolvable(out _)) { diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index 13005bd03..503393287 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -79,6 +79,13 @@ public static void TryTrackExposedUserDefinedType( InteropGeneratorDiscoveryState discoveryState, InteropReferences interopReferences) { + // Ignore all type definitions with generic parameters where we don't have constructed + // generic type signature. We can track these separately when we see them as instantiated. + if (typeDefinition.HasGenericParameters && typeSignature is not GenericInstanceTypeSignature) + { + return; + } + // We can skip all projected Windows Runtime types early, as they don't need CCW support if (typeDefinition.IsProjectedWindowsRuntimeType) { diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 7a58c4ed2..cd13d6761 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -187,14 +187,6 @@ private static void DiscoverExposedUserDefinedTypes( { args.Token.ThrowIfCancellationRequested(); - // Ignore all type definitions with generic parameters, because they would be - // unconstructed (by definition). We'll process instantiations that we can see - // separately in the discovery phase, same as we do for constructed interfaces. - if (type.HasGenericParameters) - { - continue; - } - // Track the type (if it's not applicable, it will be a no-op) InteropTypeDiscovery.TryTrackExposedUserDefinedType( typeDefinition: type, From 160675365a6a79435b69ee0aabd0305d2ec8f508 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 15:58:43 -0800 Subject: [PATCH 18/38] Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Extensions/TypeSignatureExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs index cc0a7fbcc..ca7b32cd0 100644 --- a/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/TypeSignatureExtensions.cs @@ -47,7 +47,7 @@ public bool IsFullyResolvable([NotNullWhen(true)] out TypeDefinition? definition } /// - /// Enumerates all interface types implementation by the specified type, including those implemented by base types. + /// Enumerates all interface types implemented by the specified type, including those implemented by base types. /// /// The sequence of interface types implemented by the input type. /// @@ -84,7 +84,7 @@ public IEnumerable EnumerateAllInterfaces() yield return interfaceSignature.InstantiateGenericTypes(context); // Also recurse on the base interfaces (no need to instantiate the returned interface type - // signatures for base interfaces here: they will be already instantiate when returned). + // signatures for base interfaces here: they will be already instantiated when returned). foreach (TypeSignature baseInterface in interfaceSignature.EnumerateAllInterfaces()) { yield return baseInterface; From 1e083fbd635cb86f5b0c3249986c0fcbffc0cd26 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 20:21:05 -0800 Subject: [PATCH 19/38] Add tests for generic type activation and interface QI Introduced new test cases to validate activation and interface QueryInterface (QI) for constructed generic types and their derived types. Refactored QI logic into a reusable ComHelpers.EnsureQueryInterface method. Added several generic and derived classes to exercise interface exposure and activation scenarios. --- .../ClassActivation/Program.cs | 173 ++++++++++++++++-- 1 file changed, 161 insertions(+), 12 deletions(-) diff --git a/src/Tests/FunctionalTests/ClassActivation/Program.cs b/src/Tests/FunctionalTests/ClassActivation/Program.cs index 69b6db59a..95ff89205 100644 --- a/src/Tests/FunctionalTests/ClassActivation/Program.cs +++ b/src/Tests/FunctionalTests/ClassActivation/Program.cs @@ -1,8 +1,14 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using System.Runtime.Versioning; +using System.Windows.Input; using TestComponent; using TestComponentCSharp; +using Windows.Foundation.Collections; using WindowsRuntime.InteropServices; CustomDisposableTest customDisposableTest = new(); @@ -38,8 +44,6 @@ { void* testMixedComClassUnknownPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(testMixedComClass); void* classicComActionPtr = null; - void* closablePtr = null; - void* inspectablePtr = null; try { @@ -53,21 +57,61 @@ Marshal.ThrowExceptionForHR(((delegate* unmanaged[MemberFunction])(*(void***)classicComActionPtr)[3])(classicComActionPtr)); // Sanity check: we should still also be able to 'QueryInterface' for other interfaces - Marshal.ThrowExceptionForHR(Marshal.QueryInterface( - pUnk: (nint)testMixedComClassUnknownPtr, - iid: new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), - ppv: out *(nint*)&closablePtr)); - Marshal.ThrowExceptionForHR(Marshal.QueryInterface( - pUnk: (nint)testMixedComClassUnknownPtr, - iid: new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90"), - ppv: out *(nint*)&inspectablePtr)); + ComHelpers.EnsureQueryInterface( + unknownPtr: testMixedComClassUnknownPtr, + iids: [ + new Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E"), + new Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90")]); } finally { WindowsRuntimeMarshal.Free(testMixedComClassUnknownPtr); WindowsRuntimeMarshal.Free(classicComActionPtr); - WindowsRuntimeMarshal.Free(closablePtr); - WindowsRuntimeMarshal.Free(inspectablePtr); + } +} + +ConstructedDerivedType constructedDerivedType = new(); + +unsafe +{ + void* constructedDerivedTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(constructedDerivedType); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: constructedDerivedTypePtr, + iids: [ + new Guid("E2FCC7C1-3BFC-5A0B-B2B0-72E769D1CB7E"), // 'IEnumerable' + new Guid("07F69483-8097-5F92-BB53-068DC81F281A"), // 'IEnumerable' + new Guid("E8A8353A-767E-5C47-97CF-E0336C354311"), // 'IClosable' + new Guid("8311ED02-4F46-5CAF-BF01-2AE354C04BF5")]); // 'IMapChangedEventArgs' + } + finally + { + WindowsRuntimeMarshal.Free(constructedDerivedTypePtr); + } +} + +object genericType = GenericFactory.Make(); + +unsafe +{ + void* genericTypePtr = WindowsRuntimeMarshal.ConvertToUnmanaged(genericType); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: genericTypePtr, + iids: [ + new Guid("30160817-1D7D-54E9-99DB-D7636266A476"), // 'IEnumerable' + new Guid("07F69483-8097-5F92-BB53-068DC81F281A"), // 'IEnumerable' + new Guid("907661AB-C065-5A14-9AC3-2FEB0D164DDA"), // 'IReadOnlyDictionary' + new Guid("3631E370-2F65-5F4A-8364-1619C536DB12"), // 'IEnumerable>' + new Guid("F61E8483-D7A0-5840-9DCF-40423CCC97D0")]); // 'IMapChangedEventArgs' + } + finally + { + WindowsRuntimeMarshal.Free(genericTypePtr); } } @@ -86,6 +130,87 @@ public void Dispose() } } +class GenericBaseType : IEnumerable, IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} + +// This type is extending a generic type. The base type is a 'TypeSpec' with a constructed +// generic type. We use this to validate that the base constructed interfaces are seen. +class ConstructedDerivedType : GenericBaseType, IMapChangedEventArgs +{ + public CollectionChange CollectionChange => throw new NotImplementedException(); + + public IEnumerable Key => throw new NotImplementedException(); +} + +class GenericType : IEnumerable, IReadOnlyDictionary, IMapChangedEventArgs +{ + public T2 this[T1 key] => throw new NotImplementedException(); + + public IEnumerable Keys => throw new NotImplementedException(); + + public IEnumerable Values => throw new NotImplementedException(); + + public int Count => throw new NotImplementedException(); + + public CollectionChange CollectionChange => throw new NotImplementedException(); + + public T2 Key => throw new NotImplementedException(); + + public bool ContainsKey(T1 key) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public bool TryGetValue(T1 key, [MaybeNullWhen(false)] out T2 value) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + throw new NotImplementedException(); + } +} + +class GenericFactory +{ + // This method is caling a generic one, which then constructs a generic type. + // The 'GenericType' instantiation doesn't appear anywhere in the .dll, + // but we should be able to still find it, because: + // - We can see 'Make' in the 'MethodSpec' table. + // - We can inspect all instructions in that constructed method + // - We can see a 'newobj' instruction with a 'MemberRef' to 'GenericType.ctor' + // From there, we should be able to gather info on that constructed generic type. + public static object Make() => Make(); + + private static object Make() => new GenericType(); +} + [Guid("3C832AA5-5F7E-46EE-B1BF-7FE03AE866AF")] [GeneratedComInterface] partial interface IClassicComAction @@ -93,6 +218,30 @@ partial interface IClassicComAction void Invoke(); } +file static class ComHelpers +{ + [SupportedOSPlatform("windows6.3")] + public static unsafe void EnsureQueryInterface(void* unknownPtr, params ReadOnlySpan iids) + { + foreach (Guid iid in iids) + { + void* interfacePtr = null; + + try + { + Marshal.ThrowExceptionForHR(Marshal.QueryInterface( + pUnk: (nint)unknownPtr, + iid: iid, + ppv: out *(nint*)&interfacePtr)); + } + finally + { + WindowsRuntimeMarshal.Free(interfacePtr); + } + } + } +} + /* // new RCW / Factory activation var instance = new Class(); From 54cf7306f6a210e0598fd1b3938e29428dadffc9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 15 Dec 2025 20:47:59 -0800 Subject: [PATCH 20/38] Add tests for AsyncInfo.Run with generic async operations Introduces tests for IAsyncActionWithProgress and IAsyncOperation using AsyncInfo.Run with explicit and transitive type arguments. Ensures correct COM interface querying and memory management for these async operations. --- .../ClassActivation/Program.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/Tests/FunctionalTests/ClassActivation/Program.cs b/src/Tests/FunctionalTests/ClassActivation/Program.cs index 95ff89205..62c7b5ccf 100644 --- a/src/Tests/FunctionalTests/ClassActivation/Program.cs +++ b/src/Tests/FunctionalTests/ClassActivation/Program.cs @@ -5,10 +5,13 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using System.Runtime.Versioning; +using System.Threading.Tasks; using System.Windows.Input; using TestComponent; using TestComponentCSharp; +using Windows.Foundation; using Windows.Foundation.Collections; +using Windows.Foundation.Tasks; using WindowsRuntime.InteropServices; CustomDisposableTest customDisposableTest = new(); @@ -115,6 +118,46 @@ } } +IAsyncActionWithProgress asyncActionWithProgress = GenericFactory.MakeAsyncActionWithProgress(); + +unsafe +{ + void* asyncActionWithProgressPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncActionWithProgress); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: asyncActionWithProgressPtr, + iids: [ + new Guid("0EDE398F-0090-574E-AD30-E152B433BF6A"), // 'IAsyncActionWithProgress' + new Guid("0DB2462F-B6D6-5A6C-8834-B530BAAA45FD")]); // 'IAsyncInfo' + } + finally + { + WindowsRuntimeMarshal.Free(asyncActionWithProgressPtr); + } +} + +IAsyncOperation asyncOperation = GenericFactory.MakeAsyncOperation(); + +unsafe +{ + void* asyncOperationPtr = WindowsRuntimeMarshal.ConvertToUnmanaged(asyncOperation); + + try + { + ComHelpers.EnsureQueryInterface( + unknownPtr: asyncOperationPtr, + iids: [ + new Guid("1AE01209-1ACA-51D3-A080-8B1214E0A39E"), // 'IAsyncOperation' + new Guid("0DB2462F-B6D6-5A6C-8834-B530BAAA45FD")]); // 'IAsyncInfo' + } + finally + { + WindowsRuntimeMarshal.Free(asyncOperationPtr); + } +} + sealed class TestComposable : Composable { } @@ -209,6 +252,26 @@ class GenericFactory public static object Make() => Make(); private static object Make() => new GenericType(); + + // Specific test for 'AsyncInfo.Run' with explicit type arguments + [SupportedOSPlatform("windows10.0.10240.0")] + public static IAsyncActionWithProgress MakeAsyncActionWithProgress() + { + return AsyncInfo.Run((token, progress) => Task.CompletedTask); + } + + // Specific test for 'AsyncInfo.Run' with transitive type arguments + [SupportedOSPlatform("windows10.0.10240.0")] + public static IAsyncOperation MakeAsyncOperation() + { + return MakeAsyncActionOperation(); + } + + [SupportedOSPlatform("windows10.0.10240.0")] + private static IAsyncOperation MakeAsyncActionOperation() + { + return AsyncInfo.Run(token => Task.FromResult(default(T))); + } } [Guid("3C832AA5-5F7E-46EE-B1BF-7FE03AE866AF")] From f4825fc5a9c21174ac77c11b6a0854e8091e2c3b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 16 Dec 2025 12:02:45 -0800 Subject: [PATCH 21/38] Add remarks to Keys and Values properties in dictionary classes Added XML documentation remarks to the Keys and Values properties in WindowsRuntimeDictionary, WindowsRuntimeObservableMap, and WindowsRuntimeReadOnlyDictionary classes to clarify the concrete collection types returned. This improves API documentation and developer understanding of the property return types. --- .../Collections/WindowsRuntimeDictionary{TKey, TValue}.cs | 8 ++++++++ .../WindowsRuntimeObservableMap{TKey, TValue}.cs | 8 ++++++++ .../WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs index 17f4d7cf1..d65e2778f 100644 --- a/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeDictionary{TKey, TValue}.cs @@ -89,15 +89,23 @@ WindowsRuntimeObjectReference InitializeIIterableObjectReference() protected internal sealed override bool HasUnwrappableNativeObjectReference => true; /// + /// + /// The resulting object will be of type . + /// public ICollection Keys => _keys ??= new DictionaryKeyCollection(this); /// + /// IEnumerable IReadOnlyDictionary.Keys => Keys; /// + /// + /// The resulting object will be of type . + /// public ICollection Values => _values ??= new DictionaryValueCollection(this); /// + /// IEnumerable IReadOnlyDictionary.Values => Values; /// diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs index 79c5570ef..48a99ebe1 100644 --- a/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeObservableMap{TKey, TValue}.cs @@ -123,15 +123,23 @@ public event MapChangedEventHandler? MapChanged protected internal sealed override bool HasUnwrappableNativeObjectReference => true; /// + /// + /// The resulting object will be of type . + /// public ICollection Keys => _keys ??= new DictionaryKeyCollection(this); /// + /// IEnumerable IReadOnlyDictionary.Keys => Keys; /// + /// + /// The resulting object will be of type . + /// public ICollection Values => _values ??= new DictionaryValueCollection(this); /// + /// IEnumerable IReadOnlyDictionary.Values => Values; /// diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs index e2e28038e..8bd030590 100644 --- a/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeReadOnlyDictionary{TKey, TValue}.cs @@ -88,9 +88,15 @@ WindowsRuntimeObjectReference InitializeIIterableObjectReference() protected internal sealed override bool HasUnwrappableNativeObjectReference => true; /// + /// + /// The resulting object will be of type . + /// public IEnumerable Keys => _keys ??= new ReadOnlyDictionaryKeyCollection(this); /// + /// + /// The resulting object will be of type . + /// public IEnumerable Values => _values ??= new ReadOnlyDictionaryValueCollection(this); /// From d1904eab4716c50c22adeb377865d2189f500c40 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 16 Dec 2025 13:03:37 -0800 Subject: [PATCH 22/38] Refactor generic interface tracking logic Replaces direct calls to TrackGenericInterfaceType with a new TryTrackWindowsRuntimeGenericInterfaceTypeInstance method. This centralizes and extends the logic for tracking constructed generic Windows Runtime interface types, ensuring all necessary related types and delegates are also tracked for code generation. --- .../InteropTypeDiscovery.Generics.cs | 102 +++++++++++++++++- .../Discovery/InteropTypeDiscovery.cs | 2 +- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index ac66c8781..c171c1190 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -152,7 +152,7 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance( // Track all projected Windows Runtime generic interfaces if (typeDefinition.IsInterface) { - discoveryState.TrackGenericInterfaceType(typeSignature, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance(typeSignature, discoveryState, interopReferences); // We also want to crawl base interfaces foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) @@ -178,11 +178,109 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance( continue; } - discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance(constructedSignature, discoveryState, interopReferences); } } } + /// + /// Tries to track a constructed generic Windows Runtime interface type. + /// + /// The for the constructed type to analyze. + /// The discovery state for this invocation. + /// The instance to use. + private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( + GenericInstanceTypeSignature typeSignature, + InteropGeneratorDiscoveryState discoveryState, + InteropReferences interopReferences) + { + if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerator1)) + { + discoveryState.TrackIEnumerator1Type(typeSignature); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerable1)) + { + discoveryState.TrackIEnumerable1Type(typeSignature); + + // We need special handling for 'IEnumerator' types whenever we discover any constructed 'IEnumerable' + // type. This ensures that we're never missing any 'IEnumerator' instantiation, which we might depend on + // from other generated code, or projections. This special handling is needed because unlike with the other + // interfaces, 'IEnumerator' will not show up as a base interface for other collection interface types. + discoveryState.TrackIEnumerator1Type(interopReferences.IEnumerator1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IList1)) + { + discoveryState.TrackIList1Type(typeSignature); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyList1)) + { + discoveryState.TrackIReadOnlyList1Type(typeSignature); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IDictionary2)) + { + discoveryState.TrackIDictionary2Type(typeSignature); + + // When discovering dictionary types, make sure to also track 'KeyValuePair' types. Those will + // be needed when generating code for 'IEnumerator>' types, which will be discovered + // automatically. However, the same is not true the constructed 'KeyValuePair' types themselves. + // This is for the same reason why we need the other special cases in this method: members are not analyzed. + discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyDictionary2)) + { + discoveryState.TrackIReadOnlyDictionary2Type(typeSignature); + + // Same handling as above for constructed 'KeyValuePair' types + discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableVector1)) + { + discoveryState.TrackIObservableVector1Type(typeSignature); + + // We need special handling for constructed 'VectorChangedEventHandler' types, as those are required for each + // discovered 'IObservableVector' type. These are not necessarily discovered in the same way, as while we are + // recursively constructing interfaces, we don't have the same logic for delegate types (or for types used in + // any signature of interface members). Because we only need this delegate type and the one below, we can just + // special case it. That is, we manually construct it every time we discover a constructed 'IObservableVector'. + discoveryState.TrackGenericDelegateType(interopReferences.VectorChangedEventHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableMap2)) + { + discoveryState.TrackIObservableMap2Type(typeSignature); + + // Same handling as above for 'MapChangedEventHandler' types + discoveryState.TrackGenericDelegateType(interopReferences.MapChangedEventHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IMapChangedEventArgs1)) + { + discoveryState.TrackIMapChangedEventArgs1Type(typeSignature); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncActionWithProgress1)) + { + discoveryState.TrackIAsyncActionWithProgress1Type(typeSignature); + + // Ensure that the delegate types for this instantiation of 'IAsyncActionWithProgress' are also tracked. + // Same rationale as above for the other special cased types. Same below as well for the other async info types. + discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionProgressHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionWithProgressCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperation1)) + { + discoveryState.TrackIAsyncOperation1Type(typeSignature); + + // Same handling as above for 'AsyncOperationCompletedHandler' + discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperationWithProgress2)) + { + discoveryState.TrackIAsyncOperationWithProgress2Type(typeSignature); + + // Same handling as above for 'AsyncOperationProgressHandler' and 'AsyncOperationWithProgressCompletedHandler' + discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationProgressHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationWithProgressCompletedHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + } + } + /// /// Tries to track a constructed generic user-defined type. /// diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index 503393287..a676336e4 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -151,7 +151,7 @@ public static void TryTrackExposedUserDefinedType( // So the discovery logic for generic instantiations below would otherwise miss it. if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) { - discoveryState.TrackGenericInterfaceType(constructedSignature, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance(constructedSignature, discoveryState, interopReferences); } } else if (interfaceDefinition.IsGeneratedComInterfaceType) From e4cc4a94cf4c6170196285ef204d1a7c3c17914b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 16 Dec 2025 13:03:43 -0800 Subject: [PATCH 23/38] Remove InteropGeneratorDiscoveryStateExtensions Deleted the InteropGeneratorDiscoveryStateExtensions.cs file, which contained extension methods for tracking generic interface and delegate types in the interop generator discovery process. This may be part of a refactor or cleanup to simplify or relocate this logic. --- ...nteropGeneratorDiscoveryStateExtensions.cs | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 src/WinRT.Interop.Generator/Extensions/InteropGeneratorDiscoveryStateExtensions.cs diff --git a/src/WinRT.Interop.Generator/Extensions/InteropGeneratorDiscoveryStateExtensions.cs b/src/WinRT.Interop.Generator/Extensions/InteropGeneratorDiscoveryStateExtensions.cs deleted file mode 100644 index eea7e77ed..000000000 --- a/src/WinRT.Interop.Generator/Extensions/InteropGeneratorDiscoveryStateExtensions.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using AsmResolver.DotNet.Signatures; -using WindowsRuntime.InteropGenerator.Generation; -using WindowsRuntime.InteropGenerator.References; - -namespace WindowsRuntime.InteropGenerator; - -/// -/// Extensions for . -/// -internal static class InteropGeneratorDiscoveryStateExtensions -{ - /// - /// Tracks a generic interface type of any projected or custom-mapped type. - /// - /// The current instance. - /// The generic interface type. - /// The instance to use. - public static void TrackGenericInterfaceType( - this InteropGeneratorDiscoveryState discoveryState, - GenericInstanceTypeSignature typeSignature, - InteropReferences interopReferences) - { - if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerator1)) - { - discoveryState.TrackIEnumerator1Type(typeSignature); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerable1)) - { - discoveryState.TrackIEnumerable1Type(typeSignature); - - // We need special handling for 'IEnumerator' types whenever we discover any constructed 'IEnumerable' - // type. This ensures that we're never missing any 'IEnumerator' instantiation, which we might depend on - // from other generated code, or projections. This special handling is needed because unlike with the other - // interfaces, 'IEnumerator' will not show up as a base interface for other collection interface types. - discoveryState.TrackIEnumerator1Type(interopReferences.IEnumerator1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IList1)) - { - discoveryState.TrackIList1Type(typeSignature); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyList1)) - { - discoveryState.TrackIReadOnlyList1Type(typeSignature); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IDictionary2)) - { - discoveryState.TrackIDictionary2Type(typeSignature); - - // When discovering dictionary types, make sure to also track 'KeyValuePair' types. Those will - // be needed when generating code for 'IEnumerator>' types, which will be discovered - // automatically. However, the same is not true the constructed 'KeyValuePair' types themselves. - // This is for the same reason why we need the other special cases in this method: members are not analyzed. - discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyDictionary2)) - { - discoveryState.TrackIReadOnlyDictionary2Type(typeSignature); - - // Same handling as above for constructed 'KeyValuePair' types - discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableVector1)) - { - discoveryState.TrackIObservableVector1Type(typeSignature); - - // We need special handling for constructed 'VectorChangedEventHandler' types, as those are required for each - // discovered 'IObservableVector' type. These are not necessarily discovered in the same way, as while we are - // recursively constructing interfaces, we don't have the same logic for delegate types (or for types used in - // any signature of interface members). Because we only need this delegate type and the one below, we can just - // special case it. That is, we manually construct it every time we discover a constructed 'IObservableVector'. - discoveryState.TrackGenericDelegateType(interopReferences.VectorChangedEventHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableMap2)) - { - discoveryState.TrackIObservableMap2Type(typeSignature); - - // Same handling as above for 'MapChangedEventHandler' types - discoveryState.TrackGenericDelegateType(interopReferences.MapChangedEventHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IMapChangedEventArgs1)) - { - discoveryState.TrackIMapChangedEventArgs1Type(typeSignature); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncActionWithProgress1)) - { - discoveryState.TrackIAsyncActionWithProgress1Type(typeSignature); - - // Ensure that the delegate types for this instantiation of 'IAsyncActionWithProgress' are also tracked. - // Same rationale as above for the other special cased types. Same below as well for the other async info types. - discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionProgressHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - discoveryState.TrackGenericDelegateType(interopReferences.AsyncActionWithProgressCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperation1)) - { - discoveryState.TrackIAsyncOperation1Type(typeSignature); - - discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationCompletedHandler1.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IAsyncOperationWithProgress2)) - { - discoveryState.TrackIAsyncOperationWithProgress2Type(typeSignature); - - discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationProgressHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - discoveryState.TrackGenericDelegateType(interopReferences.AsyncOperationWithProgressCompletedHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); - } - } -} \ No newline at end of file From ff013e4a7a3aae380afc3b8085b8029362d56c6a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 16 Dec 2025 13:31:26 -0800 Subject: [PATCH 24/38] Enhance generic type discovery for dictionary collections Updated generic type discovery logic to explicitly track constructed DictionaryKeyCollection, DictionaryValueCollection, ReadOnlyDictionaryKeyCollection, and ReadOnlyDictionaryValueCollection types when discovering IDictionary and IReadOnlyDictionary instantiations. This ensures correct marshaling of these types, which may not be present in input assemblies but are generated during emit. Also refactored method signatures to consistently pass the current module and args where needed. --- .../InteropTypeDiscovery.Generics.cs | 95 +++++++++++++++---- .../Discovery/InteropTypeDiscovery.cs | 11 ++- .../Generation/InteropGenerator.Discover.cs | 3 +- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index c171c1190..19b21da16 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -51,22 +51,23 @@ public static void TryTrackGenericTypeInstance( if (typeSignature.IsWindowsRuntimeType(interopReferences)) { TryTrackWindowsRuntimeGenericTypeInstance( - typeDefinition, - typeSignature, - args, - discoveryState, - interopReferences, - module); + typeDefinition: typeDefinition, + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } else { // Otherwise, try to track information for some constructed managed type TryTrackManagedGenericTypeInstance( - typeDefinition, - typeSignature, - args, - discoveryState, - interopReferences); + typeDefinition: typeDefinition, + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } @@ -152,7 +153,12 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance( // Track all projected Windows Runtime generic interfaces if (typeDefinition.IsInterface) { - TryTrackWindowsRuntimeGenericInterfaceTypeInstance(typeSignature, discoveryState, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance( + typeSignature: typeSignature, + args: args, + discoveryState, + interopReferences: interopReferences, + module: module); // We also want to crawl base interfaces foreach (TypeSignature interfaceSignature in typeSignature.EnumerateAllInterfaces()) @@ -178,7 +184,12 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance( continue; } - TryTrackWindowsRuntimeGenericInterfaceTypeInstance(constructedSignature, discoveryState, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance( + typeSignature: constructedSignature, + args: args, + discoveryState, + interopReferences: interopReferences, + module: module); } } } @@ -187,12 +198,16 @@ private static void TryTrackWindowsRuntimeGenericTypeInstance( /// Tries to track a constructed generic Windows Runtime interface type. /// /// The for the constructed type to analyze. + /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. + /// The module currently being analyzed. private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( GenericInstanceTypeSignature typeSignature, + InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, - InteropReferences interopReferences) + InteropReferences interopReferences, + ModuleDefinition module) { if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IEnumerator1)) { @@ -225,6 +240,27 @@ private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( // automatically. However, the same is not true the constructed 'KeyValuePair' types themselves. // This is for the same reason why we need the other special cases in this method: members are not analyzed. discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); + + // When we discover a constructed 'IDictionary' instantiation, we'll be generating a native object type during + // the emit phase, which is used to marshal anonymous objects. This derives from 'WindowsRuntimeDictionary'. + // For the 'Keys' and 'Values' properties, that base type will return instances of the 'DictionaryKeyCollection' + // and 'DictionaryValueCollection' types, respectively. Those instantiations will not be seen by 'cswinrtgen', + // because they will only exist in the final 'WinRT.Interop.dll' assembly being generated, and not in any input assemblies. + // So to ensure that we can still correctly marshal those to native, if needed, we manually track them as if we had seen them. + TryTrackGenericTypeInstance( + typeSignature: interopReferences.DictionaryKeyCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); + + // Handle 'DictionaryValueCollection' as well + TryTrackGenericTypeInstance( + typeSignature: interopReferences.DictionaryValueCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IReadOnlyDictionary2)) { @@ -232,6 +268,22 @@ private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( // Same handling as above for constructed 'KeyValuePair' types discoveryState.TrackKeyValuePairType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])); + + // Handle 'ReadOnlyDictionaryKeyCollection' as above + TryTrackGenericTypeInstance( + typeSignature: interopReferences.ReadOnlyDictionaryKeyCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); + + // Handle 'ReadOnlyDictionaryValueCollection' as well + TryTrackGenericTypeInstance( + typeSignature: interopReferences.ReadOnlyDictionaryValueCollection2.MakeGenericReferenceType([.. typeSignature.TypeArguments]), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableVector1)) { @@ -289,12 +341,14 @@ private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. + /// The module currently being analyzed. private static void TryTrackManagedGenericTypeInstance( TypeDefinition typeDefinition, GenericInstanceTypeSignature typeSignature, InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, - InteropReferences interopReferences) + InteropReferences interopReferences, + ModuleDefinition module) { // Check for all '[ReadOnly]Span' types in particular, and track them as SZ array types. // This is because "pass-array" and "fill-array" parameters are projected using spans, but @@ -312,10 +366,11 @@ private static void TryTrackManagedGenericTypeInstance( // Otherwise, try to track a constructed user-defined type TryTrackExposedUserDefinedType( - typeDefinition, - typeSignature, - args, - discoveryState, - interopReferences); + typeDefinition: typeDefinition, + typeSignature: typeSignature, + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs index a676336e4..22d45aae9 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.cs @@ -68,6 +68,7 @@ public static void TryTrackTypeHierarchyType( /// The arguments for this invocation. /// The discovery state for this invocation. /// The instance to use. + /// The module currently being analyzed. /// /// This method expects to either be non-generic, or /// to have be a fully constructed signature for it. @@ -77,7 +78,8 @@ public static void TryTrackExposedUserDefinedType( TypeSignature typeSignature, InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState, - InteropReferences interopReferences) + InteropReferences interopReferences, + ModuleDefinition module) { // Ignore all type definitions with generic parameters where we don't have constructed // generic type signature. We can track these separately when we see them as instantiated. @@ -151,7 +153,12 @@ public static void TryTrackExposedUserDefinedType( // So the discovery logic for generic instantiations below would otherwise miss it. if (interfaceSignature is GenericInstanceTypeSignature constructedSignature) { - TryTrackWindowsRuntimeGenericInterfaceTypeInstance(constructedSignature, discoveryState, interopReferences); + TryTrackWindowsRuntimeGenericInterfaceTypeInstance( + typeSignature: constructedSignature, + args: args, + discoveryState, + interopReferences: interopReferences, + module: module); } } else if (interfaceDefinition.IsGeneratedComInterfaceType) diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index cd13d6761..eaa95e98c 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -193,7 +193,8 @@ private static void DiscoverExposedUserDefinedTypes( typeSignature: type.ToTypeSignature(), args: args, discoveryState: discoveryState, - interopReferences: interopReferences); + interopReferences: interopReferences, + module: module); } } catch (Exception e) From 5d30c2495c3199743f9a455e0d53f01d09245d33 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 14:53:11 -0800 Subject: [PATCH 25/38] Add type mismatch validation for interop method return values Introduces a check to ensure the local variable type matches the expected ABI return type in generated interop methods. Throws a specific exception if a type mismatch is detected, improving error reporting and debugging for interop method generation. --- .../Errors/WellKnownInteropExceptions.cs | 8 ++++++++ .../Factories/InteropMethodRewriteFactory.ReturnValue.cs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs index 50d273efe..8e6374351 100644 --- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs +++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs @@ -573,6 +573,14 @@ public static WellKnownInteropWarning WindowsRuntimeClassTypeNotResolvedWarning( return Warning(66, $"Failed to resolve the base type '{baseType}' for Windows Runtime class type '{classType}': runtime casts to the base type will not work if the type is trimmed."); } + /// + /// A parameter index was not valid in a generated interop method. + /// + public static WellKnownInteropException MethodRewriteSourceLocalTypeMismatchError(TypeSignature localType, TypeSignature returnType, MethodDefinition method) + { + return Exception(67, $"Local variable of type '{localType}' cannot be used to marshal a value of type '{returnType}' in generated interop method '{method}'."); + } + /// /// Creates a new exception with the specified id and message. /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs index 3bac81fce..674e05df2 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs @@ -60,6 +60,12 @@ public static void RewriteMethod( throw WellKnownInteropExceptions.MethodRewriteSourceLocalNotFoundError(source, method); } + // Validate that the ABI type matches + if (!SignatureComparer.IgnoreVersion.Equals(source.VariableType, returnType.GetAbiType(interopReferences))) + { + throw WellKnownInteropExceptions.MethodRewriteSourceLocalTypeMismatchError(source.VariableType, returnType, method); + } + if (returnType.IsValueType) { // If the return type is blittable, we can always return it directly (simplest case) From b6215ec2a5351ff798b62a9e3a60071f48133a03 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 15:24:47 -0800 Subject: [PATCH 26/38] Add type and exception checks to marshaller selection Updated the marshaller type selection logic to include checks for Type and Exception types, in addition to custom-mapped Windows Runtime interfaces and delegates. --- .../Factories/InteropMethodRewriteFactory.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs index 985865d84..39db3a7a5 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs @@ -84,7 +84,9 @@ private static ITypeDefOrRef GetReferenceTypeMarshallerType( } // For custom-mapped types, get the marshaller type from 'WinRT.Runtime.dll' - if (type.IsCustomMappedWindowsRuntimeNonGenericInterfaceType(interopReferences) || + if (type.IsTypeOfType(interopReferences) || + type.IsTypeOfException(interopReferences) || + type.IsCustomMappedWindowsRuntimeNonGenericInterfaceType(interopReferences) || type.IsCustomMappedWindowsRuntimeNonGenericDelegateType(interopReferences)) { return interopReferences.WindowsRuntimeModule.CreateTypeReference( From 5f8dd620392409146cf20daba090503459ca0072 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 15:25:00 -0800 Subject: [PATCH 27/38] Add ManagedParameter marshalling logic Introduces ManagedParameter class to handle marshalling of managed parameters in interop method rewrites. Supports various parameter types including value types, strings, generics, and reference types, with appropriate validation and marshaller method calls. --- .../Errors/WellKnownInteropExceptions.cs | 16 ++ ...opMethodRewriteFactory.ManagedParameter.cs | 155 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs diff --git a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs index 8e6374351..b7a32c0d8 100644 --- a/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs +++ b/src/WinRT.Interop.Generator/Errors/WellKnownInteropExceptions.cs @@ -581,6 +581,22 @@ public static WellKnownInteropException MethodRewriteSourceLocalTypeMismatchErro return Exception(67, $"Local variable of type '{localType}' cannot be used to marshal a value of type '{returnType}' in generated interop method '{method}'."); } + /// + /// The type of a local was not valid in a generated interop method. + /// + public static WellKnownInteropException MethodRewriteParameterIndexNotValidError(int parameterIndex, MethodDefinition method) + { + return Exception(68, $"Parameter index '{parameterIndex}' was not valid for generated interop method '{method}'."); + } + + /// + /// The type of a parameter was not valid in a generated interop method. + /// + public static WellKnownInteropException MethodRewriteSourceParameterTypeMismatchError(TypeSignature parameterType, TypeSignature returnType, MethodDefinition method) + { + return Exception(69, $"Parameter variable of type '{parameterType}' cannot be used to marshal a value of type '{returnType}' in generated interop method '{method}'."); + } + /// /// Creates a new exception with the specified id and message. /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs new file mode 100644 index 000000000..d580241fa --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Collections; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; +using WindowsRuntime.InteropGenerator.Errors; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +internal partial class InteropMethodRewriteFactory +{ + /// + /// Contains the logic for marshalling managed parameters (i.e. parameters that are passed to managed methods). + /// + public static class ManagedParameter + { + /// + /// Performs two-pass code generation on a target method to marshal an unmanaged parameter. + /// + /// 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 index of the parameter to marshal. + /// 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, + int parameterIndex, + 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.Contains(marker)) + { + throw WellKnownInteropExceptions.MethodRewriteMarkerInstructionNotFoundError(marker, method); + } + + // If we didn't find the marker, it means the target method is either invalid, or the + // supplied marker was incorrect (or the caller forgot to add it to the method body). + if ((uint)parameterIndex >= method.Parameters.Count) + { + throw WellKnownInteropExceptions.MethodRewriteParameterIndexNotValidError(parameterIndex, method); + } + + Parameter source = method.Parameters[parameterIndex]; + + // Validate that the ABI type matches + if (!SignatureComparer.IgnoreVersion.Equals(source.ParameterType, parameterType.GetAbiType(interopReferences))) + { + throw WellKnownInteropExceptions.MethodRewriteSourceParameterTypeMismatchError(source.ParameterType, parameterType, method); + } + + if (parameterType.IsValueType) + { + // If the return type is blittable, we can just load it directly it directly (simplest case) + if (parameterType.IsBlittable(interopReferences)) + { + body.Instructions.ReplaceRange(marker, CilInstruction.CreateLdarg(parameterIndex)); + } + else if (parameterType.IsConstructedKeyValuePairType(interopReferences)) + { + // If the type is some constructed 'KeyValuePair<,>' type, we use the generated marshaller + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]); + } + else if (parameterType.IsConstructedNullableValueType(interopReferences)) + { + TypeSignature underlyingType = ((GenericInstanceTypeSignature)parameterType).TypeArguments[0]; + + // For 'Nullable' return types, we need the marshaller for the instantiated 'T' type (same as for return values) + ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(underlyingType, interopReferences, emitState); + + // Get the right reference to the unboxing marshalling method to call + IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef( + name: "UnboxToManaged"u8, + signature: MethodSignature.CreateStatic( + returnType: parameterType, + parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])); + + // Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable' value + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, marshallerMethod)]); + } + else + { + // The last case handles all other value types. It doesn't matter if they possibly hold some unmanaged + // resources, as they're only being used as parameters. That means the caller is responsible for disposal. + ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(parameterType, interopReferences, emitState); + + // Get the reference to 'ConvertToManaged' to produce the resulting value to return + IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef( + name: "ConvertToManaged"u8, + signature: MethodSignature.CreateStatic( + returnType: parameterType, + parameterTypes: [parameterType.GetAbiType(interopReferences)])); + + // We can directly call the marshaller and return it, no 'try/finally' complexity is needed + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, marshallerMethod)]); + } + } + else if (parameterType.IsTypeOfString()) + { + // When marshalling 'string' values, we must use 'HStringMarshaller' (the ABI type is not actually a COM object) + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged)]); + } + else if (parameterType is GenericInstanceTypeSignature) + { + // This case (constructed interfaces or delegates) is effectively identical to marshalling 'KeyValuePair<,>' values + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]); + } + else + { + // Get the marshaller type for all other reference types + ITypeDefOrRef marshallerType = GetReferenceTypeMarshallerType(parameterType, interopReferences, emitState); + + // Get the marshalling method, with the parameter type always just being 'void*' here too + IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef( + name: "ConvertToManaged"u8, + signature: MethodSignature.CreateStatic( + returnType: parameterType, + parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()])); + + // Marshal the value and release the original interface pointer + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, marshallerMethod)]); + } + } + } +} \ No newline at end of file From bf95f243899b45dbcd37ccc3736e8d63ac4b9759 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 15:29:32 -0800 Subject: [PATCH 28/38] Refactor method rewrite info classes for return values Replaces ReturnTypeMethodRewriteInfo and RetValTypeMethodRewriteInfo with nested MethodRewriteInfo.ReturnValue and MethodRewriteInfo.RetVal classes. Updates all usages and improves organization by grouping related types under MethodRewriteInfo. This change simplifies the codebase and clarifies the distinction between managed and unmanaged return value rewrites. --- .../Generation/InteropGenerator.Emit.cs | 18 +++--- .../Generation/InteropGeneratorEmitState.cs | 6 +- .../MethodRewriteInfo.RetVal.cs | 34 +++++++++++ .../MethodRewriteInfo.ReturnValue.cs | 56 +++++++++++++++++++ .../MethodRewriteInfo/MethodRewriteInfo.cs | 2 +- .../RetValTypeMethodRewriteInfo.cs | 30 ---------- .../ReturnTypeMethodRewriteInfo.cs | 52 ----------------- 7 files changed, 103 insertions(+), 95 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RetVal.cs create mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ReturnValue.cs delete mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/RetValTypeMethodRewriteInfo.cs delete mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/ReturnTypeMethodRewriteInfo.cs diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index c5d2adf96..d85ef7df2 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -2014,23 +2014,23 @@ private static void RewriteMethodDefinitions( switch (rewriteInfo) { // Rewrite return values for managed types - case ReturnTypeMethodRewriteInfo returnTypeInfo: + case MethodRewriteInfo.ReturnValue returnValueInfo: InteropMethodRewriteFactory.ReturnValue.RewriteMethod( - returnType: returnTypeInfo.Type, - method: returnTypeInfo.Method, - marker: returnTypeInfo.Marker, - source: returnTypeInfo.Source, + returnType: returnValueInfo.Type, + method: returnValueInfo.Method, + marker: returnValueInfo.Marker, + source: returnValueInfo.Source, interopReferences: interopReferences, emitState: emitState, module: module); break; // Rewrite return values for native types - case RetValTypeMethodRewriteInfo retValTypeInfo: + case MethodRewriteInfo.RetVal retValInfo: InteropMethodRewriteFactory.RetVal.RewriteMethod( - retValType: retValTypeInfo.Type, - method: retValTypeInfo.Method, - marker: retValTypeInfo.Marker, + retValType: retValInfo.Type, + method: retValInfo.Method, + marker: retValInfo.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 181f3ba93..5b8444c96 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -88,7 +88,7 @@ public TypeDefinition LookupTypeDefinition(TypeSignature typeSignature, string k /// /// /// - /// + /// public void TrackReturnValueMethodRewrite( TypeSignature returnType, MethodDefinition method, @@ -97,7 +97,7 @@ public void TrackReturnValueMethodRewrite( { ThrowIfReadOnly(); - _methodRewriteInfos.Add(new ReturnTypeMethodRewriteInfo + _methodRewriteInfos.Add(new MethodRewriteInfo.ReturnValue { Type = returnType, Method = method, @@ -119,7 +119,7 @@ public void TrackRetValValueMethodRewrite( { ThrowIfReadOnly(); - _methodRewriteInfos.Add(new RetValTypeMethodRewriteInfo + _methodRewriteInfos.Add(new MethodRewriteInfo.RetVal { Type = retValType, Method = method, diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RetVal.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RetVal.cs new file mode 100644 index 000000000..d86d21e98 --- /dev/null +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.RetVal.cs @@ -0,0 +1,34 @@ +// 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 an unmanaged [retval] value. + /// + /// + public sealed class RetVal : MethodRewriteInfo + { + /// + public override int CompareTo(MethodRewriteInfo? other) + { + if (other is null) + { + return 1; + } + + if (ReferenceEquals(this, other)) + { + return 0; + } + + // Same logic as in 'ReturnValue', or just compare the base state + return other is not RetVal + ? typeof(RetVal).FullName!.CompareTo(other.GetType().FullName!) + : CompareByMethodRewriteInfo(other); + } + } +} diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ReturnValue.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ReturnValue.cs new file mode 100644 index 000000000..6fdf333ca --- /dev/null +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ReturnValue.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet.Code.Cil; + +namespace WindowsRuntime.InteropGenerator.Models; + +/// +internal partial class MethodRewriteInfo +{ + /// + /// Contains info for a target method for two-pass IL generation, for a managed return value. + /// + /// + public sealed class ReturnValue : MethodRewriteInfo + { + /// + public required CilLocalVariable Source { get; init; } + + /// + public override int CompareTo(MethodRewriteInfo? other) + { + if (other is null) + { + return 1; + } + + if (ReferenceEquals(this, other)) + { + return 0; + } + + // If the input object is of a different type, just sort alphabetically based on the type name + if (other is not ReturnValue info) + { + return typeof(ReturnValue).FullName!.CompareTo(other.GetType().FullName!); + } + + int result = CompareByMethodRewriteInfo(other); + + // If the two items are already not equal, we can stop here + if (result != 0) + { + return result; + } + + int leftIndex = Method.CilMethodBody?.LocalVariables.IndexOf(Source) ?? -1; + int rightIndex = other.Method.CilMethodBody?.LocalVariables.IndexOf(info.Source) ?? -1; + + // Lastly, compare by order of instructions within the target method. + // There's no concern about stable sorting with respect to objects + // where the instructions are missing, as 'cswinrtgen' will fail. + return leftIndex.CompareTo(rightIndex); + } + } +} diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.cs index 6844e7a8e..0790df9fe 100644 --- a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.cs +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.cs @@ -12,7 +12,7 @@ namespace WindowsRuntime.InteropGenerator.Models; /// /// Contains generic info for a target method for two-pass IL generation. /// -internal abstract class MethodRewriteInfo : IComparable +internal abstract partial class MethodRewriteInfo : IComparable { /// /// The type of the value that needs to be marshalled. diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/RetValTypeMethodRewriteInfo.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/RetValTypeMethodRewriteInfo.cs deleted file mode 100644 index 036e7f6b3..000000000 --- a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/RetValTypeMethodRewriteInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace WindowsRuntime.InteropGenerator.Models; - -/// -/// Contains info for a target method for two-pass IL generation, for an unmanaged [retval] value. -/// -/// -internal sealed class RetValTypeMethodRewriteInfo : MethodRewriteInfo -{ - /// - public override int CompareTo(MethodRewriteInfo? other) - { - if (other is null) - { - return 1; - } - - if (ReferenceEquals(this, other)) - { - return 0; - } - - // Same logic as in 'ReturnTypeMethodRewriteInfo', or just compare the base state - return other is not RetValTypeMethodRewriteInfo - ? typeof(RetValTypeMethodRewriteInfo).FullName!.CompareTo(other.GetType().FullName!) - : CompareByMethodRewriteInfo(other); - } -} diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/ReturnTypeMethodRewriteInfo.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/ReturnTypeMethodRewriteInfo.cs deleted file mode 100644 index d22d5c562..000000000 --- a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/ReturnTypeMethodRewriteInfo.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using AsmResolver.DotNet.Code.Cil; - -namespace WindowsRuntime.InteropGenerator.Models; - -/// -/// Contains info for a target method for two-pass IL generation, for a managed return value. -/// -/// -internal sealed class ReturnTypeMethodRewriteInfo : MethodRewriteInfo -{ - /// - public required CilLocalVariable Source { get; init; } - - /// - public override int CompareTo(MethodRewriteInfo? other) - { - if (other is null) - { - return 1; - } - - if (ReferenceEquals(this, other)) - { - return 0; - } - - // If the input object is of a different type, just sort alphabetically based on the type name - if (other is not ReturnTypeMethodRewriteInfo info) - { - return typeof(ReturnTypeMethodRewriteInfo).FullName!.CompareTo(other.GetType().FullName!); - } - - int result = CompareByMethodRewriteInfo(other); - - // If the two items are already not equal, we can stop here - if (result != 0) - { - return result; - } - - int leftIndex = Method.CilMethodBody?.LocalVariables.IndexOf(Source) ?? -1; - int rightIndex = other.Method.CilMethodBody?.LocalVariables.IndexOf(info.Source) ?? -1; - - // Lastly, compare by order of instructions within the target method. - // There's no concern about stable sorting with respect to objects - // where the instructions are missing, as 'cswinrtgen' will fail. - return leftIndex.CompareTo(rightIndex); - } -} From acdec2cbee7785982c5bfc311176eb2334a68c4d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 15:32:50 -0800 Subject: [PATCH 29/38] Add ManagedParameter support to method rewrite info Introduces the ManagedParameter class to MethodRewriteInfo for handling managed parameters in two-pass IL generation. Updates InteropGenerator.Emit to process ManagedParameter instances using the appropriate rewrite method. --- .../Generation/InteropGenerator.Emit.cs | 12 +++++ .../MethodRewriteInfo.ManagedParameter.cs | 49 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ManagedParameter.cs diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index d85ef7df2..0267b8cdc 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -2035,6 +2035,18 @@ private static void RewriteMethodDefinitions( emitState: emitState, module: module); break; + + // Rewrite managed parameters + case MethodRewriteInfo.ManagedParameter managedParameterInfo: + InteropMethodRewriteFactory.ManagedParameter.RewriteMethod( + parameterType: managedParameterInfo.Type, + method: managedParameterInfo.Method, + marker: managedParameterInfo.Marker, + parameterIndex: managedParameterInfo.ParameterIndex, + interopReferences: interopReferences, + emitState: emitState, + module: module); + break; default: throw new UnreachableException(); } } diff --git a/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ManagedParameter.cs b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ManagedParameter.cs new file mode 100644 index 000000000..5e18a8788 --- /dev/null +++ b/src/WinRT.Interop.Generator/Models/MethodRewriteInfo/MethodRewriteInfo.ManagedParameter.cs @@ -0,0 +1,49 @@ +// 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 a managed parameter. + /// + /// + public sealed class ManagedParameter : MethodRewriteInfo + { + /// + public required int ParameterIndex { get; init; } + + /// + public override int CompareTo(MethodRewriteInfo? other) + { + if (other is null) + { + return 1; + } + + if (ReferenceEquals(this, other)) + { + return 0; + } + + // If the input object is of a different type, just sort alphabetically based on the type name + if (other is not ManagedParameter info) + { + return typeof(ManagedParameter).FullName!.CompareTo(other.GetType().FullName!); + } + + int result = CompareByMethodRewriteInfo(other); + + // If the two items are already not equal, we can stop here + if (result != 0) + { + return result; + } + + // Lastly, compare by parameter index + return ParameterIndex.CompareTo(info.ParameterIndex); + } + } +} From 89908bcdaa4de9a28dadb7d82af685f3f2d47bd0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 15:37:26 -0800 Subject: [PATCH 30/38] Refactor delegate Invoke method to use ABI parameter types Updated the delegate Invoke method to use ABI-specific sender and argument types instead of void pointers. Introduced tracking of managed parameter method rewrites in InteropGeneratorEmitState to support this change. --- .../InteropTypeDefinitionBuilder.Delegate.cs | 41 +++++++++++++++---- .../Generation/InteropGenerator.Emit.cs | 1 + .../Generation/InteropGeneratorEmitState.cs | 24 +++++++++++ 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs index fa27beecd..813b42edf 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs @@ -8,8 +8,9 @@ using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Factories; -using WindowsRuntime.InteropGenerator.References; +using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; +using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; namespace WindowsRuntime.InteropGenerator.Builders; @@ -71,19 +72,28 @@ public static void IIDs( /// The for the type. /// The instance to use. /// The instance to use. + /// The emit state for this invocation. /// The interop module being built. /// The resulting implementation type. public static void ImplType( GenericInstanceTypeSignature delegateType, InteropDefinitions interopDefinitions, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition implType) { + MemberReference delegateInvokeMethod = interopReferences.DelegateInvoke(delegateType, module); + + // Prepare the sender and arguments types. This path is only ever reached for valid + // generic Windows Runtime delegate types, and they all have exactly two type arguments. + TypeSignature senderType = ((MethodSignature)delegateInvokeMethod.Signature!).ParameterTypes[0]; + TypeSignature argsType = ((MethodSignature)delegateInvokeMethod.Signature!).ParameterTypes[1]; + // Define the 'Invoke' method as follows: // // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] - // private static int Invoke(void* thisPtr, void* sender, void* e) + // private static int Invoke(void* thisPtr, sender, e) MethodDefinition invokeMethod = new( name: "Invoke"u8, attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, @@ -91,8 +101,8 @@ public static void ImplType( returnType: module.CorLibTypeFactory.Int32, parameterTypes: [ module.CorLibTypeFactory.Void.MakePointerType(), - module.CorLibTypeFactory.Void.MakePointerType(), - module.CorLibTypeFactory.Void.MakePointerType()])) + senderType.GetAbiType(interopReferences).Import(module), + argsType.GetAbiType(interopReferences).Import(module)])) { CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } }; @@ -101,6 +111,8 @@ public static void ImplType( CilInstruction ldloc_0_returnHResult = new(Ldloc_0); CilInstruction ldarg_0_tryStart = new(Ldarg_0); CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + CilInstruction nop_parameter1Rewrite = new(Nop); + CilInstruction nop_parameter2Rewrite = new(Nop); // Create a method body for the 'Invoke' method invokeMethod.CilMethodBody = new CilMethodBody() @@ -113,11 +125,9 @@ public static void ImplType( // '.try' code { ldarg_0_tryStart }, { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(delegateType).Import(module) }, - { Ldarg_1 }, - { Call, interopReferences.WindowsRuntimeObjectMarshallerConvertToManaged.Import(module) }, - { Ldarg_2 }, - { Call, interopReferences.WindowsRuntimeObjectMarshallerConvertToManaged.Import(module) }, - { Callvirt, interopReferences.DelegateInvoke(delegateType, module).Import(module) }, + { nop_parameter1Rewrite }, + { nop_parameter2Rewrite }, + { Callvirt, delegateInvokeMethod.Import(module) }, { Ldc_I4_0 }, { Stloc_0 }, { Leave_S, ldloc_0_returnHResult.CreateLabel() }, @@ -145,6 +155,19 @@ public static void ImplType( } }; + // Track rewriting the two parameters for this method + emitState.TrackManagedParameterMethodRewrite( + paraneterType: senderType, + method: invokeMethod, + marker: nop_parameter1Rewrite, + parameterIndex: 1); + + emitState.TrackManagedParameterMethodRewrite( + paraneterType: argsType, + method: invokeMethod, + marker: nop_parameter2Rewrite, + parameterIndex: 2); + Impl( interfaceType: ComInterfaceType.InterfaceIsIUnknown, ns: InteropUtf8NameFactory.TypeNamespace(delegateType), diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 0267b8cdc..1a3a2f4db 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -300,6 +300,7 @@ private static void DefineGenericDelegateTypes( delegateType: typeSignature, interopDefinitions: interopDefinitions, interopReferences: interopReferences, + emitState: emitState, module: module, implType: out TypeDefinition delegateImplType); diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs index 5b8444c96..52a4f2a8c 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorEmitState.cs @@ -127,6 +127,30 @@ public void TrackRetValValueMethodRewrite( }); } + /// + /// Tracks a method rewrite that involves loading a parameter in the specified method. + /// + /// + /// + /// + /// + public void TrackManagedParameterMethodRewrite( + TypeSignature paraneterType, + MethodDefinition method, + CilInstruction marker, + int parameterIndex) + { + ThrowIfReadOnly(); + + _methodRewriteInfos.Add(new MethodRewriteInfo.ManagedParameter + { + Type = paraneterType, + Method = method, + Marker = marker, + ParameterIndex = parameterIndex + }); + } + /// /// Enumerates all instances with info on two-pass code generation steps to perform. /// From f927d7d92bcf1ca119b5d0fb253b84c7c35e84ba Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 17:40:48 -0800 Subject: [PATCH 31/38] Add IEnumerable, IEnumerator, and IList to type checks Extended the type comparison logic to include IEnumerable, IEnumerator, and IList interfaces in WindowsRuntimeExtensions. This ensures these interfaces are now considered in relevant type checks. --- .../Extensions/WindowsRuntimeExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index b78b155bf..791bc3859 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -199,6 +199,9 @@ public bool IsCustomMappedWindowsRuntimeNonGenericInterfaceType(InteropReference SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IDisposable) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IServiceProvider) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.ICommand) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IEnumerable) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IEnumerator) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IList) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.INotifyCollectionChanged) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.INotifyDataErrorInfo) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.INotifyPropertyChanged); From 4388a51740427d847325e9983a867931b659051e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 17:56:56 -0800 Subject: [PATCH 32/38] Add type mappings for IEnumerator and IBindableIterator Added mappings from System.Collections.IEnumerator to Microsoft.UI.Xaml.Interop.IBindableIterator and from Microsoft.UI.Xaml.Interop.IBindableIterator to Windows.UI.Xaml.Interop.IBindableIterator. This improves type translation support for collection interfaces. --- src/WinRT.Interop.Generator/Helpers/TypeMapping.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/WinRT.Interop.Generator/Helpers/TypeMapping.cs b/src/WinRT.Interop.Generator/Helpers/TypeMapping.cs index c4a3ba39d..deec11afb 100644 --- a/src/WinRT.Interop.Generator/Helpers/TypeMapping.cs +++ b/src/WinRT.Interop.Generator/Helpers/TypeMapping.cs @@ -51,6 +51,7 @@ private readonly record struct MappedType( new("System.ComponentModel.PropertyChangedEventHandler", new("Microsoft.UI.Xaml.Data", "PropertyChangedEventHandler")), new("System.Windows.Input.ICommand", new("Microsoft.UI.Xaml.Input", "ICommand")), new("System.Collections.IEnumerable", new("Microsoft.UI.Xaml.Interop", "IBindableIterable")), + new("System.Collections.IEnumerator", new("Microsoft.UI.Xaml.Interop", "IBindableIterator")), new("System.Collections.IList", new("Microsoft.UI.Xaml.Interop", "IBindableVector")), new("System.Collections.Specialized.INotifyCollectionChanged", new("Microsoft.UI.Xaml.Interop", "INotifyCollectionChanged")), new("System.Collections.Specialized.NotifyCollectionChangedAction", new("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedAction", "enum(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction;i4)")), @@ -94,6 +95,7 @@ private readonly record struct MappedType( new("Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction", new("Windows.UI.Xaml.Interop", "NotifyCollectionChangedAction", "enum(Windows.UI.Xaml.Interop.NotifyCollectionChangedAction;i4)")), new("Microsoft.UI.Xaml.Interop.INotifyCollectionChanged", new("Windows.UI.Xaml.Interop", "INotifyCollectionChanged")), new("Microsoft.UI.Xaml.Interop.IBindableIterable", new("Windows.UI.Xaml.Interop", "IBindableIterable")), + new("Microsoft.UI.Xaml.Interop.IBindableIterator", new("Windows.UI.Xaml.Interop", "IBindableIterator")), new("Microsoft.UI.Xaml.Interop.IBindableVector", new("Windows.UI.Xaml.Interop", "IBindableVector")), new("Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler", new("Windows.UI.Xaml.Interop", "NotifyCollectionChangedEventHandler")), new("Microsoft.UI.Xaml.Input.ICommand", new("Windows.UI.Xaml.Input", "ICommand")), From e93df6a658ce26ef230e5007f6f6c04a55c8f64b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 17:57:02 -0800 Subject: [PATCH 33/38] Add XAML IBindable interface mappings and adjust GUIDs Added mappings for IBindableIterable, IBindableIterator, and IBindableVector interfaces for both Windows.UI.Xaml and Microsoft.UI.Xaml projections. Moved the corresponding GUID assignments for IEnumerable, IEnumerator, and IList to the XAML section to ensure correct interface resolution. --- .../References/WellKnownInterfaceIIDs.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index 2a4ebbe32..e95112ff3 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -90,6 +90,18 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I => "Windows_Foundation_Collections_IVectorChangedEventArgs", // XAML types + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable) && useWindowsUIXamlProjections + => "Windows_UI_Xaml_Interop_IBindableIterable", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable) + => "Microsoft_UI_Xaml_Interop_IBindableIterable", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerator) && useWindowsUIXamlProjections + => "Windows_UI_Xaml_Interop_IBindableIterator", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerator) + => "Microsoft_UI_Xaml_Interop_IBindableIterator", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IList) && useWindowsUIXamlProjections + => "Windows_UI_Xaml_Interop_IBindableVector", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IList) + => "Microsoft_UI_Xaml_Interop_IBindableVector", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) && useWindowsUIXamlProjections => "Windows_UI_Xaml_Interop_INotifyCollectionChanged", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) @@ -142,12 +154,8 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.E => new Guid("9DE1C535-6AE1-11E0-84E1-18A905BCC53F"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.KeyValuePair2) => new Guid("02B51929-C1C4-4A7E-8940-0312B5C18500"), - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable) - => new Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable1) => new Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3"), - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerator) - => new Guid("6A79E863-4300-459A-9966-CBB660963EE1"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerator1) => new Guid("6A79E863-4300-459A-9966-CBB660963EE1"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.AsyncOperationWithProgressCompletedHandler2) @@ -156,8 +164,6 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.A => new Guid("9D534225-231F-55E7-A6D0-6C938E2D9160"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.MapChangedEventHandler2) => new Guid("19046F0B-CF81-5DEC-BBB2-7CC250DA8B8B"), - _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IList) - => new Guid("393DE7DE-6FD0-4C0D-BB71-47244A113E93"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IList1) => new Guid("0E3F106F-A266-50A1-8043-C90FCF3844F6"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IReadOnlyList1) @@ -190,6 +196,12 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I => new Guid("575933DF-34FE-4480-AF15-07691F3D5D9B"), // XAML types + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable) + => new Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerator) + => new Guid("6A79E863-4300-459A-9966-CBB660963EE1"), + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IList) + => new Guid("393DE7DE-6FD0-4C0D-BB71-47244A113E93"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) && useWindowsUIXamlProjections => new Guid("28B167D5-1A31-465B-9B25-D5C3AE686C40"), _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.INotifyCollectionChanged) From f334d908f968a5eef2f9df7fd7996e6bea2f731c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 17 Dec 2025 19:33:00 -0800 Subject: [PATCH 34/38] Track IMapChangedEventArgs1 type for MapChangedEventHandler Adds explicit tracking of the IMapChangedEventArgs1 type when handling MapChangedEventHandler generic delegate types. This ensures that the event args type is also discovered and processed during interop type discovery. --- .../Discovery/InteropTypeDiscovery.Generics.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index 19b21da16..6b8294d01 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -302,6 +302,9 @@ private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( // Same handling as above for 'MapChangedEventHandler' types discoveryState.TrackGenericDelegateType(interopReferences.MapChangedEventHandler2.MakeGenericReferenceType([.. typeSignature.TypeArguments])); + + // Also manually track the args type for 'MapChangedEventHandler' + discoveryState.TrackIMapChangedEventArgs1Type(interopReferences.IMapChangedEventArgs1.MakeGenericReferenceType(typeSignature.TypeArguments[1])); } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IMapChangedEventArgs1)) { From 3068206d01610be64681eeaedc2bd4973ba0f228 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 18 Dec 2025 10:25:45 -0800 Subject: [PATCH 35/38] Refactor marshaller creation to centralize logic Removed redundant Marshaller methods from multiple InteropTypeDefinitionBuilder partial classes and consolidated marshaller creation into a single public static method. Updated all call sites to use the unified Marshaller method, ensuring emitState tracking is consistently applied. This reduces code duplication and simplifies maintenance. --- .../InteropTypeDefinitionBuilder.Delegate.cs | 5 + ...nitionBuilder.IAsyncActionWithProgress1.cs | 26 ------ ...pTypeDefinitionBuilder.IAsyncOperation1.cs | 26 ------ ...ionBuilder.IAsyncOperationWithProgress2.cs | 26 ------ ...teropTypeDefinitionBuilder.IDictionary2.cs | 31 ------- ...teropTypeDefinitionBuilder.IEnumerable1.cs | 31 ------- ...teropTypeDefinitionBuilder.IEnumerator1.cs | 31 ------- .../InteropTypeDefinitionBuilder.IList1.cs | 31 ------- ...DefinitionBuilder.IMapChangedEventArgs1.cs | 31 ------- ...opTypeDefinitionBuilder.IObservableMap2.cs | 26 ------ ...ypeDefinitionBuilder.IObservableVector1.cs | 26 ------ ...eDefinitionBuilder.IReadOnlyDictionary2.cs | 31 ------- ...ropTypeDefinitionBuilder.IReadOnlyList1.cs | 31 ------- ...teropTypeDefinitionBuilder.KeyValuePair.cs | 6 +- .../Builders/InteropTypeDefinitionBuilder.cs | 8 +- .../Generation/InteropGenerator.Emit.cs | 93 +++++++++---------- 16 files changed, 57 insertions(+), 402 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs index 813b42edf..fd1528ae8 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs @@ -649,6 +649,7 @@ public static void ComWrappersMarshallerAttribute( /// The 'IID' get method for the 'IDelegate' interface. /// The resulting 'IID' get method for the boxed 'IDelegate' interface. /// 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( @@ -657,6 +658,7 @@ public static void Marshaller( MethodDefinition get_IidMethod, MethodDefinition get_ReferenceIidMethod, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition marshallerType) { @@ -767,6 +769,9 @@ public static void Marshaller( }; marshallerType.Methods.Add(unboxToUnmanagedMethod); + + // Track the type (it may be needed to marshal parameters or return values) + emitState.TrackTypeDefinition(marshallerType, delegateType, "Marshaller"); } /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncActionWithProgress1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncActionWithProgress1.cs index daee341c9..18843baa7 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncActionWithProgress1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncActionWithProgress1.cs @@ -227,32 +227,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IAsyncActionWithProgress<TProgress> interface. - /// - /// The for the async action type. - /// The instance returned by . - /// The 'IID' get method for . - /// The instance to use. - /// The module that will contain the type being created. - /// The resulting marshaller type. - public static void Marshaller( - GenericInstanceTypeSignature actionType, - TypeDefinition operationComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: actionType, - interfaceComWrappersCallbackType: operationComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - } - /// /// Creates a new type definition for the interface implementation of some IAsyncActionWithProgress<TProgress> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperation1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperation1.cs index e45035665..30c6749f1 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperation1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperation1.cs @@ -180,32 +180,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IAsyncOperation1<TResult> interface. - /// - /// The for the async operation type. - /// The instance returned by . - /// The 'IID' get method for . - /// The instance to use. - /// The module that will contain the type being created. - /// The resulting marshaller type. - public static void Marshaller( - GenericInstanceTypeSignature operationType, - TypeDefinition operationComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: operationType, - interfaceComWrappersCallbackType: operationComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - } - /// /// Creates a new type definition for the interface implementation of some IAsyncOperation1<TResult> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperationWithProgress2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperationWithProgress2.cs index 0d856babb..214ced000 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperationWithProgress2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IAsyncOperationWithProgress2.cs @@ -222,32 +222,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IAsyncOperationWithProgress<TResult, TProgress> interface. - /// - /// The for the async operation type. - /// The instance returned by . - /// The 'IID' get method for . - /// The instance to use. - /// The module that will contain the type being created. - /// The resulting marshaller type. - public static void Marshaller( - GenericInstanceTypeSignature operationType, - TypeDefinition operationComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: operationType, - interfaceComWrappersCallbackType: operationComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - } - /// /// Creates a new type definition for the interface implementation of some IAsyncOperationWithProgress<TResult, TProgress> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs index 6f05e21aa..36b170243 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs @@ -729,37 +729,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IMap<K, V> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature dictionaryType, - TypeDefinition dictionaryComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: dictionaryType, - interfaceComWrappersCallbackType: dictionaryComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, dictionaryType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IMap<K, V> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerable1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerable1.cs index 8e8c7750f..fbb78f40d 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerable1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerable1.cs @@ -342,37 +342,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IIterable<T> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature enumerableType, - TypeDefinition enumerableComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: enumerableType, - interfaceComWrappersCallbackType: enumerableComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, enumerableType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IIterable<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index b3865e57b..9eb83a639 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -256,37 +256,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IIterator<T> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature enumeratorType, - TypeDefinition enumeratorComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: enumeratorType, - interfaceComWrappersCallbackType: enumeratorComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it's needed by 'IEnumerable') - emitState.TrackTypeDefinition(marshallerType, enumeratorType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IIterator<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs index 82b89cfad..f4a5e8999 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs @@ -657,37 +657,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IVector<T> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature listType, - TypeDefinition listComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: listType, - interfaceComWrappersCallbackType: listComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, listType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IVector<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs index ddce82f70..9bb8629da 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs @@ -177,37 +177,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IMapChangedEventArgs<K> interface. - /// - /// The for the args type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature argsType, - TypeDefinition argsComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: argsType, - interfaceComWrappersCallbackType: argsComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it's needed by 'IEnumerable') - emitState.TrackTypeDefinition(marshallerType, argsType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IMapChangedEventArgs<K> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs index c707f8ec2..bb195eaa7 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableMap2.cs @@ -300,32 +300,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IObservableMap<K, V> interface. - /// - /// The for the map type. - /// The instance returned by . - /// The 'IID' get method for . - /// The instance to use. - /// The module that will contain the type being created. - /// The resulting marshaller type. - public static void Marshaller( - GenericInstanceTypeSignature mapType, - TypeDefinition mapComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: mapType, - interfaceComWrappersCallbackType: mapComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - } - /// /// Creates a new type definition for the interface implementation of some IObservableMap<K, V> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs index d70160642..efce554ba 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IObservableVector1.cs @@ -295,32 +295,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IObservableVector<T> interface. - /// - /// The for the vector type. - /// The instance returned by . - /// The 'IID' get method for . - /// The instance to use. - /// The module that will contain the type being created. - /// The resulting marshaller type. - public static void Marshaller( - GenericInstanceTypeSignature vectorType, - TypeDefinition vectorComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: vectorType, - interfaceComWrappersCallbackType: vectorComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - } - /// /// Creates a new type definition for the interface implementation of some IObservableVector<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs index b99efcaac..179251ee8 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs @@ -384,37 +384,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IMapView<K, V> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature readOnlyDictionaryType, - TypeDefinition readOnlyDictionaryComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: readOnlyDictionaryType, - interfaceComWrappersCallbackType: readOnlyDictionaryComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, readOnlyDictionaryType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IMapView<K, V> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyList1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyList1.cs index 1a0df72a0..7e78946a9 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyList1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyList1.cs @@ -269,37 +269,6 @@ public static void ComWrappersMarshallerAttribute( out marshallerType); } - /// - /// Creates a new type definition for the marshaller of some IVectorView<T> interface. - /// - /// The for the type. - /// The instance returned by . - /// The 'IID' get method for . - /// 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( - GenericInstanceTypeSignature readOnlyListType, - TypeDefinition readOnlyListComWrappersCallbackType, - MethodDefinition get_IidMethod, - InteropReferences interopReferences, - InteropGeneratorEmitState emitState, - ModuleDefinition module, - out TypeDefinition marshallerType) - { - InteropTypeDefinitionBuilder.Marshaller( - typeSignature: readOnlyListType, - interfaceComWrappersCallbackType: readOnlyListComWrappersCallbackType, - get_IidMethod: get_IidMethod, - interopReferences: interopReferences, - module: module, - out marshallerType); - - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, readOnlyListType, "Marshaller"); - } - /// /// Creates a new type definition for the interface implementation of some IVectorView<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs index 8d6f17008..f132b0201 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs @@ -136,9 +136,6 @@ public static void Marshaller( module.TopLevelTypes.Add(marshallerType); - // Track the type (it may be needed to marshal parameters or return values) - emitState.TrackTypeDefinition(marshallerType, keyValuePairType, "Marshaller"); - // Prepare the external types we need in the implemented methods TypeSignature typeSignature2 = keyValuePairType.Import(module); TypeSignature windowsRuntimeObjectReferenceValueType = interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature(); @@ -214,6 +211,9 @@ public static void Marshaller( }; marshallerType.Methods.Add(convertToManagedMethod); + + // Track the type (it may be needed to marshal parameters or return values) + emitState.TrackTypeDefinition(marshallerType, keyValuePairType, "Marshaller"); } /// diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs index d6ed1fea0..bfb3127f7 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs @@ -11,6 +11,7 @@ using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Factories; +using WindowsRuntime.InteropGenerator.Generation; using WindowsRuntime.InteropGenerator.Helpers; using WindowsRuntime.InteropGenerator.References; using static AsmResolver.PE.DotNet.Cil.CilOpCodes; @@ -290,13 +291,15 @@ private static void ComWrappersMarshallerAttribute( /// The instance returned by . /// The 'IID' get method for . /// The instance to use. + /// The emit state for this invocation. /// The module that will contain the type being created. /// The resulting marshaller type. - private static void Marshaller( + public static void Marshaller( TypeSignature typeSignature, TypeDefinition interfaceComWrappersCallbackType, MethodDefinition get_IidMethod, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition marshallerType) { @@ -363,6 +366,9 @@ private static void Marshaller( }; marshallerType.Methods.Add(convertToManagedMethod); + + // Track the type (it may be needed to marshal parameters or return values) + emitState.TrackTypeDefinition(marshallerType, typeSignature, "Marshaller"); } /// diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index 1a3a2f4db..e99445de9 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -293,6 +293,7 @@ private static void DefineGenericDelegateTypes( get_IidMethod: get_IidMethod, get_ReferenceIidMethod: get_ReferenceIidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); @@ -367,9 +368,6 @@ private static void DefineGenericDelegateTypes( } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.VectorChangedEventHandler1)) { - // We need the marshaller type for the 'IObservableVector' implementation - emitState.TrackTypeDefinition(marshallerType, typeSignature, "Marshaller"); - InteropTypeDefinitionBuilder.EventSource.VectorChangedEventHandler1( delegateType: typeSignature, marshallerType: marshallerType, @@ -380,9 +378,6 @@ private static void DefineGenericDelegateTypes( } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.MapChangedEventHandler2)) { - // We need the marshaller type for the 'IObservableMap' implementation - emitState.TrackTypeDefinition(marshallerType, typeSignature, "Marshaller"); - InteropTypeDefinitionBuilder.EventSource.MapChangedEventHandler2( delegateType: typeSignature, marshallerType: marshallerType, @@ -391,15 +386,6 @@ private static void DefineGenericDelegateTypes( module: module, eventSourceType: out _); } - else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.AsyncActionProgressHandler1) || - SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.AsyncActionWithProgressCompletedHandler1) || - SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.AsyncOperationCompletedHandler1) || - SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.AsyncOperationProgressHandler2) || - SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.AsyncOperationWithProgressCompletedHandler2)) - { - // We need these marshaller types for the various async type implementations - emitState.TrackTypeDefinition(marshallerType, typeSignature, "Marshaller"); - } } catch (Exception e) { @@ -478,9 +464,9 @@ private static void DefineIEnumeratorTypes( module: module, out TypeDefinition enumeratorComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IEnumerator1.Marshaller( - enumeratorType: typeSignature, - enumeratorComWrappersCallbackType: enumeratorComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: enumeratorComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -601,9 +587,9 @@ private static void DefineIEnumerableTypes( module: module, out TypeDefinition enumerableComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IEnumerable1.Marshaller( - enumerableType: typeSignature, - enumerableComWrappersCallbackType: enumerableComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: enumerableComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -724,9 +710,9 @@ private static void DefineIReadOnlyListTypes( module: module, out TypeDefinition readOnlyListComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IReadOnlyList1.Marshaller( - readOnlyListType: typeSignature, - readOnlyListComWrappersCallbackType: readOnlyListComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: readOnlyListComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -856,9 +842,9 @@ private static void DefineIListTypes( module: module, out TypeDefinition listComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IList1.Marshaller( - listType: typeSignature, - listComWrappersCallbackType: listComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: listComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -980,9 +966,9 @@ private static void DefineIReadOnlyDictionaryTypes( module: module, out TypeDefinition readOnlyDictionaryComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IReadOnlyDictionary2.Marshaller( - readOnlyDictionaryType: typeSignature, - readOnlyDictionaryComWrappersCallbackType: readOnlyDictionaryComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: readOnlyDictionaryComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -1113,9 +1099,9 @@ private static void DefineIDictionaryTypes( module: module, out TypeDefinition dictionaryComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IDictionary2.Marshaller( - dictionaryType: typeSignature, - dictionaryComWrappersCallbackType: dictionaryComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: dictionaryComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -1310,9 +1296,9 @@ private static void DefineIMapChangedEventArgsTypes( module: module, out TypeDefinition argsComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IMapChangedEventArgs1.Marshaller( - argsType: typeSignature, - argsComWrappersCallbackType: argsComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: argsComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, emitState: emitState, @@ -1427,11 +1413,12 @@ private static void DefineIObservableVectorTypes( module: module, out TypeDefinition comWrappersMarshallerType); - InteropTypeDefinitionBuilder.IObservableVector1.Marshaller( - vectorType: typeSignature, - vectorComWrappersCallbackType: comWrappersMarshallerType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: comWrappersMarshallerType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); @@ -1543,11 +1530,12 @@ private static void DefineIObservableMapTypes( module: module, out TypeDefinition comWrappersMarshallerType); - InteropTypeDefinitionBuilder.IObservableMap2.Marshaller( - mapType: typeSignature, - mapComWrappersCallbackType: comWrappersMarshallerType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: comWrappersMarshallerType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); @@ -1649,11 +1637,12 @@ private static void DefineIAsyncActionWithProgressTypes( module: module, out TypeDefinition actionComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IAsyncActionWithProgress1.Marshaller( - actionType: typeSignature, - operationComWrappersCallbackType: actionComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: actionComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); @@ -1755,11 +1744,12 @@ private static void DefineIAsyncOperationTypes( module: module, out TypeDefinition operationComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IAsyncOperation1.Marshaller( - operationType: typeSignature, - operationComWrappersCallbackType: operationComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: operationComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); @@ -1861,11 +1851,12 @@ private static void DefineIAsyncOperationWithProgressTypes( module: module, out TypeDefinition operationComWrappersMarshallerType); - InteropTypeDefinitionBuilder.IAsyncOperationWithProgress2.Marshaller( - operationType: typeSignature, - operationComWrappersCallbackType: operationComWrappersCallbackType, + InteropTypeDefinitionBuilder.Marshaller( + typeSignature: typeSignature, + interfaceComWrappersCallbackType: operationComWrappersCallbackType, get_IidMethod: get_IidMethod, interopReferences: interopReferences, + emitState: emitState, module: module, marshallerType: out TypeDefinition marshallerType); From e0c01998d9602537d58af62f37a0de779bbd0ed0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 18 Dec 2025 10:33:48 -0800 Subject: [PATCH 36/38] Fix method import for marshaller calls Replaces direct marshaller method calls with calls to their imported versions using `Import(module)`. Ensures correct method resolution during code generation for managed parameter marshalling. --- .../InteropMethodRewriteFactory.ManagedParameter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs index d580241fa..bae3eec7e 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs @@ -98,7 +98,7 @@ public static void RewriteMethod( // Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable' value body.Instructions.ReplaceRange(marker, [ CilInstruction.CreateLdarg(parameterIndex), - new CilInstruction(Call, marshallerMethod)]); + new CilInstruction(Call, marshallerMethod.Import(module))]); } else { @@ -116,7 +116,7 @@ public static void RewriteMethod( // We can directly call the marshaller and return it, no 'try/finally' complexity is needed body.Instructions.ReplaceRange(marker, [ CilInstruction.CreateLdarg(parameterIndex), - new CilInstruction(Call, marshallerMethod)]); + new CilInstruction(Call, marshallerMethod.Import(module))]); } } else if (parameterType.IsTypeOfString()) @@ -124,7 +124,7 @@ public static void RewriteMethod( // When marshalling 'string' values, we must use 'HStringMarshaller' (the ABI type is not actually a COM object) body.Instructions.ReplaceRange(marker, [ CilInstruction.CreateLdarg(parameterIndex), - new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged)]); + new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged.Import(module))]); } else if (parameterType is GenericInstanceTypeSignature) { @@ -148,7 +148,7 @@ public static void RewriteMethod( // Marshal the value and release the original interface pointer body.Instructions.ReplaceRange(marker, [ CilInstruction.CreateLdarg(parameterIndex), - new CilInstruction(Call, marshallerMethod)]); + new CilInstruction(Call, marshallerMethod.Import(module))]); } } } From 6b22963732faab45ed8c0b606940cbf1bbc31d13 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 18 Dec 2025 12:00:16 -0800 Subject: [PATCH 37/38] Refactor delegate invoke method parameter handling Replaces direct use of the delegate's Invoke method signature with retrieval of parameter types from interop references. This streamlines parameter extraction and improves code clarity. --- .../InteropTypeDefinitionBuilder.Delegate.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs index fd1528ae8..0b1b74009 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs @@ -410,6 +410,12 @@ public static void NativeDelegateType( ModuleDefinition module, out TypeDefinition nativeDelegateType) { + MemberReference delegateInvokeMethod = interopReferences.DelegateInvoke(delegateType, module); + + // Prepare the sender and arguments types (same as for the 'Impl' type above) + TypeSignature senderType = ((MethodSignature)delegateInvokeMethod.Signature!).ParameterTypes[0]; + TypeSignature argsType = ((MethodSignature)delegateInvokeMethod.Signature!).ParameterTypes[1]; + // We're declaring an 'internal static class' type nativeDelegateType = new( ns: InteropUtf8NameFactory.TypeNamespace(delegateType), @@ -419,9 +425,6 @@ public static void NativeDelegateType( module.TopLevelTypes.Add(nativeDelegateType); - // Construct the 'Invoke' method on the delegate type, so we can get the constructed parameter types - MethodSignature invokeSignature = delegateType.Import(module).Resolve()!.GetMethod("Invoke"u8).Signature!.InstantiateGenericTypes(GenericContext.FromType(delegateType)); - // Define the 'Invoke' method as follows: // // public static void Invoke(WindowsRuntimeObjectReference objectReference, arg0, arg1) @@ -432,8 +435,8 @@ public static void NativeDelegateType( returnType: module.CorLibTypeFactory.Void, parameterTypes: [ interopReferences.WindowsRuntimeObjectReference.ToReferenceTypeSignature().Import(module), - invokeSignature.ParameterTypes[0].Import(module), - invokeSignature.ParameterTypes[1].Import(module)])) + senderType.Import(module), + argsType.Import(module)])) { CilMethodBody = new CilMethodBody() }; nativeDelegateType.Methods.Add(invokeMethod); From 704c2688209c4bf2a996fce1f706965a651ec192 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 19 Dec 2025 19:06:58 -0800 Subject: [PATCH 38/38] Add marshalling support for Type parameters Introduces handling for parameters of type 'Type' by using 'TypeMarshaller' for conversion to managed types. This ensures correct ABI value type marshalling for 'Type' parameters in interop scenarios. --- .../InteropMethodRewriteFactory.ManagedParameter.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs index bae3eec7e..c733c37a2 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs @@ -126,6 +126,13 @@ public static void RewriteMethod( CilInstruction.CreateLdarg(parameterIndex), new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged.Import(module))]); } + else if (parameterType.IsTypeOfType(interopReferences)) + { + // When marshalling 'Type' values, we must use 'TypeMarshaller' (the ABI type is a value type) + body.Instructions.ReplaceRange(marker, [ + CilInstruction.CreateLdarg(parameterIndex), + new CilInstruction(Call, interopReferences.TypeMarshallerConvertToManaged.Import(module))]); + } else if (parameterType is GenericInstanceTypeSignature) { // This case (constructed interfaces or delegates) is effectively identical to marshalling 'KeyValuePair<,>' values