From 43e082190fdcc8b8c364b372bf140317af85ce19 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 4 Aug 2026 07:13:56 -0700 Subject: [PATCH 01/11] Use GetMany for native string list copies Copy IList values through a single IVector.GetMany ABI call with balanced HSTRING cleanup, falling back only when a provider returns fewer items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ddbd67fd-01c2-4b1b-b23f-f9eb0ad8f70e --- .../Collections/IListMethods{T}.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index 849ecd40d..a4a7e5cff 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.Buffers; +using WindowsRuntime.InteropServices.Marshalling; #pragma warning disable CS1573 @@ -92,6 +94,12 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, ArgumentException.ThrowInsufficientSpaceToCopyCollection(); } + if (typeof(T) == typeof(string) && count > 0) + { + CopyStrings(thisReference, (string[])(object)array, arrayIndex, count); + return; + } + // Copy all items into the target array, at the specified starting offset for (int i = 0; i < count; i++) { @@ -99,6 +107,68 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, } } + private static unsafe void CopyStrings(WindowsRuntimeObjectReference thisReference, string[] array, int arrayIndex, int count) + where TMethods : IVectorMethodsImpl + { + nint[]? rented = null; + Span handles = count <= 32 + ? stackalloc nint[count] + : (rented = ArrayPool.Shared.Rent(count)).AsSpan(0, count); + + uint copied = 0; + + try + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValueForCall(); + void* thisPtr = thisValue.GetThisPtrUnsafe(); + + fixed (nint* handlesPtr = handles) + { + while (copied < count) + { + uint currentCopied; + HRESULT hresult = ((delegate* unmanaged[MemberFunction])(*(void***)thisPtr)[16])( + thisPtr, + copied, + (uint)count - copied, + (void**)(handlesPtr + copied), + ¤tCopied); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + if (currentCopied == 0) + { + break; + } + + copied += currentCopied; + } + } + + for (int i = 0; i < copied; i++) + { + array[arrayIndex + i] = HStringMarshaller.ConvertToManaged((void*)handles[i]); + } + + for (int i = (int)copied; i < count; i++) + { + array[arrayIndex + i] = (string)(object)Item(thisReference, i)!; + } + } + finally + { + for (int i = 0; i < copied; i++) + { + HStringMarshaller.Free((void*)handles[i]); + } + + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + } + } + /// /// The implementation to use. /// The instance to use to invoke the native method. From 687ea7f16564564d96edf3946eb8c3e5e7d68aca Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 13:33:43 -0700 Subject: [PATCH 02/11] Use GetMany for native list copies Batch ICollection.CopyTo through IVector.GetMany across blittable, string, object, reference, value, nullable, key-value pair, Type, and Exception element categories. Add focused native-vector tests and string benchmark coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Benchmarks/Benchmarks/CollectionsPerf.cs | 14 + src/Tests/TestComponentCSharp/Class.cpp | 21 +- src/Tests/TestComponentCSharp/Class.h | 3 + .../TestComponentCSharp.idl | 3 + .../UnitTest/TestComponentCSharp_Tests.cs | 66 +++ .../InteropTypeDefinitionBuilder.IList1.cs | 11 + ...pMethodDefinitionFactory.IVectorMethods.cs | 128 +++++ ...ionFactory.IEnumeratorElementMarshaller.cs | 119 ++++- .../References/InteropReferences.cs | 245 +++++++++ .../Collections/IListMethods{T}.cs | 77 +-- .../Collections/IVectorMethods.cs | 482 ++++++++++++++++++ .../Collections/IVectorMethodsImpl{T}.cs | 5 + ...PairTypeElementMarshaller{TKey, TValue}.cs | 12 +- ...agedValueTypeElementMarshaller{T, TAbi}.cs | 5 + ...RuntimeNullableTypeElementMarshaller{T}.cs | 12 +- ...untimeReferenceTypeElementMarshaller{T}.cs | 12 +- ...agedValueTypeElementMarshaller{T, TAbi}.cs | 5 + 17 files changed, 1141 insertions(+), 79 deletions(-) diff --git a/src/Benchmarks/Benchmarks/CollectionsPerf.cs b/src/Benchmarks/Benchmarks/CollectionsPerf.cs index 612b3bc11..9cc7d02c9 100644 --- a/src/Benchmarks/Benchmarks/CollectionsPerf.cs +++ b/src/Benchmarks/Benchmarks/CollectionsPerf.cs @@ -19,6 +19,8 @@ public class CollectionsPerf private IList vector; private IList bulkVector; private int[] bulkBuffer; + private IList bulkStringVector; + private string[] bulkStringBuffer; private IDictionary stringMap; private IReadOnlyList vectorView; private IReadOnlyDictionary mapView; @@ -38,6 +40,12 @@ public void Setup() vector = instance.Items(VectorLen); bulkVector = instance.Items(BulkCount); bulkBuffer = new int[BulkCount]; + bulkStringVector = instance.NewList(); + bulkStringBuffer = new string[BulkCount]; + for (int i = 0; i < BulkCount; i++) + { + bulkStringVector.Add(i.ToString()); + } stringMap = instance.StringMap(MapLen); vectorView = instance.ItemsView(VectorLen); mapView = instance.MapView(MapLen); @@ -89,6 +97,12 @@ public void GetMany() bulkVector.CopyTo(bulkBuffer, 0); } + [Benchmark(OperationsPerInvoke = BulkCount)] + public void GetManyStrings() + { + bulkStringVector.CopyTo(bulkStringBuffer, 0); + } + [Benchmark(OperationsPerInvoke = BulkCount)] public void GetManyObjects() { diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp index 557eae1b9..012326e4e 100644 --- a/src/Tests/TestComponentCSharp/Class.cpp +++ b/src/Tests/TestComponentCSharp/Class.cpp @@ -1203,6 +1203,26 @@ namespace winrt::TestComponentCSharp::implementation }); } + IVector Class::GetDateTimeVector2() + { + auto now = winrt::clock::now(); + return winrt::single_threaded_vector(std::vector{ now, now + std::chrono::seconds{ 1 } }); + } + + IVector Class::GetClassVector2() + { + return winrt::single_threaded_vector(std::vector + { + winrt::make(), + winrt::make(), + }); + } + + IVector Class::GetExceptionVector2() + { + return winrt::single_threaded_vector(std::vector{ winrt::hresult{ -2147467259 }, winrt::hresult{ -2147024809 } }); + } + // Test IIDOptimizer IVectorView Class::GetEventArgsVector() { @@ -2210,4 +2230,3 @@ namespace winrt::TestComponentCSharp::implementation return winrt::make(); } } - diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h index 557270389..51578241a 100644 --- a/src/Tests/TestComponentCSharp/Class.h +++ b/src/Tests/TestComponentCSharp/Class.h @@ -292,6 +292,9 @@ namespace winrt::TestComponentCSharp::implementation Windows::Foundation::Collections::IVector GetIntVector2(); Windows::Foundation::Collections::IVector GetBlittableStructVector2(); Windows::Foundation::Collections::IVector GetNonBlittableStructVector2(); + Windows::Foundation::Collections::IVector GetDateTimeVector2(); + Windows::Foundation::Collections::IVector GetClassVector2(); + Windows::Foundation::Collections::IVector GetExceptionVector2(); Windows::Foundation::Collections::IMap GetIntToIntDictionary(); Windows::Foundation::Collections::IMap GetStringToBlittableDictionary(); diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl index d56bdf92c..6bff13768 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl @@ -383,6 +383,9 @@ namespace TestComponentCSharp Windows.Foundation.Collections.IVector GetIntVector2(); Windows.Foundation.Collections.IVector GetBlittableStructVector2(); Windows.Foundation.Collections.IVector GetNonBlittableStructVector2(); + Windows.Foundation.Collections.IVector GetDateTimeVector2(); + Windows.Foundation.Collections.IVector GetClassVector2(); + Windows.Foundation.Collections.IVector GetExceptionVector2(); Windows.Foundation.Collections.IMap GetIntToIntDictionary(); Windows.Foundation.Collections.IMap GetStringToBlittableDictionary(); diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index a811b7de3..4eb8db4de 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -3860,6 +3860,72 @@ public void TestListOfTypes() Assert.AreEqual(2, types.Count); Assert.AreEqual(typeof(Class), types[0]); Assert.AreEqual(typeof(int?), types[1]); + + Type[] copied = new Type[3]; + types.CopyTo(copied, 1); + Assert.IsNull(copied[0]); + Assert.AreEqual(typeof(Class), copied[1]); + Assert.AreEqual(typeof(int?), copied[2]); + } + + [TestMethod] + public void NativeVectorCopyTo_ValueTypes() + { + IList ints = TestObject.GetIntVector2(); + int[] copiedInts = new int[ints.Count + 1]; + ints.CopyTo(copiedInts, 1); + CollectionAssert.AreEqual(new[] { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, copiedInts); + + IList blittableStructs = TestObject.GetBlittableStructVector2(); + ComposedBlittableStruct[] copiedBlittableStructs = new ComposedBlittableStruct[blittableStructs.Count]; + blittableStructs.CopyTo(copiedBlittableStructs, 0); + Assert.AreEqual(4, copiedBlittableStructs[4].blittable.i32); + + IList nonBlittableStructs = TestObject.GetNonBlittableStructVector2(); + ComposedNonBlittableStruct[] copiedNonBlittableStructs = new ComposedNonBlittableStruct[nonBlittableStructs.Count]; + nonBlittableStructs.CopyTo(copiedNonBlittableStructs, 0); + Assert.AreEqual("String1", copiedNonBlittableStructs[1].strings.str); + Assert.IsTrue(copiedNonBlittableStructs[2].bools.w); + + IList dateTimes = TestObject.GetDateTimeVector2(); + DateTimeOffset[] copiedDateTimes = new DateTimeOffset[dateTimes.Count]; + dateTimes.CopyTo(copiedDateTimes, 0); + Assert.AreEqual(TimeSpan.FromSeconds(1), copiedDateTimes[1] - copiedDateTimes[0]); + } + + [TestMethod] + public void NativeVectorCopyTo_ReferenceTypes() + { + IList classes = TestObject.GetClassVector2(); + Class[] copiedClasses = new Class[classes.Count + 1]; + classes.CopyTo(copiedClasses, 1); + Assert.IsNull(copiedClasses[0]); + Assert.IsNotNull(copiedClasses[1]); + Assert.IsNotNull(copiedClasses[2]); + + IList objects = TestObject.GetUriVectorAsIInspectableVector(); + object[] copiedObjects = new object[objects.Count]; + objects.CopyTo(copiedObjects, 0); + Assert.IsTrue(copiedObjects.All(static item => item is Uri)); + } + + [TestMethod] + public void NativeVectorCopyTo_NullableType() + { + IList nullableInts = TestObject.GetNullableIntList(); + int?[] copiedNullableInts = new int?[nullableInts.Count]; + nullableInts.CopyTo(copiedNullableInts, 0); + CollectionAssert.AreEqual(new int?[] { 1, null, 2 }, copiedNullableInts); + } + + [TestMethod] + public void NativeVectorCopyTo_ExceptionType() + { + IList exceptions = TestObject.GetExceptionVector2(); + Exception[] copiedExceptions = new Exception[exceptions.Count]; + exceptions.CopyTo(copiedExceptions, 0); + Assert.AreEqual(unchecked((int)0x80004005), copiedExceptions[0].HResult); + Assert.AreEqual(unchecked((int)0x80070057), copiedExceptions[1].HResult); } [TestMethod] diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs index 515977fb5..8bf475509 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IList1.cs @@ -154,6 +154,17 @@ public static void IVectorMethods( declaration: interopReferences.IVectorMethodsImpl1GetAt(elementType), method: getAtMethod); + // Define the 'GetMany' method + MethodDefinition getManyMethod = InteropMethodDefinitionFactory.IVectorMethods.GetMany( + listType: listType, + interopReferences: interopReferences, + emitState: emitState); + + // Add and implement the 'GetMany' method + vectorMethodsType.AddMethodImplementation( + declaration: interopReferences.IVectorMethodsImpl1GetMany(elementType), + method: getManyMethod); + // Define the 'SetAt' method MethodDefinition setAtMethod = InteropMethodDefinitionFactory.IVectorMethods.SetAt( listType: listType, diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs index 0ac7c2636..39e0723b3 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs @@ -21,6 +21,134 @@ internal partial class InteropMethodDefinitionFactory /// public static class IVectorMethods { + /// + /// Creates a for copying elements through IVector<T>.GetMany. + /// + public static MethodDefinition GetMany( + GenericInstanceTypeSignature listType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState) + { + TypeSignature elementType = listType.TypeArguments[0]; + + if (elementType.IsBlittable(interopReferences)) + { + return ForwardTo(interopReferences.IVectorMethodsGetManyBlittable(elementType)); + } + else if (elementType.IsTypeOfString()) + { + return ForwardTo(interopReferences.IVectorMethodsGetManyStrings); + } + else if (elementType.IsTypeOfObject()) + { + return ForwardTo(interopReferences.IVectorMethodsGetManyObjects); + } + else if (elementType.IsTypeOfType(interopReferences)) + { + return ForwardTo(interopReferences.IVectorMethodsGetManyTypes); + } + else if (elementType.IsTypeOfException(interopReferences)) + { + return ForwardTo(interopReferences.IVectorMethodsGetManyExceptions); + } + else if (elementType.IsConstructedKeyValuePairType(interopReferences)) + { + GenericInstanceTypeSignature keyValuePairType = (GenericInstanceTypeSignature)elementType; + TypeSignature elementMarshallerType = emitState + .LookupTypeDefinition(elementType, "ElementMarshaller") + .ToTypeSignature(); + + return ForwardTo(interopReferences.IVectorMethodsGetManyKeyValuePairs( + keyValuePairType.TypeArguments[0], + keyValuePairType.TypeArguments[1], + elementMarshallerType)); + } + else if (elementType.IsConstructedNullableValueType(interopReferences)) + { + GenericInstanceTypeSignature nullableType = (GenericInstanceTypeSignature)elementType; + TypeSignature elementMarshallerType = emitState + .LookupTypeDefinition(elementType, "ElementMarshaller") + .ToTypeSignature(); + + return ForwardTo(interopReferences.IVectorMethodsGetManyNullable( + nullableType.TypeArguments[0], + elementMarshallerType)); + } + else if (elementType.IsManagedValueType(interopReferences)) + { + TypeSignature elementMarshallerType = emitState + .LookupTypeDefinition(elementType, "ElementMarshaller") + .ToTypeSignature(); + + return ForwardTo(interopReferences.IVectorMethodsGetManyManagedValues( + elementType, + elementType.GetAbiType(interopReferences), + elementMarshallerType)); + } + else if (elementType.IsValueType) + { + TypeSignature elementMarshallerType = emitState + .LookupTypeDefinition(elementType, "ElementMarshaller") + .ToTypeSignature(); + + return ForwardTo(interopReferences.IVectorMethodsGetManyUnmanagedValues( + elementType, + elementType.GetAbiType(interopReferences), + elementMarshallerType)); + } + else if (!elementType.IsValueType && + !elementType.IsTypeOfObject() && + !elementType.IsTypeOfType(interopReferences) && + !elementType.IsTypeOfException(interopReferences)) + { + TypeSignature elementMarshallerType = emitState + .LookupTypeDefinition(elementType, "ElementMarshaller") + .ToTypeSignature(); + + return ForwardTo(interopReferences.IVectorMethodsGetManyReferences(elementType, elementMarshallerType)); + } + + return new MethodDefinition( + name: "GetMany"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: interopReferences.Int32, + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + elementType.MakeSzArrayType(), + interopReferences.Int32, + interopReferences.Int32])) + { + CilInstructions = + { + { Ldc_I4_0 }, + { Ret } + } + }; + + MethodDefinition ForwardTo(IMethodDescriptor targetMethod) => new( + name: "GetMany"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: interopReferences.Int32, + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + elementType.MakeSzArrayType(), + interopReferences.Int32, + interopReferences.Int32])) + { + CilInstructions = + { + { Ldarg_0 }, + { Ldarg_1 }, + { Ldarg_2 }, + { Ldarg_3 }, + { Call, targetMethod }, + { Ret } + } + }; + } + /// /// Creates a for the SetAt method for some IVector<T> interface. /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs index eca50d9d0..10e119260 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs @@ -46,7 +46,7 @@ public static TypeDefinition UnmanagedValueType( .IWindowsRuntimeUnmanagedValueTypeElementMarshaller2 .MakeGenericReferenceType([elementType, elementAbiType]); - return ElementMarshaller( + TypeDefinition elementMarshallerType = ElementMarshaller( elementType: elementType, interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToUnmanaged(elementType, elementAbiType), @@ -54,6 +54,15 @@ public static TypeDefinition UnmanagedValueType( interopDefinitions: interopDefinitions, interopReferences: interopReferences, emitState: emitState); + + AddConvertToManaged( + elementMarshallerType, + elementType, + elementAbiType, + interopReferences.IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), + emitState); + + return elementMarshallerType; } /// @@ -88,6 +97,13 @@ public static TypeDefinition ManagedValueType( interopReferences: interopReferences, emitState: emitState); + AddConvertToManaged( + elementMarshallerType, + elementType, + elementAbiType, + interopReferences.IWindowsRuntimeManagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), + emitState); + // Rewriting labels CilInstruction nop_dispose = new(Nop); @@ -147,7 +163,7 @@ public static TypeDefinition KeyValuePair( // Specialize if both type arguments are value types (same logic as in the array element marshaller) bool isValueType = keyType.IsValueType && valueType.IsValueType; - return ElementMarshaller( + TypeDefinition elementMarshallerType = ElementMarshaller( elementType: elementType, interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToUnmanaged(keyType, valueType), @@ -155,6 +171,16 @@ public static TypeDefinition KeyValuePair( interopDefinitions: interopDefinitions, interopReferences: interopReferences, emitState: emitState); + + AddConvertToManagedAndDispose( + elementMarshallerType, + elementType, + interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToManaged(keyType, valueType), + interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerDispose(keyType, valueType), + interopReferences, + emitState); + + return elementMarshallerType; } /// @@ -179,7 +205,7 @@ public static TypeDefinition NullableValueType( .IWindowsRuntimeNullableTypeElementMarshaller1 .MakeGenericReferenceType([underlyingType]); - return ElementMarshaller( + TypeDefinition elementMarshallerType = ElementMarshaller( elementType: elementType, interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeNullableTypeElementMarshallerConvertToUnmanaged(underlyingType), @@ -187,6 +213,16 @@ public static TypeDefinition NullableValueType( interopDefinitions: interopDefinitions, interopReferences: interopReferences, emitState: emitState); + + AddConvertToManagedAndDispose( + elementMarshallerType, + elementType, + interopReferences.IWindowsRuntimeNullableTypeElementMarshallerConvertToManaged(underlyingType), + interopReferences.IWindowsRuntimeNullableTypeElementMarshallerDispose(underlyingType), + interopReferences, + emitState); + + return elementMarshallerType; } /// @@ -210,7 +246,7 @@ public static TypeDefinition ReferenceType( .IWindowsRuntimeReferenceTypeElementMarshaller1 .MakeGenericReferenceType([elementType]); - return ElementMarshaller( + TypeDefinition elementMarshallerType = ElementMarshaller( elementType: elementType, interfaceType: interfaceType, convertToUnmanagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerConvertToUnmanaged(elementType), @@ -218,6 +254,81 @@ public static TypeDefinition ReferenceType( interopDefinitions: interopDefinitions, interopReferences: interopReferences, emitState: emitState); + + AddConvertToManagedAndDispose( + elementMarshallerType, + elementType, + interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerConvertToManaged(elementType), + interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerDispose(elementType), + interopReferences, + emitState); + + return elementMarshallerType; + } + + private static void AddConvertToManaged( + TypeDefinition elementMarshallerType, + TypeSignature elementType, + TypeSignature elementAbiType, + MemberReference interfaceMethod, + InteropGeneratorEmitState emitState) + { + CilInstruction nop_convertToManaged = new(Nop); + + MethodDefinition convertToManagedMethod = new( + name: "ConvertToManaged"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: elementType, + parameterTypes: [elementAbiType])) + { + CilInstructions = + { + { nop_convertToManaged }, + { Ret } + } + }; + + elementMarshallerType.AddMethodImplementation(interfaceMethod, convertToManagedMethod); + + emitState.TrackManagedParameterMethodRewrite( + parameterType: elementType, + method: convertToManagedMethod, + marker: nop_convertToManaged, + parameterIndex: 0); + } + + private static void AddConvertToManagedAndDispose( + TypeDefinition elementMarshallerType, + TypeSignature elementType, + MemberReference convertToManagedInterfaceMethod, + MemberReference disposeInterfaceMethod, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState) + { + AddConvertToManaged( + elementMarshallerType, + elementType, + interopReferences.Void.MakePointerType(), + convertToManagedInterfaceMethod, + emitState); + + MethodDefinition disposeMethod = new( + name: "Dispose"u8, + attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + signature: MethodSignature.CreateStatic( + returnType: interopReferences.Void, + parameterTypes: [interopReferences.Void.MakePointerType()])) + { + CilInstructions = + { + { Ldarg_0 }, + { Call, interopReferences.WindowsRuntimeUnknownMarshallerFree }, + { Ret } + } + }; + + elementMarshallerType.AddMethodImplementation(disposeInterfaceMethod, disposeMethod); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 9ec993d28..691963224 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -890,6 +890,11 @@ public InteropReferences( /// public TypeReference IVectorMethodsImpl1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsImpl`1"u8); + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethods. + /// + public TypeReference IVectorMethods => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethods"u8); + /// /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>. /// @@ -3152,6 +3157,32 @@ public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerConvertToUnm parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); } + /// + /// Gets the for IWindowsRuntimeReferenceTypeElementMarshaller<T>.ConvertToManaged. + /// + public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerConvertToManaged(TypeSignature elementType) + { + return IWindowsRuntimeReferenceTypeElementMarshaller1 + .MakeGenericReferenceType([elementType]) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + + /// + /// Gets the for IWindowsRuntimeReferenceTypeElementMarshaller<T>.Dispose. + /// + public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerDispose(TypeSignature elementType) + { + return IWindowsRuntimeReferenceTypeElementMarshaller1 + .MakeGenericReferenceType([elementType]) + .ToTypeDefOrRef() + .CreateMemberReference("Dispose"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeElementMarshaller<T, TAbi>.ConvertToUnmanaged. /// @@ -3167,6 +3198,16 @@ public MemberReference IWindowsRuntimeManagedValueTypeElementMarshallerConvertTo parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); } + public MemberReference IWindowsRuntimeManagedValueTypeElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeManagedValueTypeElementMarshaller2 + .MakeGenericReferenceType([elementType, abiType]) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 1)])); + } + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeElementMarshaller<T, TAbi>.Dispose. /// @@ -3197,6 +3238,16 @@ public MemberReference IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvert parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); } + public MemberReference IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) + { + return IWindowsRuntimeUnmanagedValueTypeElementMarshaller2 + .MakeGenericReferenceType([elementType, abiType]) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 0), + parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 1)])); + } + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeElementMarshaller<TKey, TValue>.ConvertToUnmanaged. /// @@ -3215,6 +3266,28 @@ public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertTo new GenericParameterSignature(GenericParameterType.Type, 1)])])); } + public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToManaged(TypeSignature keyType, TypeSignature valueType) + { + return IWindowsRuntimeKeyValuePairTypeElementMarshaller2 + .MakeGenericReferenceType([keyType, valueType]) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: KeyValuePair2.MakeGenericValueType([ + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)]), + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + + public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerDispose(TypeSignature keyType, TypeSignature valueType) + { + return IWindowsRuntimeKeyValuePairTypeElementMarshaller2 + .MakeGenericReferenceType([keyType, valueType]) + .ToTypeDefOrRef() + .CreateMemberReference("Dispose"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeNullableTypeElementMarshaller<T>.ConvertToUnmanaged. /// @@ -3229,6 +3302,26 @@ public MemberReference IWindowsRuntimeNullableTypeElementMarshallerConvertToUnma parameterTypes: [Nullable1.MakeGenericValueType([new GenericParameterSignature(GenericParameterType.Type, 0)])])); } + public MemberReference IWindowsRuntimeNullableTypeElementMarshallerConvertToManaged(TypeSignature underlyingType) + { + return IWindowsRuntimeNullableTypeElementMarshaller1 + .MakeGenericReferenceType([underlyingType]) + .ToTypeDefOrRef() + .CreateMemberReference("ConvertToManaged"u8, MethodSignature.CreateStatic( + returnType: Nullable1.MakeGenericValueType([new GenericParameterSignature(GenericParameterType.Type, 0)]), + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + + public MemberReference IWindowsRuntimeNullableTypeElementMarshallerDispose(TypeSignature underlyingType) + { + return IWindowsRuntimeNullableTypeElementMarshaller1 + .MakeGenericReferenceType([underlyingType]) + .ToTypeDefOrRef() + .CreateMemberReference("Dispose"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); + } + /// /// Gets the for WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeBlittableValueTypeArrayMarshaller<T>.ConvertToUnmanaged. /// @@ -4675,6 +4768,158 @@ public MemberReference IVectorMethodsImpl1GetAt(TypeSignature elementType) _corLibTypeFactory.UInt32])); } + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>.GetMany. + /// + public MemberReference IVectorMethodsImpl1GetMany(TypeSignature elementType) + { + return IVectorMethodsImpl1 + .MakeGenericReferenceType([elementType]) + .ToTypeDefOrRef() + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + new GenericParameterSignature(GenericParameterType.Type, 0).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + } + + /// + /// Gets the blittable IVectorMethods.GetMany<T> overload. + /// + public MethodSpecification IVectorMethodsGetManyBlittable(TypeSignature elementType) + { + return IVectorMethods + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 1, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([elementType]); + } + + /// + /// Gets the IVectorMethods.GetManyStrings method. + /// + public MemberReference IVectorMethodsGetManyStrings => field ??= IVectorMethods + .CreateMemberReference("GetManyStrings"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + _corLibTypeFactory.String.MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// + /// Gets the projected-reference IVectorMethods.GetManyReferences<T, TElementMarshaller> overload. + /// + public MethodSpecification IVectorMethodsGetManyReferences(TypeSignature elementType, TypeSignature elementMarshallerType) + { + return IVectorMethods + .CreateMemberReference("GetManyReferences"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 2, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([elementType, elementMarshallerType]); + } + + public MethodSpecification IVectorMethodsGetManyUnmanagedValues(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return IVectorMethods + .CreateMemberReference("GetManyUnmanagedValues"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 3, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([elementType, abiType, elementMarshallerType]); + } + + public MethodSpecification IVectorMethodsGetManyManagedValues(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + { + return IVectorMethods + .CreateMemberReference("GetManyManagedValues"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 3, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([elementType, abiType, elementMarshallerType]); + } + + public MethodSpecification IVectorMethodsGetManyNullable(TypeSignature underlyingType, TypeSignature elementMarshallerType) + { + return IVectorMethods + .CreateMemberReference("GetManyNullable"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 2, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Nullable1.MakeGenericValueType([new GenericParameterSignature(GenericParameterType.Method, 0)]).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([underlyingType, elementMarshallerType]); + } + + public MethodSpecification IVectorMethodsGetManyKeyValuePairs( + TypeSignature keyType, + TypeSignature valueType, + TypeSignature elementMarshallerType) + { + return IVectorMethods + .CreateMemberReference("GetManyKeyValuePairs"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + genericParameterCount: 3, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + KeyValuePair2.MakeGenericValueType([ + new GenericParameterSignature(GenericParameterType.Method, 0), + new GenericParameterSignature(GenericParameterType.Method, 1)]).MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])) + .MakeGenericInstanceMethod([keyType, valueType, elementMarshallerType]); + } + + public MemberReference IVectorMethodsGetManyObjects => field ??= IVectorMethods + .CreateMemberReference("GetManyObjects"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Object.MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + public MemberReference IVectorMethodsGetManyTypes => field ??= IVectorMethods + .CreateMemberReference("GetManyTypes"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Type.ToReferenceTypeSignature().MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + public MemberReference IVectorMethodsGetManyExceptions => field ??= IVectorMethods + .CreateMemberReference("GetManyExceptions"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Exception.ToReferenceTypeSignature().MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>.SetAt. /// diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index a4a7e5cff..ba69b1a49 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -2,8 +2,6 @@ // Licensed under the MIT License. using System; -using System.Buffers; -using WindowsRuntime.InteropServices.Marshalling; #pragma warning disable CS1573 @@ -94,81 +92,18 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, ArgumentException.ThrowInsufficientSpaceToCopyCollection(); } - if (typeof(T) == typeof(string) && count > 0) - { - CopyStrings(thisReference, (string[])(object)array, arrayIndex, count); - return; - } + int copied = count > 0 + ? TMethods.GetMany(thisReference, array, arrayIndex, count) + : 0; - // Copy all items into the target array, at the specified starting offset - for (int i = 0; i < count; i++) + // Some providers may return fewer items than requested. Preserve ICollection.CopyTo + // semantics by retrieving any remaining items individually. + for (int i = copied; i < count; i++) { array[i + arrayIndex] = Item(thisReference, i); } } - private static unsafe void CopyStrings(WindowsRuntimeObjectReference thisReference, string[] array, int arrayIndex, int count) - where TMethods : IVectorMethodsImpl - { - nint[]? rented = null; - Span handles = count <= 32 - ? stackalloc nint[count] - : (rented = ArrayPool.Shared.Rent(count)).AsSpan(0, count); - - uint copied = 0; - - try - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValueForCall(); - void* thisPtr = thisValue.GetThisPtrUnsafe(); - - fixed (nint* handlesPtr = handles) - { - while (copied < count) - { - uint currentCopied; - HRESULT hresult = ((delegate* unmanaged[MemberFunction])(*(void***)thisPtr)[16])( - thisPtr, - copied, - (uint)count - copied, - (void**)(handlesPtr + copied), - ¤tCopied); - - RestrictedErrorInfo.ThrowExceptionForHR(hresult); - - if (currentCopied == 0) - { - break; - } - - copied += currentCopied; - } - } - - for (int i = 0; i < copied; i++) - { - array[arrayIndex + i] = HStringMarshaller.ConvertToManaged((void*)handles[i]); - } - - for (int i = (int)copied; i < count; i++) - { - array[arrayIndex + i] = (string)(object)Item(thisReference, i)!; - } - } - finally - { - for (int i = 0; i < copied; i++) - { - HStringMarshaller.Free((void*)handles[i]); - } - - if (rented is not null) - { - ArrayPool.Shared.Return(rented); - } - } - } - /// /// The implementation to use. /// The instance to use to invoke the native method. diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs index 8a5eeaacf..7479c3a6a 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Runtime.CompilerServices; +using WindowsRuntime.InteropServices.Marshalling; namespace WindowsRuntime.InteropServices; @@ -11,6 +13,8 @@ namespace WindowsRuntime.InteropServices; [WindowsRuntimeImplementationOnlyMember] public static unsafe class IVectorMethods { + private const int GetManyBufferLength = 64; + /// /// Gets the number of items in the vector. /// @@ -23,6 +27,484 @@ public static uint Size(WindowsRuntimeObjectReference thisReference) return IVectorViewMethods.Size(thisReference); } + /// + /// Copies blittable elements from a vector through its GetMany ABI method. + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where T : unmanaged + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + int copied = 0; + + fixed (T* destination = &array[arrayIndex]) + { + while (copied < count) + { + uint requested = (uint)(count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, destination + copied, &actual)); + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies string elements from a vector through its GetMany ABI method. + /// + public static int GetManyStrings(WindowsRuntimeObjectReference thisReference, string[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span handles = stackalloc nint[GetManyBufferLength]; + int copied = 0; + + fixed (nint* handlesPtr = handles) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + handles.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, handlesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = HStringMarshaller.ConvertToManaged((void*)handles[i]); + } + } + finally + { + for (int i = 0; i < actual; i++) + { + HStringMarshaller.Free((void*)handles[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies projected reference elements from a vector through its GetMany ABI method. + /// + public static int GetManyReferences(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where T : class + where TElementMarshaller : IWindowsRuntimeReferenceTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc nint[GetManyBufferLength]; + int copied = 0; + + fixed (nint* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + nativeValues.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i])!; + } + } + finally + { + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose((void*)nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies ABI-transformed unmanaged values from a vector through its GetMany ABI method. + /// + public static int GetManyUnmanagedValues(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where T : unmanaged + where TAbi : unmanaged + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc TAbi[GetManyBufferLength]; + int copied = 0; + + fixed (TAbi* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies managed value types from a vector through its GetMany ABI method. + /// + public static int GetManyManagedValues(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where T : struct + where TAbi : unmanaged + where TElementMarshaller : IWindowsRuntimeManagedValueTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc TAbi[GetManyBufferLength]; + int copied = 0; + + fixed (TAbi* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + nativeValues.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); + } + } + finally + { + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose(nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies nullable values from a vector through its GetMany ABI method. + /// + public static int GetManyNullable(WindowsRuntimeObjectReference thisReference, T?[] array, int arrayIndex, int count) + where T : struct + where TElementMarshaller : IWindowsRuntimeNullableTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc nint[GetManyBufferLength]; + int copied = 0; + + fixed (nint* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + nativeValues.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i]); + } + } + finally + { + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose((void*)nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies key/value pairs from a vector through its GetMany ABI method. + /// + public static int GetManyKeyValuePairs( + WindowsRuntimeObjectReference thisReference, + System.Collections.Generic.KeyValuePair[] array, + int arrayIndex, + int count) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc nint[GetManyBufferLength]; + int copied = 0; + + fixed (nint* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + nativeValues.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i]); + } + } + finally + { + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose((void*)nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies object elements from a vector through its GetMany ABI method. + /// + public static int GetManyObjects(WindowsRuntimeObjectReference thisReference, object[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + Span nativeValues = stackalloc nint[GetManyBufferLength]; + int copied = 0; + + fixed (nint* nativeValuesPtr = nativeValues) + { + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + nativeValues.Clear(); + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = WindowsRuntimeObjectMarshaller.ConvertToManaged((void*)nativeValues[i])!; + } + } + finally + { + for (int i = 0; i < actual; i++) + { + WindowsRuntimeUnknownMarshaller.Free((void*)nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + } + + return copied; + } + + /// + /// Copies elements from a vector through its GetMany ABI method. + /// + public static int GetManyTypes(WindowsRuntimeObjectReference thisReference, Type[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ABI.System.Type* nativeValues = stackalloc ABI.System.Type[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = ABI.System.TypeMarshaller.ConvertToManaged(nativeValues[i])!; + } + } + finally + { + for (int i = 0; i < actual; i++) + { + ABI.System.TypeMarshaller.Dispose(nativeValues[i]); + } + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + + return copied; + } + + /// + /// Copies elements from a vector through its GetMany ABI method. + /// + public static int GetManyExceptions(WindowsRuntimeObjectReference thisReference, Exception[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ABI.System.Exception* nativeValues = stackalloc ABI.System.Exception[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint requested = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR( + InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); + + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = ABI.System.ExceptionMarshaller.ConvertToManaged(nativeValues[i])!; + } + + copied += (int)actual; + + if (actual < requested) + { + break; + } + } + + return copied; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static HRESULT InvokeGetMany(void* thisPtr, uint startIndex, uint capacity, void* items, uint* actual) + { + return ((delegate* unmanaged[MemberFunction])(*(void***)thisPtr)[16])( + thisPtr, + startIndex, + capacity, + items, + actual); + } + /// /// Removes the item at the specified index in the vector. /// diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs index bc240c276..16ff6a3d1 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs @@ -19,6 +19,11 @@ public interface IVectorMethodsImpl /// static abstract T GetAt(WindowsRuntimeObjectReference thisReference, uint index); + /// + /// Copies elements from the vector through its GetMany ABI method. + /// + static abstract int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count); + /// /// Sets the value at the specified index in the vector. /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs index 56bd5f1b8..6f63e91b9 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs @@ -11,7 +11,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// The type of the key. /// The type of the value. [WindowsRuntimeImplementationOnlyMember] -public interface IWindowsRuntimeKeyValuePairTypeElementMarshaller +public unsafe interface IWindowsRuntimeKeyValuePairTypeElementMarshaller { /// /// Marshals a type to its native Windows Runtime representation. @@ -19,4 +19,14 @@ public interface IWindowsRuntimeKeyValuePairTypeElementMarshaller /// The input value to marshal. /// The marshalled native value. static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(KeyValuePair value); + + /// + /// Converts an unmanaged key/value pair to its managed representation. + /// + static abstract KeyValuePair ConvertToManaged(void* value); + + /// + /// Releases an unmanaged key/value pair. + /// + static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs index 827dc6e6f..9d51845f0 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs @@ -20,6 +20,11 @@ public interface IWindowsRuntimeManagedValueTypeElementMarshaller /// The marshalled native value. static abstract TAbi ConvertToUnmanaged(T value); + /// + /// Marshals a native Windows Runtime value type to its managed representation. + /// + static abstract T ConvertToManaged(TAbi value); + /// /// Disposes resources associated with an unmanaged value. /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs index 7b8c2f794..6d6ed00ac 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs @@ -10,7 +10,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// /// The underlying value type of the nullable type. [WindowsRuntimeImplementationOnlyMember] -public interface IWindowsRuntimeNullableTypeElementMarshaller +public unsafe interface IWindowsRuntimeNullableTypeElementMarshaller where T : struct { /// @@ -19,4 +19,14 @@ public interface IWindowsRuntimeNullableTypeElementMarshaller /// The input value to marshal. /// The marshalled native value. static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(T? value); + + /// + /// Converts an unmanaged nullable value to its managed representation. + /// + static abstract T? ConvertToManaged(void* value); + + /// + /// Releases an unmanaged nullable value. + /// + static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs index d7516ed65..a12f029f2 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs @@ -8,7 +8,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// /// The type of elements in the array. [WindowsRuntimeImplementationOnlyMember] -public interface IWindowsRuntimeReferenceTypeElementMarshaller +public unsafe interface IWindowsRuntimeReferenceTypeElementMarshaller where T : class { /// @@ -17,4 +17,14 @@ public interface IWindowsRuntimeReferenceTypeElementMarshaller /// The input object to marshal. /// A instance for . static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(T? value); + + /// + /// Converts an unmanaged pointer to a managed collection element. + /// + static abstract T? ConvertToManaged(void* value); + + /// + /// Releases an unmanaged collection element. + /// + static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs index bfb03fcb8..1f390a314 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs @@ -19,4 +19,9 @@ public interface IWindowsRuntimeUnmanagedValueTypeElementMarshaller /// The input value to marshal. /// The marshalled native value. static abstract TAbi ConvertToUnmanaged(T value); + + /// + /// Marshals a native Windows Runtime value type to its managed representation. + /// + static abstract T ConvertToManaged(TAbi value); } From ec453d3429aafac0896dc0d2082efdc4ad96c4f3 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 14:53:02 -0700 Subject: [PATCH 03/11] Address GetMany review feedback Validate native counts, preserve E_NOTIMPL fallback compatibility, correct GetMany vtable signatures, and add chunk-boundary string coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Tests/TestComponentCSharp/Class.cpp | 13 +++++++++ src/Tests/TestComponentCSharp/Class.h | 1 + .../TestComponentCSharp.idl | 1 + .../UnitTest/TestComponentCSharp_Tests.cs | 13 +++++++++ .../WellKnownTypeSignatureFactory.cs | 3 ++- .../Collections/IListMethods{T}.cs | 16 ++++++++--- .../Collections/IVectorMethods.cs | 27 ++++++++++++++----- .../InteropServices/Vtables/IVectorVftbl.cs | 2 +- .../Vtables/IVectorViewVftbl.cs | 2 +- 9 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp index 012326e4e..0fb03b9e3 100644 --- a/src/Tests/TestComponentCSharp/Class.cpp +++ b/src/Tests/TestComponentCSharp/Class.cpp @@ -1203,6 +1203,19 @@ namespace winrt::TestComponentCSharp::implementation }); } + IVector Class::GetStringVector2() + { + std::vector values; + values.reserve(130); + + for (int32_t i = 0; i < 130; i++) + { + values.push_back(to_hstring(i)); + } + + return winrt::single_threaded_vector(std::move(values)); + } + IVector Class::GetDateTimeVector2() { auto now = winrt::clock::now(); diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h index 51578241a..67b02198b 100644 --- a/src/Tests/TestComponentCSharp/Class.h +++ b/src/Tests/TestComponentCSharp/Class.h @@ -292,6 +292,7 @@ namespace winrt::TestComponentCSharp::implementation Windows::Foundation::Collections::IVector GetIntVector2(); Windows::Foundation::Collections::IVector GetBlittableStructVector2(); Windows::Foundation::Collections::IVector GetNonBlittableStructVector2(); + Windows::Foundation::Collections::IVector GetStringVector2(); Windows::Foundation::Collections::IVector GetDateTimeVector2(); Windows::Foundation::Collections::IVector GetClassVector2(); Windows::Foundation::Collections::IVector GetExceptionVector2(); diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl index 6bff13768..f031226a9 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl @@ -383,6 +383,7 @@ namespace TestComponentCSharp Windows.Foundation.Collections.IVector GetIntVector2(); Windows.Foundation.Collections.IVector GetBlittableStructVector2(); Windows.Foundation.Collections.IVector GetNonBlittableStructVector2(); + Windows.Foundation.Collections.IVector GetStringVector2(); Windows.Foundation.Collections.IVector GetDateTimeVector2(); Windows.Foundation.Collections.IVector GetClassVector2(); Windows.Foundation.Collections.IVector GetExceptionVector2(); diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index 4eb8db4de..127df9538 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -3909,6 +3909,19 @@ public void NativeVectorCopyTo_ReferenceTypes() Assert.IsTrue(copiedObjects.All(static item => item is Uri)); } + [TestMethod] + public void NativeVectorCopyTo_StringTypeAcrossChunks() + { + IList strings = TestObject.GetStringVector2(); + string[] copiedStrings = new string[strings.Count + 2]; + strings.CopyTo(copiedStrings, 1); + Assert.IsNull(copiedStrings[0]); + Assert.AreEqual("0", copiedStrings[1]); + Assert.AreEqual("64", copiedStrings[65]); + Assert.AreEqual("129", copiedStrings[130]); + Assert.IsNull(copiedStrings[131]); + } + [TestMethod] public void NativeVectorCopyTo_NullableType() { diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs index cddde51aa..53dccb3d7 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs @@ -393,7 +393,7 @@ public static MethodSignature IReadOnlyList1IndexOfImpl(TypeSignature elementTyp /// The resulting instance. public static MethodSignature IReadOnlyList1GetManyImpl(TypeSignature elementType, InteropReferences interopReferences) { - // Signature for 'delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany' + // Signature for 'delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany' return new( attributes: CallingConventionAttributes.Unmanaged, returnType: new CustomModifierTypeSignature( @@ -403,6 +403,7 @@ public static MethodSignature IReadOnlyList1GetManyImpl(TypeSignature elementTyp parameterTypes: [ interopReferences.Void.MakePointerType(), interopReferences.UInt32, + interopReferences.UInt32, elementType.MakePointerType(), interopReferences.UInt32.MakePointerType()]); } diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index ba69b1a49..7f961ccb9 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -92,9 +92,19 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, ArgumentException.ThrowInsufficientSpaceToCopyCollection(); } - int copied = count > 0 - ? TMethods.GetMany(thisReference, array, arrayIndex, count) - : 0; + int copied = 0; + + if (count > 0) + { + try + { + copied = TMethods.GetMany(thisReference, array, arrayIndex, count); + } + catch (Exception e) when (e.HResult == WellKnownErrorCodes.E_NOTIMPL) + { + // Preserve compatibility with providers that implement GetAt but not GetMany. + } + } // Some providers may return fewer items than requested. Preserve ICollection.CopyTo // semantics by retrieving any remaining items individually. diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs index 7479c3a6a..5c7211691 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs @@ -48,6 +48,7 @@ public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] ar RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, destination + copied, &actual)); + actual = uint.Min(actual, requested); copied += (int)actual; if (actual < requested) @@ -83,6 +84,7 @@ public static int GetManyStrings(WindowsRuntimeObjectReference thisReference, st RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, handlesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -135,6 +137,7 @@ public static int GetManyReferences(WindowsRuntimeObjectR RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -186,6 +189,7 @@ public static int GetManyUnmanagedValues(WindowsRun RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); for (int i = 0; i < actual; i++) { array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); @@ -229,6 +233,7 @@ public static int GetManyManagedValues(WindowsRunti RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -281,6 +286,7 @@ public static int GetManyNullable(WindowsRuntimeObjectRef RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -336,6 +342,7 @@ public static int GetManyKeyValuePairs( RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -386,6 +393,7 @@ public static int GetManyObjects(WindowsRuntimeObjectReference thisReference, ob RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -429,9 +437,15 @@ public static int GetManyTypes(WindowsRuntimeObjectReference thisReference, Type uint requested = (uint)int.Min(GetManyBufferLength, count - copied); uint actual; + for (int i = 0; i < requested; i++) + { + nativeValues[i] = default; + } + RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); + actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -474,9 +488,15 @@ public static int GetManyExceptions(WindowsRuntimeObjectReference thisReference, uint requested = (uint)int.Min(GetManyBufferLength, count - copied); uint actual; + for (int i = 0; i < requested; i++) + { + nativeValues[i] = default; + } + RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); + actual = uint.Min(actual, requested); for (int i = 0; i < actual; i++) { array[arrayIndex + copied + i] = ABI.System.ExceptionMarshaller.ConvertToManaged(nativeValues[i])!; @@ -497,12 +517,7 @@ public static int GetManyExceptions(WindowsRuntimeObjectReference thisReference, [MethodImpl(MethodImplOptions.AggressiveInlining)] private static HRESULT InvokeGetMany(void* thisPtr, uint startIndex, uint capacity, void* items, uint* actual) { - return ((delegate* unmanaged[MemberFunction])(*(void***)thisPtr)[16])( - thisPtr, - startIndex, - capacity, - items, - actual); + return ((IVectorVftbl*)*(void***)thisPtr)->GetMany(thisPtr, startIndex, capacity, items, actual); } /// diff --git a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs index 7f0150e3f..303cabe49 100644 --- a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs +++ b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs @@ -32,6 +32,6 @@ internal unsafe struct IVectorVftbl public delegate* unmanaged[MemberFunction] Append; public delegate* unmanaged[MemberFunction] RemoveAtEnd; public delegate* unmanaged[MemberFunction] Clear; - public delegate* unmanaged[MemberFunction] GetMany; + public delegate* unmanaged[MemberFunction] GetMany; public delegate* unmanaged[MemberFunction] ReplaceAll; } \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorViewVftbl.cs b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorViewVftbl.cs index 1a7952888..52e4d415b 100644 --- a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorViewVftbl.cs +++ b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorViewVftbl.cs @@ -28,5 +28,5 @@ internal unsafe struct IVectorViewVftbl // does not matter, since this vtable slot is never actually used within this assembly. It is only // used from 'WinRT.Interop.dll', which will emit specialized vtable types when necessary. public delegate* unmanaged[MemberFunction] IndexOf; - public delegate* unmanaged[MemberFunction] GetMany; + public delegate* unmanaged[MemberFunction] GetMany; } \ No newline at end of file From 43ddebe946b1d24ac252d0392440a0a5ffda47df Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 15:17:58 -0700 Subject: [PATCH 04/11] Correct GetMany ABI signature comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Factories/WellKnownTypeDefinitionFactory.cs | 2 +- .../Factories/WellKnownTypeSignatureFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs index d0bdb60a0..6bf8e846c 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs @@ -416,7 +416,7 @@ public static TypeDefinition IReadOnlyList1Vftbl( // public delegate* unmanaged[MemberFunction]*, HRESULT> GetAt; // public delegate* unmanaged[MemberFunction] get_Size; // public delegate* unmanaged[MemberFunction], uint*, HRESULT> IndexOf; - // public delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany; + // public delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany; vftblType.Fields.Add(new FieldDefinition("QueryInterface"u8, FieldAttributes.Public, queryInterfaceType.MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("AddRef"u8, FieldAttributes.Public, addRefType.MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("Release"u8, FieldAttributes.Public, releaseType.MakeFunctionPointerType())); diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs index 53dccb3d7..2fef6145b 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs @@ -582,7 +582,7 @@ public static MethodSignature IList1ClearImpl(InteropReferences interopReference /// The resulting instance. public static MethodSignature IList1GetManyImpl(TypeSignature elementType, InteropReferences interopReferences) { - // Signature for 'delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany'. + // Signature for 'delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany'. // This is the same as 'IVectorView.GetMany', so we can reuse that one here (like the methods above). return IReadOnlyList1GetManyImpl(elementType, interopReferences); } From aa6c0f90caed5924f37a63b9594593a9ddc1bf58 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 15:20:55 -0700 Subject: [PATCH 05/11] Remove speculative GetMany fallback No known conforming IVector implementation returns E_NOTIMPL from GetMany, so preserve normal failure propagation rather than masking incomplete providers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Collections/IListMethods{T}.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index 7f961ccb9..ba69b1a49 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -92,19 +92,9 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, ArgumentException.ThrowInsufficientSpaceToCopyCollection(); } - int copied = 0; - - if (count > 0) - { - try - { - copied = TMethods.GetMany(thisReference, array, arrayIndex, count); - } - catch (Exception e) when (e.HResult == WellKnownErrorCodes.E_NOTIMPL) - { - // Preserve compatibility with providers that implement GetAt but not GetMany. - } - } + int copied = count > 0 + ? TMethods.GetMany(thisReference, array, arrayIndex, count) + : 0; // Some providers may return fewer items than requested. Preserve ICollection.CopyTo // semantics by retrieving any remaining items individually. From a4d6a7d3b37166c88ea52ab592b7f5e314d0b0ef Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 15:29:25 -0700 Subject: [PATCH 06/11] Trust GetMany returned count contract Remove defensive count clamping because it cannot validate or repair a malformed native provider and conforming implementations must return at most the requested capacity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../InteropServices/Collections/IVectorMethods.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs index 5c7211691..084bdfa51 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs @@ -48,7 +48,6 @@ public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] ar RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, destination + copied, &actual)); - actual = uint.Min(actual, requested); copied += (int)actual; if (actual < requested) @@ -84,7 +83,6 @@ public static int GetManyStrings(WindowsRuntimeObjectReference thisReference, st RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, handlesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -137,7 +135,6 @@ public static int GetManyReferences(WindowsRuntimeObjectR RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -189,7 +186,6 @@ public static int GetManyUnmanagedValues(WindowsRun RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); for (int i = 0; i < actual; i++) { array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); @@ -233,7 +229,6 @@ public static int GetManyManagedValues(WindowsRunti RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -286,7 +281,6 @@ public static int GetManyNullable(WindowsRuntimeObjectRef RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -342,7 +336,6 @@ public static int GetManyKeyValuePairs( RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -393,7 +386,6 @@ public static int GetManyObjects(WindowsRuntimeObjectReference thisReference, ob RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -445,7 +437,6 @@ public static int GetManyTypes(WindowsRuntimeObjectReference thisReference, Type RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); - actual = uint.Min(actual, requested); try { for (int i = 0; i < actual; i++) @@ -496,7 +487,6 @@ public static int GetManyExceptions(WindowsRuntimeObjectReference thisReference, RestrictedErrorInfo.ThrowExceptionForHR( InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); - actual = uint.Min(actual, requested); for (int i = 0; i < actual; i++) { array[arrayIndex + copied + i] = ABI.System.ExceptionMarshaller.ConvertToManaged(nativeValues[i])!; From ee3d0844dd3913fa2b6b2f1dd51a6ca8350aed30 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 18:28:49 -0700 Subject: [PATCH 07/11] Optimize blittable CCW GetMany copies Use span copies for managed arrays and List exposed through IVector, while retaining the indexer fallback for arbitrary IList implementations. Add coverage for offsets, limited capacity, empty requests, and each implementation path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Tests/TestComponentCSharp/Class.cpp | 14 ++++++++++++++ src/Tests/TestComponentCSharp/Class.h | 1 + .../TestComponentCSharp/TestComponentCSharp.idl | 1 + src/Tests/UnitTest/TestComponentCSharp_Tests.cs | 16 ++++++++++++++++ .../Collections/IListAdapterExtensions.cs | 17 +++++++++++++++++ 5 files changed, 49 insertions(+) diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp index 0fb03b9e3..1adf0c363 100644 --- a/src/Tests/TestComponentCSharp/Class.cpp +++ b/src/Tests/TestComponentCSharp/Class.cpp @@ -2067,6 +2067,20 @@ namespace winrt::TestComponentCSharp::implementation return sum; } + int64_t Class::SumIntsWithGetMany(IVector const& values, uint32_t startIndex, uint32_t capacity) + { + std::vector items(capacity); + uint32_t retrieved = values.GetMany(startIndex, items); + int64_t sum = 0; + + for (uint32_t i = 0; i < retrieved; i++) + { + sum += items[i]; + } + + return sum; + } + int32_t Class::CountKeyValuePairsWithGetMany(winrt::Windows::Foundation::Collections::IIterable> const& pairs) { auto iterator = pairs.First(); diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h index 67b02198b..9c93e49f3 100644 --- a/src/Tests/TestComponentCSharp/Class.h +++ b/src/Tests/TestComponentCSharp/Class.h @@ -431,6 +431,7 @@ namespace winrt::TestComponentCSharp::implementation double Calculate(winrt::Windows::Foundation::Collections::IVector> const& values); winrt::Windows::Foundation::Collections::IVector> GetNullableIntList(); int32_t SumNullableIntsWithGetMany(winrt::Windows::Foundation::Collections::IVector> const& values); + int64_t SumIntsWithGetMany(winrt::Windows::Foundation::Collections::IVector const& values, uint32_t startIndex, uint32_t capacity); int32_t CountKeyValuePairsWithGetMany(winrt::Windows::Foundation::Collections::IIterable> const& pairs); static int GetPropertyType(Windows::Foundation::IInspectable const& obj); diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl index f031226a9..f8f01290d 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl @@ -484,6 +484,7 @@ namespace TestComponentCSharp Double Calculate(Windows.Foundation.Collections.IVector > values); Windows.Foundation.Collections.IVector > GetNullableIntList(); Int32 SumNullableIntsWithGetMany(Windows.Foundation.Collections.IVector > values); + Int64 SumIntsWithGetMany(Windows.Foundation.Collections.IVector values, UInt32 startIndex, UInt32 capacity); Int32 CountKeyValuePairsWithGetMany(Windows.Foundation.Collections.IIterable > pairs); // Boxing diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index 127df9538..ea138d10c 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -3941,6 +3941,22 @@ public void NativeVectorCopyTo_ExceptionType() Assert.AreEqual(unchecked((int)0x80070057), copiedExceptions[1].HResult); } + [TestMethod] + public void ManagedVectorGetMany_BlittableFastPathsAndFallback() + { + int[] array = [10, 20, 30, 40, 50]; + Assert.AreEqual(90L, TestObject.SumIntsWithGetMany(array, 1, 3)); + + List list = [10, 20, 30, 40, 50]; + Assert.AreEqual(90L, TestObject.SumIntsWithGetMany(list, 1, 3)); + + Collection collection = [10, 20, 30, 40, 50]; + Assert.AreEqual(90L, TestObject.SumIntsWithGetMany(collection, 1, 3)); + + Assert.AreEqual(0L, TestObject.SumIntsWithGetMany(array, (uint)array.Length, 3)); + Assert.AreEqual(0L, TestObject.SumIntsWithGetMany(array, 0, 0)); + } + [TestMethod] public void PrimitiveTypeInfo() { diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListAdapterExtensions.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListAdapterExtensions.cs index ddd8fe06d..46c90b1d3 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListAdapterExtensions.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListAdapterExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using WindowsRuntime.InteropServices.Marshalling; namespace WindowsRuntime.InteropServices; @@ -249,6 +250,22 @@ public static unsafe uint GetMany(IList list, uint startIndex, uint itemsSize int itemCount = int.Min((int)itemsSize, count - (int)startIndex); + Span destination = new(items, itemCount); + + if (list is T[] array) + { + array.AsSpan((int)startIndex, itemCount).CopyTo(destination); + + return (uint)itemCount; + } + + if (list is List concreteList) + { + CollectionsMarshal.AsSpan(concreteList).Slice((int)startIndex, itemCount).CopyTo(destination); + + return (uint)itemCount; + } + for (int i = 0; i < itemCount; i++) { items[i] = list[i + (int)startIndex]; From 367fe1db074af888b2ee4971631be37d589829be Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 11 Aug 2026 18:51:41 -0700 Subject: [PATCH 08/11] Stage managed GetMany benchmark Keep the TestWinRT-dependent benchmark call commented until the corresponding native benchmark API is available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Benchmarks/Benchmarks/CollectionsPerf.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Benchmarks/Benchmarks/CollectionsPerf.cs b/src/Benchmarks/Benchmarks/CollectionsPerf.cs index 9cc7d02c9..ad766740b 100644 --- a/src/Benchmarks/Benchmarks/CollectionsPerf.cs +++ b/src/Benchmarks/Benchmarks/CollectionsPerf.cs @@ -19,6 +19,8 @@ public class CollectionsPerf private IList vector; private IList bulkVector; private int[] bulkBuffer; + private int[] managedBulkVector; + private ClassWithMarshalingRoutines instance; private IList bulkStringVector; private string[] bulkStringBuffer; private IDictionary stringMap; @@ -35,11 +37,18 @@ public class CollectionsPerf [GlobalSetup] public void Setup() { - ClassWithMarshalingRoutines instance = new(); + instance = new(); vector = instance.Items(VectorLen); bulkVector = instance.Items(BulkCount); bulkBuffer = new int[BulkCount]; + managedBulkVector = new int[BulkCount]; + for (int i = 0; i < BulkCount; i++) + { + managedBulkVector[i] = i; + } + // Will be uncommented once the TestWinRT change is done. + // _ = instance.GetManyFromManagedList(managedBulkVector); bulkStringVector = instance.NewList(); bulkStringBuffer = new string[BulkCount]; for (int i = 0; i < BulkCount; i++) @@ -97,6 +106,13 @@ public void GetMany() bulkVector.CopyTo(bulkBuffer, 0); } + // Will be uncommented once the TestWinRT change is done. + // [Benchmark(OperationsPerInvoke = BulkCount)] + // public uint GetManyFromManagedList() + // { + // return instance.GetManyFromManagedList(managedBulkVector); + // } + [Benchmark(OperationsPerInvoke = BulkCount)] public void GetManyStrings() { From e9d832547b237422730d16de570c9358d625f188 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Wed, 12 Aug 2026 19:29:50 -0700 Subject: [PATCH 09/11] Add ToArray collection benchmarks Cover writable vectors and vector views for blittable, string, and projected object elements so GetMany improvements through LINQ ToArray remain measurable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Benchmarks/Benchmarks/CollectionsPerf.cs | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Benchmarks/Benchmarks/CollectionsPerf.cs b/src/Benchmarks/Benchmarks/CollectionsPerf.cs index ad766740b..2b9261cf3 100644 --- a/src/Benchmarks/Benchmarks/CollectionsPerf.cs +++ b/src/Benchmarks/Benchmarks/CollectionsPerf.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using BenchmarkComponent; using BenchmarkDotNet.Attributes; @@ -25,6 +26,7 @@ public class CollectionsPerf private string[] bulkStringBuffer; private IDictionary stringMap; private IReadOnlyList vectorView; + private IReadOnlyList bulkVectorView; private IReadOnlyDictionary mapView; private IList objectVector; @@ -32,6 +34,7 @@ public class CollectionsPerf private WrappedClass[] bulkObjectBuffer; private IDictionary objectMap; private IReadOnlyList objectVectorView; + private IReadOnlyList bulkObjectVectorView; private IReadOnlyDictionary objectMapView; [GlobalSetup] @@ -57,6 +60,7 @@ public void Setup() } stringMap = instance.StringMap(MapLen); vectorView = instance.ItemsView(VectorLen); + bulkVectorView = instance.ItemsView(BulkCount); mapView = instance.MapView(MapLen); objectVector = instance.ObjectItems(VectorLen); @@ -64,6 +68,7 @@ public void Setup() bulkObjectBuffer = new WrappedClass[BulkCount]; objectMap = instance.ObjectMap(MapLen); objectVectorView = instance.ObjectItemsView(VectorLen); + bulkObjectVectorView = instance.ObjectItemsView(BulkCount); objectMapView = instance.ObjectMapView(MapLen); } @@ -125,6 +130,36 @@ public void GetManyObjects() bulkObjectVector.CopyTo(bulkObjectBuffer, 0); } + [Benchmark(OperationsPerInvoke = BulkCount)] + public int[] ToArray() + { + return bulkVector.ToArray(); + } + + [Benchmark(OperationsPerInvoke = BulkCount)] + public string[] ToArrayStrings() + { + return bulkStringVector.ToArray(); + } + + [Benchmark(OperationsPerInvoke = BulkCount)] + public WrappedClass[] ToArrayObjects() + { + return bulkObjectVector.ToArray(); + } + + [Benchmark(OperationsPerInvoke = BulkCount)] + public int[] ToArrayView() + { + return bulkVectorView.ToArray(); + } + + [Benchmark(OperationsPerInvoke = BulkCount)] + public WrappedClass[] ToArrayViewObjects() + { + return bulkObjectVectorView.ToArray(); + } + [Benchmark(OperationsPerInvoke = MapLen)] public int Map() { From 9c85b8dfcd320877d7dba5c0af96e8c2ff4105c6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 13 Aug 2026 14:11:43 -0700 Subject: [PATCH 10/11] Clean up the new GetMany helpers for codebase consistency Move the 'GetMany' family out of 'IVectorMethods' and into specialized extension types in a new 'IVectorMethodsExtensions' file, mirroring the existing 'IListAdapterExtensions' and 'IEnumeratorAdapterExtensions' files, so that all specializations expose a uniform 'GetMany' member. Add a 'GetManyUnsafe' helper to 'IVectorVftbl', matching the '*Unsafe' convention used by all the other vtable types, and use it from all the 'GetMany' specializations. Update the interop generator to match the existing patterns: the element type dispatch in 'InteropMethodDefinitionFactory.IVectorMethods.GetMany' now uses the same switch expression shape as 'IList1Impl.GetMany', and the new 'InteropReferences' members follow the naming and documentation of the equivalent 'IListAdapter*' ones. Also simplify 'IListMethods.CopyTo' with an early return, and complete the XML docs on all the new interface and reference members. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9216861-f68c-437e-8f82-776d072bac2a --- ...pMethodDefinitionFactory.IVectorMethods.cs | 132 ++--- ...ionFactory.IEnumeratorElementMarshaller.cs | 126 +++-- .../References/InteropReferences.cs | 244 ++++++--- .../Collections/IListMethods{T}.cs | 14 +- .../Collections/IVectorMethods.cs | 487 ----------------- .../Collections/IVectorMethodsExtensions.cs | 513 ++++++++++++++++++ .../Collections/IVectorMethodsImpl{T}.cs | 8 +- ...PairTypeElementMarshaller{TKey, TValue}.cs | 9 +- ...agedValueTypeElementMarshaller{T, TAbi}.cs | 4 +- ...RuntimeNullableTypeElementMarshaller{T}.cs | 9 +- ...untimeReferenceTypeElementMarshaller{T}.cs | 9 +- ...agedValueTypeElementMarshaller{T, TAbi}.cs | 4 +- .../InteropServices/Vtables/IVectorVftbl.cs | 16 + 13 files changed, 855 insertions(+), 720 deletions(-) create mode 100644 src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs index 39e0723b3..77c133a65 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorMethods.cs @@ -22,8 +22,11 @@ internal partial class InteropMethodDefinitionFactory public static class IVectorMethods { /// - /// Creates a for copying elements through IVector<T>.GetMany. + /// Creates a for the GetMany method for some IVector<T> interface. /// + /// The for the type. + /// The instance to use. + /// The emit state for this invocation. public static MethodDefinition GetMany( GenericInstanceTypeSignature listType, InteropReferences interopReferences, @@ -31,102 +34,38 @@ public static MethodDefinition GetMany( { TypeSignature elementType = listType.TypeArguments[0]; - if (elementType.IsBlittable(interopReferences)) - { - return ForwardTo(interopReferences.IVectorMethodsGetManyBlittable(elementType)); - } - else if (elementType.IsTypeOfString()) - { - return ForwardTo(interopReferences.IVectorMethodsGetManyStrings); - } - else if (elementType.IsTypeOfObject()) - { - return ForwardTo(interopReferences.IVectorMethodsGetManyObjects); - } - else if (elementType.IsTypeOfType(interopReferences)) - { - return ForwardTo(interopReferences.IVectorMethodsGetManyTypes); - } - else if (elementType.IsTypeOfException(interopReferences)) - { - return ForwardTo(interopReferences.IVectorMethodsGetManyExceptions); - } - else if (elementType.IsConstructedKeyValuePairType(interopReferences)) + // Get the appropriate 'GetMany' method descriptor for 'IVector' types + IMethodDescriptor getManyMethod = elementType switch { - GenericInstanceTypeSignature keyValuePairType = (GenericInstanceTypeSignature)elementType; - TypeSignature elementMarshallerType = emitState - .LookupTypeDefinition(elementType, "ElementMarshaller") - .ToTypeSignature(); - - return ForwardTo(interopReferences.IVectorMethodsGetManyKeyValuePairs( - keyValuePairType.TypeArguments[0], - keyValuePairType.TypeArguments[1], - elementMarshallerType)); - } - else if (elementType.IsConstructedNullableValueType(interopReferences)) - { - GenericInstanceTypeSignature nullableType = (GenericInstanceTypeSignature)elementType; - TypeSignature elementMarshallerType = emitState - .LookupTypeDefinition(elementType, "ElementMarshaller") - .ToTypeSignature(); - - return ForwardTo(interopReferences.IVectorMethodsGetManyNullable( - nullableType.TypeArguments[0], - elementMarshallerType)); - } - else if (elementType.IsManagedValueType(interopReferences)) - { - TypeSignature elementMarshallerType = emitState - .LookupTypeDefinition(elementType, "ElementMarshaller") - .ToTypeSignature(); - - return ForwardTo(interopReferences.IVectorMethodsGetManyManagedValues( - elementType, - elementType.GetAbiType(interopReferences), - elementMarshallerType)); - } - else if (elementType.IsValueType) - { - TypeSignature elementMarshallerType = emitState - .LookupTypeDefinition(elementType, "ElementMarshaller") - .ToTypeSignature(); - - return ForwardTo(interopReferences.IVectorMethodsGetManyUnmanagedValues( - elementType, - elementType.GetAbiType(interopReferences), - elementMarshallerType)); - } - else if (!elementType.IsValueType && - !elementType.IsTypeOfObject() && - !elementType.IsTypeOfType(interopReferences) && - !elementType.IsTypeOfException(interopReferences)) - { - TypeSignature elementMarshallerType = emitState - .LookupTypeDefinition(elementType, "ElementMarshaller") - .ToTypeSignature(); - - return ForwardTo(interopReferences.IVectorMethodsGetManyReferences(elementType, elementMarshallerType)); - } - - return new MethodDefinition( - name: "GetMany"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: interopReferences.Int32, - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - elementType.MakeSzArrayType(), - interopReferences.Int32, - interopReferences.Int32])) - { - CilInstructions = - { - { Ldc_I4_0 }, - { Ret } - } + _ when elementType.IsBlittable(interopReferences) => interopReferences.IVectorMethodsBlittableValueTypeGetMany(elementType), + _ when elementType.IsConstructedKeyValuePairType(interopReferences) => interopReferences.IVectorMethodsKeyValuePairTypeGetMany( + keyType: ((GenericInstanceTypeSignature)elementType).TypeArguments[0], + valueType: ((GenericInstanceTypeSignature)elementType).TypeArguments[1], + elementMarshallerType: emitState.LookupTypeDefinition(elementType, "ElementMarshaller").ToTypeSignature()), + _ when elementType.IsConstructedNullableValueType(interopReferences) => interopReferences.IVectorMethodsNullableTypeGetMany( + underlyingType: ((GenericInstanceTypeSignature)elementType).TypeArguments[0], + elementMarshallerType: emitState.LookupTypeDefinition(elementType, "ElementMarshaller").ToTypeSignature()), + _ when elementType.IsManagedValueType(interopReferences) => interopReferences.IVectorMethodsManagedValueTypeGetMany( + elementType: elementType, + abiType: elementType.GetAbiType(interopReferences), + elementMarshallerType: emitState.LookupTypeDefinition(elementType, "ElementMarshaller").ToTypeSignature()), + _ when elementType.IsValueType => interopReferences.IVectorMethodsUnmanagedValueTypeGetMany( + elementType: elementType, + abiType: elementType.GetAbiType(interopReferences), + elementMarshallerType: emitState.LookupTypeDefinition(elementType, "ElementMarshaller").ToTypeSignature()), + _ when elementType.IsTypeOfObject() => interopReferences.IVectorMethodsOfObjectGetMany, + _ when elementType.IsTypeOfString() => interopReferences.IVectorMethodsOfStringGetMany, + _ when elementType.IsTypeOfType(interopReferences) => interopReferences.IVectorMethodsOfTypeGetMany, + _ when elementType.IsTypeOfException(interopReferences) => interopReferences.IVectorMethodsOfExceptionGetMany, + _ => interopReferences.IVectorMethodsReferenceTypeGetMany( + elementType: elementType, + elementMarshallerType: emitState.LookupTypeDefinition(elementType, "ElementMarshaller").ToTypeSignature()) }; - MethodDefinition ForwardTo(IMethodDescriptor targetMethod) => new( + // Define the 'GetMany' method as follows: + // + // public static int GetMany(WindowsRuntimeObjectReference thisReference, [] array, int arrayIndex, int count) + return new( name: "GetMany"u8, attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, signature: MethodSignature.CreateStatic( @@ -139,11 +78,12 @@ public static MethodDefinition GetMany( { CilInstructions = { + // return (thisReference, array, arrayIndex, count); { Ldarg_0 }, { Ldarg_1 }, { Ldarg_2 }, { Ldarg_3 }, - { Call, targetMethod }, + { Call, getManyMethod }, { Ret } } }; @@ -544,4 +484,4 @@ private static MethodDefinition SetAtOrInsertAt( return setAtOrInsertAtMethod; } } -} \ No newline at end of file +} diff --git a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs index 10e119260..3ad6112c7 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropTypeDefinitionFactory.IEnumeratorElementMarshaller.cs @@ -55,12 +55,13 @@ public static TypeDefinition UnmanagedValueType( interopReferences: interopReferences, emitState: emitState); - AddConvertToManaged( - elementMarshallerType, - elementType, - elementAbiType, - interopReferences.IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), - emitState); + // Add the 'ConvertToManaged' method (unmanaged value types have nothing to dispose) + ConvertToManaged( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + elementAbiType: elementAbiType, + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), + emitState: emitState); return elementMarshallerType; } @@ -97,12 +98,13 @@ public static TypeDefinition ManagedValueType( interopReferences: interopReferences, emitState: emitState); - AddConvertToManaged( - elementMarshallerType, - elementType, - elementAbiType, - interopReferences.IWindowsRuntimeManagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), - emitState); + // Add the 'ConvertToManaged' method + ConvertToManaged( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + elementAbiType: elementAbiType, + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeManagedValueTypeElementMarshallerConvertToManaged(elementType, elementAbiType), + emitState: emitState); // Rewriting labels CilInstruction nop_dispose = new(Nop); @@ -172,13 +174,14 @@ public static TypeDefinition KeyValuePair( interopReferences: interopReferences, emitState: emitState); - AddConvertToManagedAndDispose( - elementMarshallerType, - elementType, - interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToManaged(keyType, valueType), - interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerDispose(keyType, valueType), - interopReferences, - emitState); + // Add the 'ConvertToManaged' and 'Dispose' methods + ConvertToManagedAndDispose( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToManaged(keyType, valueType), + disposeInterfaceMethod: interopReferences.IWindowsRuntimeKeyValuePairTypeElementMarshallerDispose(keyType, valueType), + interopReferences: interopReferences, + emitState: emitState); return elementMarshallerType; } @@ -214,13 +217,14 @@ public static TypeDefinition NullableValueType( interopReferences: interopReferences, emitState: emitState); - AddConvertToManagedAndDispose( - elementMarshallerType, - elementType, - interopReferences.IWindowsRuntimeNullableTypeElementMarshallerConvertToManaged(underlyingType), - interopReferences.IWindowsRuntimeNullableTypeElementMarshallerDispose(underlyingType), - interopReferences, - emitState); + // Add the 'ConvertToManaged' and 'Dispose' methods + ConvertToManagedAndDispose( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeNullableTypeElementMarshallerConvertToManaged(underlyingType), + disposeInterfaceMethod: interopReferences.IWindowsRuntimeNullableTypeElementMarshallerDispose(underlyingType), + interopReferences: interopReferences, + emitState: emitState); return elementMarshallerType; } @@ -255,26 +259,39 @@ public static TypeDefinition ReferenceType( interopReferences: interopReferences, emitState: emitState); - AddConvertToManagedAndDispose( - elementMarshallerType, - elementType, - interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerConvertToManaged(elementType), - interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerDispose(elementType), - interopReferences, - emitState); + // Add the 'ConvertToManaged' and 'Dispose' methods + ConvertToManagedAndDispose( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + convertToManagedInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerConvertToManaged(elementType), + disposeInterfaceMethod: interopReferences.IWindowsRuntimeReferenceTypeElementMarshallerDispose(elementType), + interopReferences: interopReferences, + emitState: emitState); return elementMarshallerType; } - private static void AddConvertToManaged( + /// + /// Adds the ConvertToManaged method to an element marshaller type. + /// + /// The element marshaller type to add the method to. + /// The for the element type. + /// The ABI type for . + /// The ConvertToManaged interface method being implemented. + /// The emit state for this invocation. + private static void ConvertToManaged( TypeDefinition elementMarshallerType, TypeSignature elementType, TypeSignature elementAbiType, - MemberReference interfaceMethod, + MemberReference convertToManagedInterfaceMethod, InteropGeneratorEmitState emitState) { + // Rewriting labels CilInstruction nop_convertToManaged = new(Nop); + // Define the 'ConvertToManaged' method as follows: + // + // public static ConvertToManaged( value) MethodDefinition convertToManagedMethod = new( name: "ConvertToManaged"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, @@ -289,8 +306,12 @@ private static void AddConvertToManaged( } }; - elementMarshallerType.AddMethodImplementation(interfaceMethod, convertToManagedMethod); + // Add and implement the 'ConvertToManaged' method + elementMarshallerType.AddMethodImplementation( + declaration: convertToManagedInterfaceMethod, + method: convertToManagedMethod); + // Track rewriting the managed value for 'ConvertToManaged' emitState.TrackManagedParameterMethodRewrite( parameterType: elementType, method: convertToManagedMethod, @@ -298,7 +319,16 @@ private static void AddConvertToManaged( parameterIndex: 0); } - private static void AddConvertToManagedAndDispose( + /// + /// Adds the ConvertToManaged and Dispose methods to an element marshaller type for an element type marshalled as a native object. + /// + /// The element marshaller type to add the methods to. + /// The for the element type. + /// The ConvertToManaged interface method being implemented. + /// The Dispose interface method being implemented. + /// The instance to use. + /// The emit state for this invocation. + private static void ConvertToManagedAndDispose( TypeDefinition elementMarshallerType, TypeSignature elementType, MemberReference convertToManagedInterfaceMethod, @@ -306,13 +336,17 @@ private static void AddConvertToManagedAndDispose( InteropReferences interopReferences, InteropGeneratorEmitState emitState) { - AddConvertToManaged( - elementMarshallerType, - elementType, - interopReferences.Void.MakePointerType(), - convertToManagedInterfaceMethod, - emitState); + // These element types are all marshalled as native object pointers + ConvertToManaged( + elementMarshallerType: elementMarshallerType, + elementType: elementType, + elementAbiType: interopReferences.Void.MakePointerType(), + convertToManagedInterfaceMethod: convertToManagedInterfaceMethod, + emitState: emitState); + // Define the 'Dispose' method as follows: + // + // public static void Dispose(void* value) MethodDefinition disposeMethod = new( name: "Dispose"u8, attributes: MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, @@ -322,13 +356,17 @@ private static void AddConvertToManagedAndDispose( { CilInstructions = { + // WindowsRuntimeUnknownMarshaller.Free(value); { Ldarg_0 }, { Call, interopReferences.WindowsRuntimeUnknownMarshallerFree }, { Ret } } }; - elementMarshallerType.AddMethodImplementation(disposeInterfaceMethod, disposeMethod); + // Add and implement the 'Dispose' method + elementMarshallerType.AddMethodImplementation( + declaration: disposeInterfaceMethod, + method: disposeMethod); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 691963224..a47b60f3f 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -891,9 +891,39 @@ public InteropReferences( public TypeReference IVectorMethodsImpl1 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsImpl`1"u8); /// - /// Gets the for WindowsRuntime.InteropServices.IVectorMethods. + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsExtensions. /// - public TypeReference IVectorMethods => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethods"u8); + public TypeReference IVectorMethodsExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsBlittableValueTypeExtensions. + /// + public TypeReference IVectorMethodsBlittableValueTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsBlittableValueTypeExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsUnmanagedValueTypeExtensions. + /// + public TypeReference IVectorMethodsUnmanagedValueTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsUnmanagedValueTypeExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsManagedValueTypeExtensions. + /// + public TypeReference IVectorMethodsManagedValueTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsManagedValueTypeExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsKeyValuePairTypeExtensions. + /// + public TypeReference IVectorMethodsKeyValuePairTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsKeyValuePairTypeExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsNullableTypeExtensions. + /// + public TypeReference IVectorMethodsNullableTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsNullableTypeExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsReferenceTypeExtensions. + /// + public TypeReference IVectorMethodsReferenceTypeExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IVectorMethodsReferenceTypeExtensions"u8); /// /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>. @@ -3158,8 +3188,9 @@ public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerConvertToUnm } /// - /// Gets the for IWindowsRuntimeReferenceTypeElementMarshaller<T>.ConvertToManaged. + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeReferenceTypeElementMarshaller<T>.ConvertToManaged. /// + /// The input element type. public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerConvertToManaged(TypeSignature elementType) { return IWindowsRuntimeReferenceTypeElementMarshaller1 @@ -3171,8 +3202,9 @@ public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerConvertToMan } /// - /// Gets the for IWindowsRuntimeReferenceTypeElementMarshaller<T>.Dispose. + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeReferenceTypeElementMarshaller<T>.Dispose. /// + /// The input element type. public MemberReference IWindowsRuntimeReferenceTypeElementMarshallerDispose(TypeSignature elementType) { return IWindowsRuntimeReferenceTypeElementMarshaller1 @@ -3198,6 +3230,11 @@ public MemberReference IWindowsRuntimeManagedValueTypeElementMarshallerConvertTo parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeManagedValueTypeElementMarshaller<T, TAbi>.ConvertToManaged. + /// + /// The input element type. + /// The ABI type. public MemberReference IWindowsRuntimeManagedValueTypeElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) { return IWindowsRuntimeManagedValueTypeElementMarshaller2 @@ -3238,6 +3275,11 @@ public MemberReference IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvert parameterTypes: [new GenericParameterSignature(GenericParameterType.Type, 0)])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeUnmanagedValueTypeElementMarshaller<T, TAbi>.ConvertToManaged. + /// + /// The input element type. + /// The ABI type. public MemberReference IWindowsRuntimeUnmanagedValueTypeElementMarshallerConvertToManaged(TypeSignature elementType, TypeSignature abiType) { return IWindowsRuntimeUnmanagedValueTypeElementMarshaller2 @@ -3266,6 +3308,11 @@ public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertTo new GenericParameterSignature(GenericParameterType.Type, 1)])])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeElementMarshaller<TKey, TValue>.ConvertToManaged. + /// + /// The input key type. + /// The input value type. public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertToManaged(TypeSignature keyType, TypeSignature valueType) { return IWindowsRuntimeKeyValuePairTypeElementMarshaller2 @@ -3278,6 +3325,11 @@ public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerConvertTo parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeKeyValuePairTypeElementMarshaller<TKey, TValue>.Dispose. + /// + /// The input key type. + /// The input value type. public MemberReference IWindowsRuntimeKeyValuePairTypeElementMarshallerDispose(TypeSignature keyType, TypeSignature valueType) { return IWindowsRuntimeKeyValuePairTypeElementMarshaller2 @@ -3302,6 +3354,10 @@ public MemberReference IWindowsRuntimeNullableTypeElementMarshallerConvertToUnma parameterTypes: [Nullable1.MakeGenericValueType([new GenericParameterSignature(GenericParameterType.Type, 0)])])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeNullableTypeElementMarshaller<T>.ConvertToManaged. + /// + /// The underlying value type. public MemberReference IWindowsRuntimeNullableTypeElementMarshallerConvertToManaged(TypeSignature underlyingType) { return IWindowsRuntimeNullableTypeElementMarshaller1 @@ -3312,6 +3368,10 @@ public MemberReference IWindowsRuntimeNullableTypeElementMarshallerConvertToMana parameterTypes: [_corLibTypeFactory.Void.MakePointerType()])); } + /// + /// Gets the for WindowsRuntime.InteropServices.Marshalling.IWindowsRuntimeNullableTypeElementMarshaller<T>.Dispose. + /// + /// The underlying value type. public MemberReference IWindowsRuntimeNullableTypeElementMarshallerDispose(TypeSignature underlyingType) { return IWindowsRuntimeNullableTypeElementMarshaller1 @@ -4771,6 +4831,7 @@ public MemberReference IVectorMethodsImpl1GetAt(TypeSignature elementType) /// /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>.GetMany. /// + /// The input element type. public MemberReference IVectorMethodsImpl1GetMany(TypeSignature elementType) { return IVectorMethodsImpl1 @@ -4786,11 +4847,60 @@ public MemberReference IVectorMethodsImpl1GetMany(TypeSignature elementType) } /// - /// Gets the blittable IVectorMethods.GetMany<T> overload. + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsExtensions.GetMany. + /// + public MemberReference IVectorMethodsOfStringGetMany => field ??= IVectorMethodsExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + _corLibTypeFactory.String.MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsExtensions.GetMany. + /// + public MemberReference IVectorMethodsOfObjectGetMany => field ??= IVectorMethodsExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Object.MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsExtensions.GetMany. + /// + public MemberReference IVectorMethodsOfExceptionGetMany => field ??= IVectorMethodsExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Exception.ToReferenceTypeSignature().MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsExtensions.GetMany. /// - public MethodSpecification IVectorMethodsGetManyBlittable(TypeSignature elementType) + public MemberReference IVectorMethodsOfTypeGetMany => field ??= IVectorMethodsExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Int32, + parameterTypes: [ + WindowsRuntimeObjectReference.ToReferenceTypeSignature(), + Type.ToReferenceTypeSignature().MakeSzArrayType(), + _corLibTypeFactory.Int32, + _corLibTypeFactory.Int32])); + + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsBlittableValueTypeExtensions.GetMany<T>. + /// + /// The input element type. + public MethodSpecification IVectorMethodsBlittableValueTypeGetMany(TypeSignature elementType) { - return IVectorMethods + return IVectorMethodsBlittableValueTypeExtensions .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, genericParameterCount: 1, @@ -4803,38 +4913,35 @@ public MethodSpecification IVectorMethodsGetManyBlittable(TypeSignature elementT } /// - /// Gets the IVectorMethods.GetManyStrings method. - /// - public MemberReference IVectorMethodsGetManyStrings => field ??= IVectorMethods - .CreateMemberReference("GetManyStrings"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Int32, - parameterTypes: [ - WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - _corLibTypeFactory.String.MakeSzArrayType(), - _corLibTypeFactory.Int32, - _corLibTypeFactory.Int32])); - - /// - /// Gets the projected-reference IVectorMethods.GetManyReferences<T, TElementMarshaller> overload. + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsUnmanagedValueTypeExtensions.GetMany<T, TAbi, TElementMarshaller>. /// - public MethodSpecification IVectorMethodsGetManyReferences(TypeSignature elementType, TypeSignature elementMarshallerType) + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification IVectorMethodsUnmanagedValueTypeGetMany(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) { - return IVectorMethods - .CreateMemberReference("GetManyReferences"u8, MethodSignature.CreateStatic( + return IVectorMethodsUnmanagedValueTypeExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, - genericParameterCount: 2, + genericParameterCount: 3, parameterTypes: [ WindowsRuntimeObjectReference.ToReferenceTypeSignature(), new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), _corLibTypeFactory.Int32, _corLibTypeFactory.Int32])) - .MakeGenericInstanceMethod([elementType, elementMarshallerType]); + .MakeGenericInstanceMethod([elementType, abiType, elementMarshallerType]); } - public MethodSpecification IVectorMethodsGetManyUnmanagedValues(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsManagedValueTypeExtensions.GetMany<T, TAbi, TElementMarshaller>. + /// + /// The input element type. + /// The ABI type. + /// The element marshaller type. + public MethodSpecification IVectorMethodsManagedValueTypeGetMany(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) { - return IVectorMethods - .CreateMemberReference("GetManyUnmanagedValues"u8, MethodSignature.CreateStatic( + return IVectorMethodsManagedValueTypeExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, genericParameterCount: 3, parameterTypes: [ @@ -4845,24 +4952,37 @@ public MethodSpecification IVectorMethodsGetManyUnmanagedValues(TypeSignature el .MakeGenericInstanceMethod([elementType, abiType, elementMarshallerType]); } - public MethodSpecification IVectorMethodsGetManyManagedValues(TypeSignature elementType, TypeSignature abiType, TypeSignature elementMarshallerType) + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsKeyValuePairTypeExtensions.GetMany<TKey, TValue, TElementMarshaller>. + /// + /// The input key type. + /// The input value type. + /// The element marshaller type. + public MethodSpecification IVectorMethodsKeyValuePairTypeGetMany(TypeSignature keyType, TypeSignature valueType, TypeSignature elementMarshallerType) { - return IVectorMethods - .CreateMemberReference("GetManyManagedValues"u8, MethodSignature.CreateStatic( + return IVectorMethodsKeyValuePairTypeExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, genericParameterCount: 3, parameterTypes: [ WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), + KeyValuePair2.MakeGenericValueType([ + new GenericParameterSignature(GenericParameterType.Method, 0), + new GenericParameterSignature(GenericParameterType.Method, 1)]).MakeSzArrayType(), _corLibTypeFactory.Int32, _corLibTypeFactory.Int32])) - .MakeGenericInstanceMethod([elementType, abiType, elementMarshallerType]); + .MakeGenericInstanceMethod([keyType, valueType, elementMarshallerType]); } - public MethodSpecification IVectorMethodsGetManyNullable(TypeSignature underlyingType, TypeSignature elementMarshallerType) + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsNullableTypeExtensions.GetMany<T, TElementMarshaller>. + /// + /// The underlying value type. + /// The element marshaller type. + public MethodSpecification IVectorMethodsNullableTypeGetMany(TypeSignature underlyingType, TypeSignature elementMarshallerType) { - return IVectorMethods - .CreateMemberReference("GetManyNullable"u8, MethodSignature.CreateStatic( + return IVectorMethodsNullableTypeExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, genericParameterCount: 2, parameterTypes: [ @@ -4873,53 +4993,25 @@ public MethodSpecification IVectorMethodsGetManyNullable(TypeSignature underlyin .MakeGenericInstanceMethod([underlyingType, elementMarshallerType]); } - public MethodSpecification IVectorMethodsGetManyKeyValuePairs( - TypeSignature keyType, - TypeSignature valueType, - TypeSignature elementMarshallerType) + /// + /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsReferenceTypeExtensions.GetMany<T, TElementMarshaller>. + /// + /// The input element type. + /// The element marshaller type. + public MethodSpecification IVectorMethodsReferenceTypeGetMany(TypeSignature elementType, TypeSignature elementMarshallerType) { - return IVectorMethods - .CreateMemberReference("GetManyKeyValuePairs"u8, MethodSignature.CreateStatic( + return IVectorMethodsReferenceTypeExtensions + .CreateMemberReference("GetMany"u8, MethodSignature.CreateStatic( returnType: _corLibTypeFactory.Int32, - genericParameterCount: 3, + genericParameterCount: 2, parameterTypes: [ WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - KeyValuePair2.MakeGenericValueType([ - new GenericParameterSignature(GenericParameterType.Method, 0), - new GenericParameterSignature(GenericParameterType.Method, 1)]).MakeSzArrayType(), + new GenericParameterSignature(GenericParameterType.Method, 0).MakeSzArrayType(), _corLibTypeFactory.Int32, _corLibTypeFactory.Int32])) - .MakeGenericInstanceMethod([keyType, valueType, elementMarshallerType]); + .MakeGenericInstanceMethod([elementType, elementMarshallerType]); } - public MemberReference IVectorMethodsGetManyObjects => field ??= IVectorMethods - .CreateMemberReference("GetManyObjects"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Int32, - parameterTypes: [ - WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - Object.MakeSzArrayType(), - _corLibTypeFactory.Int32, - _corLibTypeFactory.Int32])); - - public MemberReference IVectorMethodsGetManyTypes => field ??= IVectorMethods - .CreateMemberReference("GetManyTypes"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Int32, - parameterTypes: [ - WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - Type.ToReferenceTypeSignature().MakeSzArrayType(), - _corLibTypeFactory.Int32, - _corLibTypeFactory.Int32])); - - public MemberReference IVectorMethodsGetManyExceptions => field ??= IVectorMethods - .CreateMemberReference("GetManyExceptions"u8, MethodSignature.CreateStatic( - returnType: _corLibTypeFactory.Int32, - parameterTypes: [ - WindowsRuntimeObjectReference.ToReferenceTypeSignature(), - Exception.ToReferenceTypeSignature().MakeSzArrayType(), - _corLibTypeFactory.Int32, - _corLibTypeFactory.Int32])); - - /// /// Gets the for WindowsRuntime.InteropServices.IVectorMethodsImpl<T>.SetAt. /// @@ -6825,4 +6917,4 @@ public MemberReference ReadOnlyDictionaryValueCollection2_ctor(TypeSignature key new GenericParameterSignature(GenericParameterType.Type, 0), new GenericParameterSignature(GenericParameterType.Type, 1)])])]); } -} \ No newline at end of file +} diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index ba69b1a49..1282e1c99 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -92,12 +92,16 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, ArgumentException.ThrowInsufficientSpaceToCopyCollection(); } - int copied = count > 0 - ? TMethods.GetMany(thisReference, array, arrayIndex, count) - : 0; + // If there are no items to copy, we can just stop here + if (count == 0) + { + return; + } + + int copied = TMethods.GetMany(thisReference, array, arrayIndex, count); - // Some providers may return fewer items than requested. Preserve ICollection.CopyTo - // semantics by retrieving any remaining items individually. + // Some vectors might return fewer items than requested, so preserve the semantics + // of 'ICollection.CopyTo' by retrieving any remaining items individually for (int i = copied; i < count; i++) { array[i + arrayIndex] = Item(thisReference, i); diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs index 084bdfa51..8a5eeaacf 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethods.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using System.Runtime.CompilerServices; -using WindowsRuntime.InteropServices.Marshalling; namespace WindowsRuntime.InteropServices; @@ -13,8 +11,6 @@ namespace WindowsRuntime.InteropServices; [WindowsRuntimeImplementationOnlyMember] public static unsafe class IVectorMethods { - private const int GetManyBufferLength = 64; - /// /// Gets the number of items in the vector. /// @@ -27,489 +23,6 @@ public static uint Size(WindowsRuntimeObjectReference thisReference) return IVectorViewMethods.Size(thisReference); } - /// - /// Copies blittable elements from a vector through its GetMany ABI method. - /// - public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) - where T : unmanaged - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - int copied = 0; - - fixed (T* destination = &array[arrayIndex]) - { - while (copied < count) - { - uint requested = (uint)(count - copied); - uint actual; - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, destination + copied, &actual)); - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies string elements from a vector through its GetMany ABI method. - /// - public static int GetManyStrings(WindowsRuntimeObjectReference thisReference, string[] array, int arrayIndex, int count) - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span handles = stackalloc nint[GetManyBufferLength]; - int copied = 0; - - fixed (nint* handlesPtr = handles) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - handles.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, handlesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = HStringMarshaller.ConvertToManaged((void*)handles[i]); - } - } - finally - { - for (int i = 0; i < actual; i++) - { - HStringMarshaller.Free((void*)handles[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies projected reference elements from a vector through its GetMany ABI method. - /// - public static int GetManyReferences(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) - where T : class - where TElementMarshaller : IWindowsRuntimeReferenceTypeElementMarshaller - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc nint[GetManyBufferLength]; - int copied = 0; - - fixed (nint* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - nativeValues.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i])!; - } - } - finally - { - for (int i = 0; i < actual; i++) - { - TElementMarshaller.Dispose((void*)nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies ABI-transformed unmanaged values from a vector through its GetMany ABI method. - /// - public static int GetManyUnmanagedValues(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) - where T : unmanaged - where TAbi : unmanaged - where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeElementMarshaller - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc TAbi[GetManyBufferLength]; - int copied = 0; - - fixed (TAbi* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies managed value types from a vector through its GetMany ABI method. - /// - public static int GetManyManagedValues(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) - where T : struct - where TAbi : unmanaged - where TElementMarshaller : IWindowsRuntimeManagedValueTypeElementMarshaller - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc TAbi[GetManyBufferLength]; - int copied = 0; - - fixed (TAbi* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - nativeValues.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(nativeValues[i]); - } - } - finally - { - for (int i = 0; i < actual; i++) - { - TElementMarshaller.Dispose(nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies nullable values from a vector through its GetMany ABI method. - /// - public static int GetManyNullable(WindowsRuntimeObjectReference thisReference, T?[] array, int arrayIndex, int count) - where T : struct - where TElementMarshaller : IWindowsRuntimeNullableTypeElementMarshaller - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc nint[GetManyBufferLength]; - int copied = 0; - - fixed (nint* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - nativeValues.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i]); - } - } - finally - { - for (int i = 0; i < actual; i++) - { - TElementMarshaller.Dispose((void*)nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies key/value pairs from a vector through its GetMany ABI method. - /// - public static int GetManyKeyValuePairs( - WindowsRuntimeObjectReference thisReference, - System.Collections.Generic.KeyValuePair[] array, - int arrayIndex, - int count) - where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeElementMarshaller - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc nint[GetManyBufferLength]; - int copied = 0; - - fixed (nint* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - nativeValues.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged((void*)nativeValues[i]); - } - } - finally - { - for (int i = 0; i < actual; i++) - { - TElementMarshaller.Dispose((void*)nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies object elements from a vector through its GetMany ABI method. - /// - public static int GetManyObjects(WindowsRuntimeObjectReference thisReference, object[] array, int arrayIndex, int count) - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - Span nativeValues = stackalloc nint[GetManyBufferLength]; - int copied = 0; - - fixed (nint* nativeValuesPtr = nativeValues) - { - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - nativeValues.Clear(); - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValuesPtr, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = WindowsRuntimeObjectMarshaller.ConvertToManaged((void*)nativeValues[i])!; - } - } - finally - { - for (int i = 0; i < actual; i++) - { - WindowsRuntimeUnknownMarshaller.Free((void*)nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - } - - return copied; - } - - /// - /// Copies elements from a vector through its GetMany ABI method. - /// - public static int GetManyTypes(WindowsRuntimeObjectReference thisReference, Type[] array, int arrayIndex, int count) - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - ABI.System.Type* nativeValues = stackalloc ABI.System.Type[GetManyBufferLength]; - int copied = 0; - - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - for (int i = 0; i < requested; i++) - { - nativeValues[i] = default; - } - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); - - try - { - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = ABI.System.TypeMarshaller.ConvertToManaged(nativeValues[i])!; - } - } - finally - { - for (int i = 0; i < actual; i++) - { - ABI.System.TypeMarshaller.Dispose(nativeValues[i]); - } - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - - return copied; - } - - /// - /// Copies elements from a vector through its GetMany ABI method. - /// - public static int GetManyExceptions(WindowsRuntimeObjectReference thisReference, Exception[] array, int arrayIndex, int count) - { - using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); - - void* thisPtr = thisValue.GetThisPtrUnsafe(); - ABI.System.Exception* nativeValues = stackalloc ABI.System.Exception[GetManyBufferLength]; - int copied = 0; - - while (copied < count) - { - uint requested = (uint)int.Min(GetManyBufferLength, count - copied); - uint actual; - - for (int i = 0; i < requested; i++) - { - nativeValues[i] = default; - } - - RestrictedErrorInfo.ThrowExceptionForHR( - InvokeGetMany(thisPtr, (uint)copied, requested, nativeValues, &actual)); - - for (int i = 0; i < actual; i++) - { - array[arrayIndex + copied + i] = ABI.System.ExceptionMarshaller.ConvertToManaged(nativeValues[i])!; - } - - copied += (int)actual; - - if (actual < requested) - { - break; - } - } - - return copied; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static HRESULT InvokeGetMany(void* thisPtr, uint startIndex, uint capacity, void* items, uint* actual) - { - return ((IVectorVftbl*)*(void***)thisPtr)->GetMany(thisPtr, startIndex, capacity, items, actual); - } - /// /// Removes the item at the specified index in the vector. /// diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs new file mode 100644 index 000000000..5d26a4112 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs @@ -0,0 +1,513 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using WindowsRuntime.InteropServices.Marshalling; + +namespace WindowsRuntime.InteropServices; + +/// +/// Extensions for the type. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsExtensions +{ + // Note: all the 'GetMany' extensions in this file share the same structure. Except for the blittable + // specialization, which can retrieve items straight into the target array, they retrieve the requested + // items from the native vector in batches, into a stack buffer, and then marshal each batch to managed. + // They can't be shared because each one needs a different ABI buffer type and marshalling logic, and + // sharing code for all of them would require some additional abstraction on top which would in turn + // increase overhead. To avoid that, we just keep a separate version of the code for each of them. Any + // changes to these methods should be kept in sync. + + /// + /// The maximum number of items to retrieve from a vector on each GetMany ABI call. + /// + internal const int GetManyBufferLength = 64; + + extension(IVectorMethods) + { + /// + /// Retrieves multiple items from the vector, starting from the first one, and copies them to a target array. + /// + /// The instance to use to invoke the native method. + /// The target array to copy the retrieved items to. + /// The zero-based index in to start copying to. + /// The number of items to retrieve from the vector. + /// The number of items that were retrieved. This value can be less than if the end of the vector is reached. + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, string[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + HSTRING* items = stackalloc HSTRING[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + // Marshal all retrieved items into the target array + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = HStringMarshaller.ConvertToManaged(items[i]); + } + } + finally + { + // Make sure to release all retrieved items, even if marshalling failed (this shouldn't ever throw) + for (int i = 0; i < actual; i++) + { + HStringMarshaller.Free(items[i]); + } + } + + copied += (int)actual; + + // If the vector returned fewer items than requested, we reached the end of the collection + if (actual < capacity) + { + break; + } + } + + return copied; + } + + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, object[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void** items = stackalloc void*[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = WindowsRuntimeObjectMarshaller.ConvertToManaged(items[i])!; + } + } + finally + { + for (int i = 0; i < actual; i++) + { + WindowsRuntimeUnknownMarshaller.Free(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, Exception[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ABI.System.Exception* items = stackalloc ABI.System.Exception[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + // Exception values are just 'HRESULT'-s, so there's nothing to release after marshalling + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = ABI.System.ExceptionMarshaller.ConvertToManaged(items[i])!; + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, Type[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ABI.System.Type* items = stackalloc ABI.System.Type[GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + // Same as with 'string' above, but with the 'Type' marshaller + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = ABI.System.TypeMarshaller.ConvertToManaged(items[i])!; + } + } + finally + { + // Make sure to dispose all retrieved values (this shouldn't ever throw) + for (int i = 0; i < actual; i++) + { + ABI.System.TypeMarshaller.Dispose(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for blittable value types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsBlittableValueTypeExtensions +{ + extension(IVectorMethods) + where T : unmanaged + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + int copied = 0; + + // Blittable items don't need any marshalling, so we can retrieve them + // directly into the target array, with no intermediate stack buffer + fixed (T* items = array) + { + while (copied < count) + { + uint capacity = (uint)(count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items + arrayIndex + copied, &actual)); + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for unmanaged value types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsUnmanagedValueTypeExtensions +{ + extension(IVectorMethods) + where T : unmanaged + where TAbi : unmanaged + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where TElementMarshaller : IWindowsRuntimeUnmanagedValueTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + TAbi* items = stackalloc TAbi[IVectorMethodsExtensions.GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(IVectorMethodsExtensions.GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + // Unmanaged value types have no resources to release after marshalling + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(items[i]); + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for managed value types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsManagedValueTypeExtensions +{ + extension(IVectorMethods) + where T : struct + where TAbi : unmanaged + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where TElementMarshaller : IWindowsRuntimeManagedValueTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + TAbi* items = stackalloc TAbi[IVectorMethodsExtensions.GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(IVectorMethodsExtensions.GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + // Same as with 'string' above, but with the provided marshaller + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(items[i]); + } + } + finally + { + // Make sure to dispose all retrieved values (this shouldn't ever throw) + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsKeyValuePairTypeExtensions +{ + extension(IVectorMethods) + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, KeyValuePair[] array, int arrayIndex, int count) + where TElementMarshaller : IWindowsRuntimeKeyValuePairTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void** items = stackalloc void*[IVectorMethodsExtensions.GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(IVectorMethodsExtensions.GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + // Same as with 'string' above, but with the provided marshaller + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(items[i]); + } + } + finally + { + // Make sure to release all retrieved values (this shouldn't ever throw) + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsNullableTypeExtensions +{ + extension(IVectorMethods) + where T : struct + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T?[] array, int arrayIndex, int count) + where TElementMarshaller : IWindowsRuntimeNullableTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void** items = stackalloc void*[IVectorMethodsExtensions.GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(IVectorMethodsExtensions.GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + // Same as with 'string' above, but with the provided marshaller + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(items[i]); + } + } + finally + { + // Make sure to release all retrieved values (this shouldn't ever throw) + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} + +/// +/// Extensions for the type for reference types. +/// +[WindowsRuntimeImplementationOnlyMember] +public static unsafe class IVectorMethodsReferenceTypeExtensions +{ + extension(IVectorMethods) + where T : class + { + /// + public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count) + where TElementMarshaller : IWindowsRuntimeReferenceTypeElementMarshaller + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void** items = stackalloc void*[IVectorMethodsExtensions.GetManyBufferLength]; + int copied = 0; + + while (copied < count) + { + uint capacity = (uint)int.Min(IVectorMethodsExtensions.GetManyBufferLength, count - copied); + uint actual; + + RestrictedErrorInfo.ThrowExceptionForHR(IVectorVftbl.GetManyUnsafe(thisPtr, (uint)copied, capacity, items, &actual)); + + try + { + for (int i = 0; i < actual; i++) + { + array[arrayIndex + copied + i] = TElementMarshaller.ConvertToManaged(items[i])!; + } + } + finally + { + for (int i = 0; i < actual; i++) + { + TElementMarshaller.Dispose(items[i]); + } + } + + copied += (int)actual; + + if (actual < capacity) + { + break; + } + } + + return copied; + } + } +} diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs index 16ff6a3d1..adda808bb 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsImpl{T}.cs @@ -20,8 +20,14 @@ public interface IVectorMethodsImpl static abstract T GetAt(WindowsRuntimeObjectReference thisReference, uint index); /// - /// Copies elements from the vector through its GetMany ABI method. + /// Retrieves multiple items from the vector, starting from the first one, and copies them to a target array. /// + /// The instance to use to invoke the native method. + /// The target array to copy the retrieved items to. + /// The zero-based index in to start copying to. + /// The number of items to retrieve from the vector. + /// The number of items that were retrieved. This value can be less than if the end of the vector is reached. + /// static abstract int GetMany(WindowsRuntimeObjectReference thisReference, T[] array, int arrayIndex, int count); /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs index 6f63e91b9..9268ccd13 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeKeyValuePairTypeElementMarshaller{TKey, TValue}.cs @@ -6,7 +6,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// -/// An interface for marshalling collection elements to native. +/// An interface for marshalling collection elements to and from native. /// /// The type of the key. /// The type of the value. @@ -21,12 +21,15 @@ public unsafe interface IWindowsRuntimeKeyValuePairTypeElementMarshaller value); /// - /// Converts an unmanaged key/value pair to its managed representation. + /// Marshals a native Windows Runtime type to its managed representation. /// + /// The input value to marshal. + /// The marshalled managed value. static abstract KeyValuePair ConvertToManaged(void* value); /// - /// Releases an unmanaged key/value pair. + /// Disposes resources associated with an unmanaged value. /// + /// The unmanaged value to dispose. static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs index 9d51845f0..b05dd3607 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeManagedValueTypeElementMarshaller{T, TAbi}.cs @@ -4,7 +4,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// -/// An interface for marshalling collection elements to native. +/// An interface for marshalling collection elements to and from native. /// /// The type of elements in the array. /// The ABI type for type . @@ -23,6 +23,8 @@ public interface IWindowsRuntimeManagedValueTypeElementMarshaller /// /// Marshals a native Windows Runtime value type to its managed representation. /// + /// The input value to marshal. + /// The marshalled managed value. static abstract T ConvertToManaged(TAbi value); /// diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs index 6d6ed00ac..1215d7590 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeNullableTypeElementMarshaller{T}.cs @@ -6,7 +6,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// -/// An interface for marshalling collection elements to native. +/// An interface for marshalling collection elements to and from native. /// /// The underlying value type of the nullable type. [WindowsRuntimeImplementationOnlyMember] @@ -21,12 +21,15 @@ public unsafe interface IWindowsRuntimeNullableTypeElementMarshaller static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(T? value); /// - /// Converts an unmanaged nullable value to its managed representation. + /// Marshals a native Windows Runtime value to its managed representation. /// + /// The input value to marshal. + /// The marshalled managed value. static abstract T? ConvertToManaged(void* value); /// - /// Releases an unmanaged nullable value. + /// Disposes resources associated with an unmanaged value. /// + /// The unmanaged value to dispose. static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs index a12f029f2..24d433b1a 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeReferenceTypeElementMarshaller{T}.cs @@ -4,7 +4,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// -/// An interface for marshalling collection elements to native. +/// An interface for marshalling collection elements to and from native. /// /// The type of elements in the array. [WindowsRuntimeImplementationOnlyMember] @@ -19,12 +19,15 @@ public unsafe interface IWindowsRuntimeReferenceTypeElementMarshaller static abstract WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(T? value); /// - /// Converts an unmanaged pointer to a managed collection element. + /// Converts an unmanaged pointer to a Windows Runtime object to a managed object. /// + /// The input object to convert to managed. + /// The resulting managed object. static abstract T? ConvertToManaged(void* value); /// - /// Releases an unmanaged collection element. + /// Disposes resources associated with an unmanaged value. /// + /// The unmanaged value to dispose. static abstract void Dispose(void* value); } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs index 1f390a314..8f657754d 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/Collections/IWindowsRuntimeUnmanagedValueTypeElementMarshaller{T, TAbi}.cs @@ -4,7 +4,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// -/// An interface for marshalling collection elements to native. +/// An interface for marshalling collection elements to and from native. /// /// The type of elements in the array. /// The ABI type for type . @@ -23,5 +23,7 @@ public interface IWindowsRuntimeUnmanagedValueTypeElementMarshaller /// /// Marshals a native Windows Runtime value type to its managed representation. /// + /// The input value to marshal. + /// The marshalled managed value. static abstract T ConvertToManaged(TAbi value); } diff --git a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs index 303cabe49..76fb698d3 100644 --- a/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs +++ b/src/WinRT.Runtime2/InteropServices/Vtables/IVectorVftbl.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Windows.Foundation; @@ -34,4 +35,19 @@ internal unsafe struct IVectorVftbl public delegate* unmanaged[MemberFunction] Clear; public delegate* unmanaged[MemberFunction] GetMany; public delegate* unmanaged[MemberFunction] ReplaceAll; + + /// + /// Retrieves multiple items from the vector beginning at the given index. + /// + /// The target COM object. + /// The zero-based index of the first item to retrieve. + /// The number of items that can be written to . + /// The target buffer to write the retrieved items to. + /// The number of items that were retrieved. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HRESULT GetManyUnsafe(void* thisPtr, uint startIndex, uint capacity, void* items, uint* actual) + { + return ((IVectorVftbl*)*(void***)thisPtr)->GetMany(thisPtr, startIndex, capacity, items, actual); + } } \ No newline at end of file From 5a966de07e77cb84c427610782cd92e90cd2b152 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 13 Aug 2026 14:29:52 -0700 Subject: [PATCH 11/11] Add missing trailing periods on multi-line comments Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9216861-f68c-437e-8f82-776d072bac2a --- .../InteropServices/Collections/IListMethods{T}.cs | 2 +- .../InteropServices/Collections/IVectorMethodsExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs index 1282e1c99..9ad4eb174 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListMethods{T}.cs @@ -101,7 +101,7 @@ public static void CopyTo(WindowsRuntimeObjectReference thisReference, int copied = TMethods.GetMany(thisReference, array, arrayIndex, count); // Some vectors might return fewer items than requested, so preserve the semantics - // of 'ICollection.CopyTo' by retrieving any remaining items individually + // of 'ICollection.CopyTo' by retrieving any remaining items individually. for (int i = copied; i < count; i++) { array[i + arrayIndex] = Item(thisReference, i); diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs index 5d26a4112..f27f3c433 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IVectorMethodsExtensions.cs @@ -220,7 +220,7 @@ public static int GetMany(WindowsRuntimeObjectReference thisReference, T[] array int copied = 0; // Blittable items don't need any marshalling, so we can retrieve them - // directly into the target array, with no intermediate stack buffer + // directly into the target array, with no intermediate stack buffer. fixed (T* items = array) { while (copied < count)