From e1cc2044bfed1a7516ecf44df50cba054cdd1040 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 13:37:58 -0800 Subject: [PATCH 01/23] Add UniversalApiContract enum and mapping Add a C# stub enum Windows.Foundation.UniversalApiContract (with ContractVersion attribute) required for ABI projection of value types and delegates; marshalling of this type is not supported. Also add a mapping entry in src/cswinrt/helpers.h so the contract is recognized during codegen. --- .../Windows.Foundation/UniversalApiContract.cs | 15 +++++++++++++++ src/cswinrt/helpers.h | 1 + 2 files changed, 16 insertions(+) create mode 100644 src/WinRT.Runtime2/Windows.Foundation/UniversalApiContract.cs diff --git a/src/WinRT.Runtime2/Windows.Foundation/UniversalApiContract.cs b/src/WinRT.Runtime2/Windows.Foundation/UniversalApiContract.cs new file mode 100644 index 000000000..577adb7bb --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Foundation/UniversalApiContract.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Windows.Foundation.Metadata; + +namespace Windows.Foundation; + +/// +/// Represents the Universal API contract. +/// +/// +/// This type is required for ABI projection of the value types and delegates, but marshalling it is not supported. +/// +[ContractVersion(1245184u)] +public enum UniversalApiContract; \ No newline at end of file diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 6e171e50c..6b81da52b 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -852,6 +852,7 @@ namespace cswinrt { "Size", "Windows.Foundation", "Size" }, { "TimeSpan", "System", "TimeSpan", true }, { "TypedEventHandler`2", "System", "EventHandler`2", false }, + { "UniversalApiContract", "Windows.Foundation", "UniversalApiContract"}, { "Uri", "System", "Uri", true } } }, From 386c6568f69bdf4fd34b0a9ad0c8b7fc0f65f683 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 15:17:51 -0800 Subject: [PATCH 02/23] Add IBuffer interface and register mapping Introduce Windows.Storage.Streams.IBuffer (new IBuffer.cs) with GUID, ContractVersion and Capacity/Length members for WinRT interop. Update src/cswinrt/helpers.h to register the IBuffer type mapping and tidy up trailing commas in the type mapping tables to allow additional entries. --- .../Windows.Storage.Streams/IBuffer.cs | 29 +++++++++++++++++++ src/cswinrt/helpers.h | 17 +++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/WinRT.Runtime2/Windows.Storage.Streams/IBuffer.cs diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/IBuffer.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/IBuffer.cs new file mode 100644 index 000000000..86642d7f5 --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/IBuffer.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using WindowsRuntime; + +namespace Windows.Storage.Streams; + +/// +/// Represents a referenced array of bytes used by byte stream read and write interfaces. +/// +[WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] +[Guid("905A0FE0-BC53-11DF-8C49-001E4FC686DA")] +[ContractVersion(typeof(UniversalApiContract), 65536u)] +public interface IBuffer +{ + /// + /// Gets the maximum number of bytes that the buffer can hold. + /// + uint Capacity { get; } + + /// + /// Gets the number of bytes currently in use in the buffer. + /// + uint Length { get; set; } +} diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 6b81da52b..cd8226dc2 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -775,7 +775,7 @@ namespace cswinrt }, { "Microsoft.UI.Xaml.Input", { - { "ICommand", "System.Windows.Input", "ICommand", true } + { "ICommand", "System.Windows.Input", "ICommand", true }, } }, { "Microsoft.UI.Xaml.Interop", @@ -853,7 +853,7 @@ namespace cswinrt { "TimeSpan", "System", "TimeSpan", true }, { "TypedEventHandler`2", "System", "EventHandler`2", false }, { "UniversalApiContract", "Windows.Foundation", "UniversalApiContract"}, - { "Uri", "System", "Uri", true } + { "Uri", "System", "Uri", true }, } }, { "Windows.Foundation.Collections", @@ -871,14 +871,14 @@ namespace cswinrt { "IVectorView`1", "System.Collections.Generic", "IReadOnlyList`1", true, true }, { "IVector`1", "System.Collections.Generic", "IList`1", true, true }, { "MapChangedEventHandler`2", "Windows.Foundation.Collections", "MapChangedEventHandler`2" }, - { "VectorChangedEventHandler`1", "Windows.Foundation.Collections", "VectorChangedEventHandler`1" } + { "VectorChangedEventHandler`1", "Windows.Foundation.Collections", "VectorChangedEventHandler`1" }, } }, { "Windows.Foundation.Metadata", { { "AttributeTargets", "System", "AttributeTargets" }, { "AttributeUsageAttribute", "System", "AttributeUsageAttribute" }, - { "ContractVersionAttribute", "Windows.Foundation.Metadata", "ContractVersionAttribute"} + { "ContractVersionAttribute", "Windows.Foundation.Metadata", "ContractVersionAttribute"}, } }, { "Windows.Foundation.Numerics", @@ -892,6 +892,11 @@ namespace cswinrt { "Vector4", "System.Numerics", "Vector4" }, } }, + { "Windows.Storage.Streams", + { + { "IBuffer", "Windows.Storage.Streams", "IBuffer" }, + } + }, { "Windows.UI.Xaml", { { "CornerRadius", "Windows.UI.Xaml", "CornerRadius", false, false, true }, @@ -930,7 +935,7 @@ namespace cswinrt }, { "Windows.UI.Xaml.Input", { - { "ICommand", "System.Windows.Input", "ICommand", true } + { "ICommand", "System.Windows.Input", "ICommand", true }, } }, { "Windows.UI.Xaml.Interop", @@ -943,7 +948,7 @@ namespace cswinrt { "NotifyCollectionChangedEventArgs", "System.Collections.Specialized", "NotifyCollectionChangedEventArgs", true }, { "NotifyCollectionChangedEventHandler", "System.Collections.Specialized", "NotifyCollectionChangedEventHandler", true }, { "TypeKind", "Windows.UI.Xaml.Interop", "TypeKind", true }, - { "TypeName", "System", "Type", true } + { "TypeName", "System", "Type", true }, } }, { "Windows.UI.Xaml.Media", From aae75839d28c95cc0f3af5e8f8080e9519d39f75 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 15:18:17 -0800 Subject: [PATCH 03/23] Add ABI marshalling for IBuffer Introduce ABI/COM interop support for Windows.Storage.Streams.IBuffer. Adds a new IBuffer marshaller, IBufferMethods wrappers, the IBufferVftbl layout, and a managed IBufferImpl providing UnmanagedCallersOnly stubs for get_Capacity/get_Length/set_Length with HRESULT/RestrictedErrorInfo handling. Also includes a DynamicInterfaceCastable implementation (IBufferInterfaceImpl) and assembly TypeMap/TypeMapAssociation entries for runtime type mapping. --- .../ABI/Windows.Storage.Streams/IBuffer.cs | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs new file mode 100644 index 000000000..24392e088 --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs @@ -0,0 +1,259 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Storage.Streams; +using WindowsRuntime; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; +using static System.Runtime.InteropServices.ComWrappers; + +#pragma warning disable IDE0008, IDE1006 + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code +[assembly: TypeMap( + value: "Windows.Storage.Streams.IBuffer", + target: typeof(IBuffer), + trimTarget: typeof(IBuffer))] +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + +[assembly: TypeMapAssociation( + source: typeof(IBuffer), + proxy: typeof(ABI.Windows.Storage.Streams.IBufferInterfaceImpl))] + +namespace ABI.Windows.Storage.Streams; + +/// +/// Marshaller for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IBufferMarshaller +{ + /// + public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(IBuffer? value) + { + return WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(value, in WellKnownWindowsInterfaceIIDs.IID_IBuffer); + } + + /// + public static IBuffer? ConvertToManaged(void* value) + { + return (IBuffer?)WindowsRuntimeObjectMarshaller.ConvertToManaged(value); + } +} + +/// +/// Interop methods for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IBufferMethods +{ + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Capacity(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + uint result; + + HRESULT hresult = ((IBufferVftbl*)*(void***)thisPtr)->get_Capacity(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Length(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + uint result; + + HRESULT hresult = ((IBufferVftbl*)*(void***)thisPtr)->get_Length(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Length(WindowsRuntimeObjectReference thisReference, uint value) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + + HRESULT hresult = ((IBufferVftbl*)*(void***)thisPtr)->set_Length(thisPtr, value); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + } +} + +/// +/// Binding type for . +/// +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct IBufferVftbl +{ + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetIids; + public delegate* unmanaged[MemberFunction] GetRuntimeClassName; + public delegate* unmanaged[MemberFunction] GetTrustLevel; + public delegate* unmanaged[MemberFunction] get_Capacity; + public delegate* unmanaged[MemberFunction] get_Length; + public delegate* unmanaged[MemberFunction] set_Length; +} + +/// +/// The implementation. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IBufferImpl +{ + /// + /// The value for the managed implementation. + /// + [FixedAddressValueType] + private static readonly IBufferVftbl Vftbl; + + /// + /// Initializes . + /// + static IBufferImpl() + { + *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; + + Vftbl.get_Capacity = &get_Capacity; + Vftbl.get_Length = &get_Length; + Vftbl.set_Length = &set_Length; + } + + /// + /// Gets a pointer to the managed implementation. + /// + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_Capacity(void* thisPtr, uint* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.Capacity; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_Length(void* thisPtr, uint* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.Length; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT set_Length(void* thisPtr, uint value) + { + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + thisObject.Length = value; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } +} + +/// +/// The implementation for . +/// +[DynamicInterfaceCastableImplementation] +[Guid("00000036-0000-0000-C000-000000000046")] +file interface IBufferInterfaceImpl : IBuffer +{ + /// + uint IBuffer.Capacity + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IBuffer).TypeHandle); + + return IBufferMethods.Capacity(thisReference); + } + } + + /// + uint IBuffer.Length + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IBuffer).TypeHandle); + + return IBufferMethods.Length(thisReference); + } + set + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IBuffer).TypeHandle); + + IBufferMethods.Length(thisReference, value); + } + } +} \ No newline at end of file From 651fe4fd4f21de32d682c539deb7848201835b58 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 15:20:32 -0800 Subject: [PATCH 04/23] Add IBuffer interop references and IID Add support for Windows.Storage.Streams.IBuffer across the generator and runtime. This adds an IBuffer TypeReference in InteropReferences, includes IBuffer in WindowsRuntimeExtensions runtime-type checks, and emits the IID mapping in WellKnownInterfaceIIDs (and its template entries) so IBuffer is recognized and usable for WinRT interop. --- .../Extensions/WindowsRuntimeExtensions.cs | 3 ++- .../References/InteropReferences.cs | 5 +++++ .../InteropServices/WellKnownInterfaceIIDs.g.cs | 7 +++++++ .../InteropServices/WellKnownInterfaceIIDs.tt | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 43cac0eb8..8be566056 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -248,7 +248,8 @@ public bool IsManuallyProjectedWindowsRuntimeNonGenericInterfaceType(InteropRefe SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IAsyncAction) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IAsyncInfo) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IVectorChangedEventArgs) || - SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IStringable); + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IStringable) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IBuffer); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 17c82b425..8f02d5c69 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -1489,6 +1489,11 @@ public InteropReferences( /// public TypeReference AsyncOperationWithProgressCompletedHandler2 => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Foundation"u8, "AsyncOperationWithProgressCompletedHandler`2"u8); + /// + /// Gets the for Windows.Storage.Streams.IBuffer. + /// + public TypeReference IBuffer => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IBuffer"u8); + /// /// Gets the for . /// diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs index 2a52094ec..95b3757eb 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs @@ -204,4 +204,11 @@ public static ref readonly Guid IID_Windows_Foundation_IAsyncInfo [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IAsyncInfo; } + + /// The IID for Windows.Storage.Streams.IBuffer (mapped to . + public static ref readonly Guid IID_Windows_Storage_Streams_IBuffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IBuffer; + } } \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt index 805aa7672..a790bc7bd 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt @@ -49,7 +49,8 @@ var entries = new (string ProjectedFullyQualifiedName, string WindowsRuntimeFull ("System.Windows.Input.ICommand", "Microsoft.UI.Xaml.Input.ICommand", false), ("Windows.Foundation.Collections.IVectorChangedEventArgs", null, false), ("Windows.Foundation.IAsyncAction", null, false), - ("Windows.Foundation.IAsyncInfo", null, false) + ("Windows.Foundation.IAsyncInfo", null, false), + ("Windows.Storage.Streams.IBuffer", null, false) }; for (int i = 0; i < entries.Length; i++) From 0c6cbd81701443430628048de7a3759016d40342 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:09:56 -0800 Subject: [PATCH 05/23] Add InputStreamOptions enum for WinRT runtime Introduce Windows.Storage.Streams.InputStreamOptions enum to WinRT.Runtime2 to mirror the UWP API. The enum is annotated with Windows Runtime metadata (WindowsRuntimeMetadata, WindowsRuntimeClassName, WindowsRuntimeReferenceType), SupportedOSPlatform and ContractVersion attributes, and marked with Flags. Defines three values: None (0), Partial (1) and ReadAhead (2). --- .../InputStreamOptions.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs new file mode 100644 index 000000000..17927d3df --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.Versioning; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using WindowsRuntime; + +namespace Windows.Storage.Streams; + +/// +/// Specifies the read options for an input stream. +/// +/// +[Flags] +[WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] +[WindowsRuntimeClassName("Windows.Foundation.IReference`1")] +[WindowsRuntimeReferenceType(typeof(InputStreamOptions?))] +[SupportedOSPlatform("Windows10.0.10240.0")] +[ContractVersion(typeof(UniversalApiContract), 65536u)] +public enum InputStreamOptions : uint +{ + /// + /// No options are specified. + /// + None = 0u, + + /// + /// The asynchronous read operation completes when one or more bytes is available. + /// + Partial = 1u, + + /// + /// The asynchronous read operation may optionally read ahead and prefetch additional bytes. + /// + ReadAhead = 2u +} From f7bf1f2ab04da4c3925cdae42bb28a90c3073cc9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:13:55 -0800 Subject: [PATCH 06/23] Add InputStreamOptions marshaller and IID Introduce ABI marshalling for Windows.Storage.Streams.InputStreamOptions: add InputStreamOptions.cs implementing boxing/unboxing, interface entries, and a WindowsRuntimeComWrappers marshaller attribute to support COM interop. Also add the IReferenceOfInputStreamOptions IID to WellKnownWindowsInterfaceIIDs.g.cs and update the WellKnownWindowsInterfaceIIDs.tt template to include the new IID entry. --- .../InputStreamOptions.cs | 128 ++++++++++++++++++ .../WellKnownWindowsInterfaceIIDs.g.cs | 25 ++++ .../WellKnownWindowsInterfaceIIDs.tt | 1 + 3 files changed, 154 insertions(+) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Storage.Streams/InputStreamOptions.cs diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/InputStreamOptions.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/InputStreamOptions.cs new file mode 100644 index 000000000..fdb05a299 --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/InputStreamOptions.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Storage.Streams; +using WindowsRuntime; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; +using static System.Runtime.InteropServices.ComWrappers; + +#pragma warning disable IDE1006, CA1416 + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code +[assembly: TypeMap( + value: "Windows.Storage.Streams.InputStreamOptions", + target: typeof(InputStreamOptions), + trimTarget: typeof(InputStreamOptions))] + +[assembly: TypeMap( + value: "Windows.Foundation.IReference`1", + target: typeof(InputStreamOptions), + trimTarget: typeof(InputStreamOptions))] +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + +namespace ABI.Windows.Storage.Streams; + +/// +/// Marshaller for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class InputStreamOptionsMarshaller +{ + /// + public static WindowsRuntimeObjectReferenceValue BoxToUnmanaged(InputStreamOptions? value) + { + return WindowsRuntimeValueTypeMarshaller.BoxToUnmanaged(value, CreateComInterfaceFlags.None, in WellKnownWindowsInterfaceIIDs.IID_IReferenceOfInputStreamOptions); + } + + /// + public static InputStreamOptions? UnboxToManaged(void* value) + { + return WindowsRuntimeValueTypeMarshaller.UnboxToManaged(value); + } +} + +/// +/// The set of values for . +/// +file struct InputStreamOptionsInterfaceEntries +{ + public ComInterfaceEntry IReferenceOfInputStreamOptions; + public ComInterfaceEntry IPropertyValue; + public ComInterfaceEntry IStringable; + public ComInterfaceEntry IWeakReferenceSource; + public ComInterfaceEntry IMarshal; + public ComInterfaceEntry IAgileObject; + public ComInterfaceEntry IInspectable; + public ComInterfaceEntry IUnknown; +} + +/// +/// The implementation of . +/// +file static class InputStreamOptionsInterfaceEntriesImpl +{ + /// + /// The value for . + /// + [FixedAddressValueType] + public static readonly InputStreamOptionsInterfaceEntries Entries; + + /// + /// Initializes . + /// + static InputStreamOptionsInterfaceEntriesImpl() + { + Entries.IReferenceOfInputStreamOptions.IID = WellKnownWindowsInterfaceIIDs.IID_IReferenceOfInputStreamOptions; + Entries.IReferenceOfInputStreamOptions.Vtable = IReferenceImpl.UInt32Enum; + Entries.IPropertyValue.IID = WellKnownWindowsInterfaceIIDs.IID_IPropertyValue; + Entries.IPropertyValue.Vtable = IPropertyValueImpl.OtherTypeVtable; + Entries.IStringable.IID = WellKnownWindowsInterfaceIIDs.IID_IStringable; + Entries.IStringable.Vtable = IStringableImpl.Vtable; + Entries.IWeakReferenceSource.IID = WellKnownWindowsInterfaceIIDs.IID_IWeakReferenceSource; + Entries.IWeakReferenceSource.Vtable = IWeakReferenceSourceImpl.Vtable; + Entries.IMarshal.IID = WellKnownWindowsInterfaceIIDs.IID_IMarshal; + Entries.IMarshal.Vtable = IMarshalImpl.Vtable; + Entries.IAgileObject.IID = WellKnownWindowsInterfaceIIDs.IID_IAgileObject; + Entries.IAgileObject.Vtable = IAgileObjectImpl.Vtable; + Entries.IInspectable.IID = WellKnownWindowsInterfaceIIDs.IID_IInspectable; + Entries.IInspectable.Vtable = IInspectableImpl.Vtable; + Entries.IUnknown.IID = WellKnownWindowsInterfaceIIDs.IID_IUnknown; + Entries.IUnknown.Vtable = IUnknownImpl.Vtable; + } +} + +/// +/// A custom implementation for . +/// +internal sealed unsafe class InputStreamOptionsComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute +{ + /// + public override void* GetOrCreateComInterfaceForObject(object value) + { + return WindowsRuntimeComWrappersMarshal.GetOrCreateComInterfaceForObject(value, CreateComInterfaceFlags.None); + } + + /// + public override ComInterfaceEntry* ComputeVtables(out int count) + { + count = sizeof(InputStreamOptionsInterfaceEntries) / sizeof(ComInterfaceEntry); + + return (ComInterfaceEntry*)Unsafe.AsPointer(in InputStreamOptionsInterfaceEntriesImpl.Entries); + } + + /// + public override object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) + { + wrapperFlags = CreatedWrapperFlags.NonWrapping; + + return WindowsRuntimeValueTypeMarshaller.UnboxToManagedUnsafe(value, in WellKnownWindowsInterfaceIIDs.IID_IReferenceOfInputStreamOptions); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs index 30a8be4b8..97205b5ee 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs @@ -2310,6 +2310,31 @@ public static ref readonly Guid IID_IReferenceOfCollectionChange } } + /// The IID for IReferenceOfInputStreamOptions (00000000-0000-0000-C000-000000000046). + public static ref readonly Guid IID_IReferenceOfInputStreamOptions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + /// The IID for IReferenceArrayOfInt32 (A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7). public static ref readonly Guid IID_IReferenceArrayOfInt32 { diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt index 3cd1bce81..3f2ab5534 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt @@ -109,6 +109,7 @@ var entries = new (string Name, string IID)[] ("IReferenceOfAsyncStatus", "A4B74936-2947-5FE8-88D5-51CD35050E71"), ("IReferenceOfAsyncActionCompletedHandler", "F31DBB29-606D-5A89-A23A-C09AB9605B8F"), ("IReferenceOfCollectionChange", "25BCAF91-880D-537D-82FC-9BBF0CACCB8B"), + ("IReferenceOfInputStreamOptions", "00000000-0000-0000-C000-000000000046"), // TODO ("IReferenceArrayOfInt32", "A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7"), ("IReferenceArrayOfString", "0385688E-E3C7-5C5E-A389-5524EDE349F1"), ("IReferenceArrayOfByte", "2AF22683-3734-56D0-A60E-688CC85D1619"), From 4d390797d8708ad4baafdca60d7ef1f389e1b08e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:15:52 -0800 Subject: [PATCH 07/23] Add InputStreamOptions to interop references Add support for Windows.Storage.Streams.InputStreamOptions in the interop generator: introduce an InputStreamOptions TypeReference in InteropReferences, register the mapping in cswinrt/helpers.h, and treat InputStreamOptions as a known WinRT type in WindowsRuntimeExtensions so the generator recognizes and handles it like other built-in WinRT types. --- .../Extensions/WindowsRuntimeExtensions.cs | 3 ++- src/WinRT.Interop.Generator/References/InteropReferences.cs | 5 +++++ src/cswinrt/helpers.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 8be566056..56af53a94 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -183,7 +183,8 @@ public bool IsManuallyProjectedWindowsRuntimeNonGenericStructOrClassType(Interop SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Point) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Rect) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Size) || - SignatureComparer.IgnoreVersion.Equals(type, interopReferences.EventRegistrationToken); + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.EventRegistrationToken) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.InputStreamOptions); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 8f02d5c69..93b9addbf 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -1494,6 +1494,11 @@ public InteropReferences( /// public TypeReference IBuffer => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IBuffer"u8); + /// + /// Gets the for Windows.Storage.Streams.InputStreamOptions. + /// + public TypeReference InputStreamOptions => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "InputStreamOptions"u8); + /// /// Gets the for . /// diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index cd8226dc2..e86d867dc 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -895,6 +895,7 @@ namespace cswinrt { "Windows.Storage.Streams", { { "IBuffer", "Windows.Storage.Streams", "IBuffer" }, + { "InputStreamOptions", "Windows.Storage.Streams", "InputStreamOptions" }, } }, { "Windows.UI.Xaml", From c2cb53ff14c7fa62300add1ab686bbaa1f7a321e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:22:24 -0800 Subject: [PATCH 08/23] Add IInputStream interface for WinRT streams Add a new IInputStream definition under Windows.Storage.Streams in WinRT.Runtime2. The interface includes licensing header, WinRT metadata attributes (WindowsRuntimeMetadata, Guid, ContractVersion), implements IDisposable, and declares the ReadAsync method returning IAsyncOperationWithProgress with XML docs and remarks describing buffer semantics and usage guidance. --- .../Windows.Storage.Streams/IInputStream.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/WinRT.Runtime2/Windows.Storage.Streams/IInputStream.cs diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/IInputStream.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/IInputStream.cs new file mode 100644 index 000000000..b6e53c825 --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/IInputStream.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using WindowsRuntime; + +namespace Windows.Storage.Streams; + +/// +/// Represents a sequential stream of bytes to be read. +/// +[WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] +[Guid("905A0FE2-BC53-11DF-8C49-001E4FC686DA")] +[ContractVersion(typeof(UniversalApiContract), 65536u)] +public interface IInputStream : IDisposable +{ + /// + /// Reads data from the stream asynchronously. + /// + /// A buffer that may be used to return the bytes that are read. The return value contains the buffer that holds the results. + /// The number of bytes to read that is less than or equal to the value. + /// Specifies the type of the asynchronous read operation. + /// The asynchronous operation. + /// + /// + /// Always read data from the buffer returned in the . Don't assume that the + /// input buffer contains the data. Depending on the implementation, the data that's read might be placed into the input buffer, or it might + /// be returned in a different buffer. For the input buffer, you don't have to implement the interface. Instead, you + /// can create an instance of the Buffer class. + /// + /// + /// Also consider reading a buffer into an by using the + /// ReadBuffer method of the + /// DataReader class. + /// + /// + IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options); +} From 45529525114e226ba636d77249a38a289000ef7b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:23:19 -0800 Subject: [PATCH 09/23] Add ComWrappers marshaller to InputStreamOptions Add the ABI.Windows.Storage.Streams.InputStreamOptionsComWrappersMarshaller attribute to the InputStreamOptions enum in src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs to enable custom ComWrappers-based marshalling for this enum. --- src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs index 17927d3df..c295319a7 100644 --- a/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/InputStreamOptions.cs @@ -19,6 +19,7 @@ namespace Windows.Storage.Streams; [WindowsRuntimeReferenceType(typeof(InputStreamOptions?))] [SupportedOSPlatform("Windows10.0.10240.0")] [ContractVersion(typeof(UniversalApiContract), 65536u)] +[ABI.Windows.Storage.Streams.InputStreamOptionsComWrappersMarshaller] public enum InputStreamOptions : uint { /// From 2298df043db777e0849e3dcc70f013eb8a76b835 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 16:38:14 -0800 Subject: [PATCH 10/23] Add IInputStream ABI marshaller and IID Introduce ABI support for Windows.Storage.Streams.IInputStream by adding a new IInputStream.cs containing marshalling helpers, vtable layout, managed->unmanaged and unmanaged->managed call paths, and a DynamicInterfaceCastable implementation. Also add the IInputStream IID to WellKnownWindowsInterfaceIIDs.g.cs and update the IID template (WellKnownWindowsInterfaceIIDs.tt) to include the new entry so the IID can be generated. --- .../Windows.Storage.Streams/IInputStream.cs | 197 ++++++++++++++++++ .../WellKnownWindowsInterfaceIIDs.g.cs | 25 +++ .../WellKnownWindowsInterfaceIIDs.tt | 1 + 3 files changed, 223 insertions(+) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs new file mode 100644 index 000000000..47902b32e --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Storage.Streams; +using WindowsRuntime; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; +using static System.Runtime.InteropServices.ComWrappers; + +#pragma warning disable CA2256, IDE0008, IDE1006 + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code +[assembly: TypeMap( + value: "Windows.Storage.Streams.IInputStream", + target: typeof(IInputStream), + trimTarget: typeof(IInputStream))] +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + +[assembly: TypeMapAssociation( + source: typeof(IInputStream), + proxy: typeof(ABI.Windows.Storage.Streams.IInputStreamInterfaceImpl))] + +namespace ABI.Windows.Storage.Streams; + +/// +/// Marshaller for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IInputStreamMarshaller +{ + /// + public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(IInputStream? value) + { + return WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(value, in WellKnownWindowsInterfaceIIDs.IID_IInputStream); + } + + /// + public static IInputStream? ConvertToManaged(void* value) + { + return (IInputStream?)WindowsRuntimeObjectMarshaller.ConvertToManaged(value); + } +} + +/// +/// Interop methods for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IInputStreamMethods +{ + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IAsyncOperationWithProgress ReadAsync( + WindowsRuntimeObjectReference thisReference, + IBuffer buffer, + uint count, + InputStreamOptions options) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + using WindowsRuntimeObjectReferenceValue bufferValue = IBufferMarshaller.ConvertToUnmanaged(buffer); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IInputStreamVftbl*)*(void***)thisPtr)->ReadAsync( + thisPtr, + bufferValue.GetThisPtrUnsafe(), + count, + options, + &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern IAsyncOperationWithProgress? ConvertToManaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperationWithProgress'2<<#CsWinRT>Windows.Storage.Streams.IBuffer|uint>Marshaller, WinRT.Interop")] object? _, + void* value); + + return ConvertToManaged(null, result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } +} + +/// +/// Binding type for . +/// +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct IInputStreamVftbl +{ + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetIids; + public delegate* unmanaged[MemberFunction] GetRuntimeClassName; + public delegate* unmanaged[MemberFunction] GetTrustLevel; + public delegate* unmanaged[MemberFunction] ReadAsync; +} + +/// +/// The implementation. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IInputStreamImpl +{ + /// + /// The value for the managed implementation. + /// + [FixedAddressValueType] + private static readonly IInputStreamVftbl Vftbl; + + /// + /// Initializes . + /// + static IInputStreamImpl() + { + *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; + + Vftbl.ReadAsync = &ReadAsync; + } + + /// + /// Gets a pointer to the managed implementation. + /// + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT ReadAsync(void* thisPtr, void* buffer, uint count, InputStreamOptions options, void** result) + { + if (buffer is null || result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IAsyncOperationWithProgress operation = thisObject.ReadAsync( + buffer: IBufferMarshaller.ConvertToManaged(buffer)!, + count: count, + options: options); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperationWithProgress'2<<#CsWinRT>Windows.Storage.Streams.IBuffer|uint>Marshaller, WinRT.Interop")] object? _, + IAsyncOperationWithProgress? value); + + *result = ConvertToUnmanaged(null, operation).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } +} + +/// +/// The implementation for . +/// +[DynamicInterfaceCastableImplementation] +[Guid("00000036-0000-0000-C000-000000000046")] +file interface IInputStreamInterfaceImpl : IInputStream +{ + /// + IAsyncOperationWithProgress IInputStream.ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IInputStream).TypeHandle); + + return IInputStreamMethods.ReadAsync(thisReference, buffer, count, options); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs index 97205b5ee..8b8fa631d 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs @@ -310,6 +310,31 @@ public static ref readonly Guid IID_IMemoryBufferByteAccess } } + /// The IID for IInputStream (905A0FE2-BC53-11DF-8C49-001E4FC686DA). + public static ref readonly Guid IID_IInputStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xE2, 0x0F, 0x5A, 0x90, + 0x53, 0xBC, + 0xDF, 0x11, + 0x8C, + 0x49, + 0x00, + 0x1E, + 0x4F, + 0xC6, + 0x86, + 0xDA + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + /// The IID for IContextCallback (000001DA-0000-0000-C000-000000000046). public static ref readonly Guid IID_IContextCallback { diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt index 3f2ab5534..0c631cb30 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt @@ -29,6 +29,7 @@ var entries = new (string Name, string IID)[] ("IBuffer", "905A0FE0-BC53-11DF-8C49-001E4FC686DA"), ("IBufferByteAccess", "905A0FEF-BC53-11DF-8C49-001E4FC686DA"), ("IMemoryBufferByteAccess", "5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D"), + ("IInputStream", "905A0FE2-BC53-11DF-8C49-001E4FC686DA"), ("IContextCallback", "000001DA-0000-0000-C000-000000000046"), ("ICallbackWithNoReentrancyToApplicationSTA", "0A299774-3E4E-FC42-1D9D-72CEE105CA57"), ("IErrorInfo", "1CF2B120-547D-101B-8E65-08002B2BD119"), From 3b5bf5c3c4d138714bc200e5a4729b52c8f49c4e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 23:43:08 -0800 Subject: [PATCH 11/23] Add IOutputStream interface Introduce Windows.Storage.Streams.IOutputStream in src/WinRT.Runtime2/Windows.Storage.Streams/IOutputStream.cs. The new interface (inherits IDisposable) models a sequential byte output stream and exposes WriteAsync(IBuffer) returning IAsyncOperationWithProgress and FlushAsync() returning IAsyncOperation. Includes WinRT metadata attributes (Guid, ContractVersion) and XML documentation describing behavior and usage. --- .../Windows.Storage.Streams/IOutputStream.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/WinRT.Runtime2/Windows.Storage.Streams/IOutputStream.cs diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/IOutputStream.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/IOutputStream.cs new file mode 100644 index 000000000..fba084aa8 --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/IOutputStream.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using WindowsRuntime; + +namespace Windows.Storage.Streams; + +/// +/// Represents a sequential stream of bytes to be written. +/// +[WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] +[Guid("905A0FE6-BC53-11DF-8C49-001E4FC686DA")] +[ContractVersion(typeof(UniversalApiContract), 65536u)] +public interface IOutputStream : IDisposable +{ + /// + /// Writes data asynchronously in a sequential stream. + /// + /// A buffer that contains the data to be written. + /// + /// The byte writer operation. The first integer represents the number of bytes written. + /// The second integer represents the progress of the write operation. + /// + /// + /// + /// Some stream implementations support queuing of write operations. In this case, the asynchronous execution of the method does not complete until + /// the method has completed. For the buffer parameter, you don't have to implement the interface. Instead, you can create an + /// instance of the Buffer class or create a buffer by using methods in the + /// CryptographicBuffer class. + /// + /// + /// Also consider writing a buffer into an by using the + /// WriteBuffer method of the + /// DataWriter class. + /// + /// + IAsyncOperationWithProgress WriteAsync(IBuffer buffer); + + /// + /// Flushes data asynchronously in a sequential stream. + /// + /// The stream flush operation. + /// + /// The method improves, but does not guarantee durable and coherent storage of data, + /// and it introduces latencies. It's generally recommended to avoid this method to achieve good performance, + /// but it should be used if coherency is desired. + /// + IAsyncOperation FlushAsync(); +} From 760fbbcbb62ddfacffd70daed8b2d950a92059b9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 23:49:15 -0800 Subject: [PATCH 12/23] Add IOutputStream marshaller and IID Add ABI marshalling and runtime support for Windows.Storage.Streams.IOutputStream. Introduces a new IOutputStream.cs that provides the marshaller, unmanaged vtable (IOutputStreamVftbl), managed-to-unmanaged implementations for WriteAsync and FlushAsync, a DynamicInterfaceCastable implementation, and type mappings. Also add the IOutputStream IID to WellKnownWindowsInterfaceIIDs.g.cs and include the entry in the WellKnownWindowsInterfaceIIDs.tt template. This enables proper COM/WinRT interop for IOutputStream (905A0FE6-BC53-11DF-8C49-001E4FC686DA) and error/ownership handling for async operations. --- .../Windows.Storage.Streams/IOutputStream.cs | 256 ++++++++++++++++++ .../WellKnownWindowsInterfaceIIDs.g.cs | 25 ++ .../WellKnownWindowsInterfaceIIDs.tt | 1 + 3 files changed, 282 insertions(+) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs new file mode 100644 index 000000000..15933f98f --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs @@ -0,0 +1,256 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Storage.Streams; +using WindowsRuntime; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; +using static System.Runtime.InteropServices.ComWrappers; + +#pragma warning disable CA2256, IDE0008, IDE1006 + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code +[assembly: TypeMap( + value: "Windows.Storage.Streams.IOutputStream", + target: typeof(IOutputStream), + trimTarget: typeof(IOutputStream))] +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + +[assembly: TypeMapAssociation( + source: typeof(IOutputStream), + proxy: typeof(ABI.Windows.Storage.Streams.IOutputStreamInterfaceImpl))] + +namespace ABI.Windows.Storage.Streams; + +/// +/// Marshaller for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IOutputStreamMarshaller +{ + /// + public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(IOutputStream? value) + { + return WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(value, in WellKnownWindowsInterfaceIIDs.IID_IOutputStream); + } + + /// + public static IOutputStream? ConvertToManaged(void* value) + { + return (IOutputStream?)WindowsRuntimeObjectMarshaller.ConvertToManaged(value); + } +} + +/// +/// Interop methods for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IOutputStreamMethods +{ + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IAsyncOperationWithProgress WriteAsync(WindowsRuntimeObjectReference thisReference, IBuffer buffer) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + using WindowsRuntimeObjectReferenceValue bufferValue = IBufferMarshaller.ConvertToUnmanaged(buffer); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IOutputStreamVftbl*)*(void***)thisPtr)->WriteAsync( + thisPtr, + bufferValue.GetThisPtrUnsafe(), + &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern IAsyncOperationWithProgress? ConvertToManaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperationWithProgress'2Marshaller, WinRT.Interop")] object? _, + void* value); + + return ConvertToManaged(null, result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IAsyncOperation FlushAsync(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IOutputStreamVftbl*)*(void***)thisPtr)->FlushAsync(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern IAsyncOperation? ConvertToManaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperation'1Marshaller, WinRT.Interop")] object? _, + void* value); + + return ConvertToManaged(null, result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } +} + +/// +/// Binding type for . +/// +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct IOutputStreamVftbl +{ + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetIids; + public delegate* unmanaged[MemberFunction] GetRuntimeClassName; + public delegate* unmanaged[MemberFunction] GetTrustLevel; + public delegate* unmanaged[MemberFunction] WriteAsync; + public delegate* unmanaged[MemberFunction] FlushAsync; +} + +/// +/// The implementation. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IOutputStreamImpl +{ + /// + /// The value for the managed implementation. + /// + [FixedAddressValueType] + private static readonly IOutputStreamVftbl Vftbl; + + /// + /// Initializes . + /// + static IOutputStreamImpl() + { + *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; + + Vftbl.WriteAsync = &WriteAsync; + Vftbl.FlushAsync = &FlushAsync; + } + + /// + /// Gets a pointer to the managed implementation. + /// + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT WriteAsync(void* thisPtr, void* buffer, void** result) + { + if (buffer is null || result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IAsyncOperationWithProgress operation = thisObject.WriteAsync(IBufferMarshaller.ConvertToManaged(buffer)!); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperationWithProgress'2Marshaller, WinRT.Interop")] object? _, + IAsyncOperationWithProgress? value); + + *result = ConvertToUnmanaged(null, operation).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT FlushAsync(void* thisPtr, void** result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IAsyncOperation operation = thisObject.FlushAsync(); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] + static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( + [UnsafeAccessorType("ABI.Windows.Foundation.<#CsWinRT>IAsyncOperation'1Marshaller, WinRT.Interop")] object? _, + IAsyncOperation? value); + + *result = ConvertToUnmanaged(null, operation).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } +} + +/// +/// The implementation for . +/// +[DynamicInterfaceCastableImplementation] +[Guid("00000036-0000-0000-C000-000000000046")] +file interface IOutputStreamInterfaceImpl : IOutputStream +{ + /// + IAsyncOperationWithProgress IOutputStream.WriteAsync(IBuffer buffer) + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IOutputStream).TypeHandle); + + return IOutputStreamMethods.WriteAsync(thisReference, buffer); + } + + /// + IAsyncOperation IOutputStream.FlushAsync() + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IOutputStream).TypeHandle); + + return IOutputStreamMethods.FlushAsync(thisReference); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs index 8b8fa631d..4cf7d7f2d 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs @@ -335,6 +335,31 @@ public static ref readonly Guid IID_IInputStream } } + /// The IID for IOutputStream (905A0FE6-BC53-11DF-8C49-001E4FC686DA). + public static ref readonly Guid IID_IOutputStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xE6, 0x0F, 0x5A, 0x90, + 0x53, 0xBC, + 0xDF, 0x11, + 0x8C, + 0x49, + 0x00, + 0x1E, + 0x4F, + 0xC6, + 0x86, + 0xDA + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + /// The IID for IContextCallback (000001DA-0000-0000-C000-000000000046). public static ref readonly Guid IID_IContextCallback { diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt index 0c631cb30..dd29ca83a 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt @@ -30,6 +30,7 @@ var entries = new (string Name, string IID)[] ("IBufferByteAccess", "905A0FEF-BC53-11DF-8C49-001E4FC686DA"), ("IMemoryBufferByteAccess", "5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D"), ("IInputStream", "905A0FE2-BC53-11DF-8C49-001E4FC686DA"), + ("IOutputStream", "905A0FE6-BC53-11DF-8C49-001E4FC686DA"), ("IContextCallback", "000001DA-0000-0000-C000-000000000046"), ("ICallbackWithNoReentrancyToApplicationSTA", "0A299774-3E4E-FC42-1D9D-72CEE105CA57"), ("IErrorInfo", "1CF2B120-547D-101B-8E65-08002B2BD119"), From 03d154842cec396f266432ac3ae8e4ad04cef2ac Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 12 Feb 2026 23:52:40 -0800 Subject: [PATCH 13/23] Add IInputStream and IOutputStream WinRT refs Register Windows.Storage.Streams.IInputStream and IOutputStream across the generator: add TypeReference properties in InteropReferences, add entries to the cswinrt helpers mapping, and update WindowsRuntimeExtensions to recognize these interfaces alongside IBuffer. This ensures the generator correctly identifies and references WinRT input/output stream types. --- .../Extensions/WindowsRuntimeExtensions.cs | 4 +++- .../References/InteropReferences.cs | 10 ++++++++++ src/cswinrt/helpers.h | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 56af53a94..1567a1b46 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -250,7 +250,9 @@ public bool IsManuallyProjectedWindowsRuntimeNonGenericInterfaceType(InteropRefe SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IAsyncInfo) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IVectorChangedEventArgs) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IStringable) || - SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IBuffer); + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IBuffer) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IInputStream) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IOutputStream); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 93b9addbf..a0f3d2916 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -1494,6 +1494,16 @@ public InteropReferences( /// public TypeReference IBuffer => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IBuffer"u8); + /// + /// Gets the for Windows.Storage.Streams.IInputStream. + /// + public TypeReference IInputStream => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IInputStream"u8); + + /// + /// Gets the for Windows.Storage.Streams.IOutputStream. + /// + public TypeReference IOutputStream => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IOutputStream"u8); + /// /// Gets the for Windows.Storage.Streams.InputStreamOptions. /// diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index e86d867dc..5d43a732f 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -895,6 +895,8 @@ namespace cswinrt { "Windows.Storage.Streams", { { "IBuffer", "Windows.Storage.Streams", "IBuffer" }, + { "IInputStream", "Windows.Storage.Streams", "IInputStream" }, + { "IOutputStream", "Windows.Storage.Streams", "IOutputStream" }, { "InputStreamOptions", "Windows.Storage.Streams", "InputStreamOptions" }, } }, From bd094773044f1967ff5fd6fca2ac72a4bd398d26 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 00:01:35 -0800 Subject: [PATCH 14/23] Add IRandomAccessStream interface Introduce Windows.Storage.Streams.IRandomAccessStream to WinRT.Runtime2. The new interface (with WinRT metadata, GUID, and contract version attributes) extends IDisposable, IInputStream, and IOutputStream and exposes CanRead, CanWrite, Position, Size, GetInputStreamAt, GetOutputStreamAt, Seek, and CloneStream. File includes copyright/mit header and XML documentation. --- .../IRandomAccessStream.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs new file mode 100644 index 000000000..b8c176b5a --- /dev/null +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Foundation.Metadata; +using WindowsRuntime; + +namespace Windows.Storage.Streams; + +/// +/// Supports random access of data in input and output streams. +/// +[WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] +[Guid("905A0FE1-BC53-11DF-8C49-001E4FC686DA")] +[ContractVersion(typeof(UniversalApiContract), 65536u)] +public interface IRandomAccessStream : IDisposable, IInputStream, IOutputStream +{ + /// + /// Gets a value that indicates whether the stream can be read from. + /// + bool CanRead { get; } + + /// + /// Gets a value that indicates whether the stream can be written to. + /// + bool CanWrite { get; } + + /// + /// Gets the byte offset of the stream. + /// + /// + /// + /// The initial offset of an is 0. + /// + /// + /// This offset is affected by both and operations. + /// + /// + ulong Position { get; } + + /// + /// Gets or sets the size of the random access stream. + /// + ulong Size { get; set; } + + /// + /// Returns an input stream at a specified location in a stream. + /// + /// The location in the stream at which to begin. + /// The input stream. + IInputStream GetInputStreamAt(ulong position); + + /// + /// Returns an output stream at a specified location in a stream. + /// + /// The location in the output stream at which to begin. + /// The output stream. + IOutputStream GetOutputStreamAt(ulong position); + + /// + /// Sets the position of the stream to the specified valu + /// + /// The new position of the stream. + /// + /// This method does not check the position to make sure the value is valid for the stream. If the position is + /// invalid for the stream, the and + /// methods will return an error when called. + /// + void Seek(ulong position); + + /// + /// Creates a new instance of an over the same resource as the current stream. + /// + /// The new stream. The initial, internal position of the stream is 0. + /// + /// The internal position and lifetime of this new stream are independent from the position and lifetime of the cloned stream. + /// + IRandomAccessStream CloneStream(); +} From 349f6182645158f7af384642dd5e36b9bf34c062 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 09:57:39 -0800 Subject: [PATCH 15/23] Add IRandomAccessStream marshalling and IID Introduce full interop support for Windows.Storage.Streams.IRandomAccessStream: add ABI/IRandomAccessStream.cs containing marshaller, unmanaged vtable (IRandomAccessStreamVftbl), managed-to-unmanaged implementations (IRandomAccessStreamImpl), interface castable proxy, and helper methods for calling the COM methods. Also register type maps/associations for the interface. Update WellKnownWindowsInterfaceIIDs.g.cs and its template (.tt) to include the IRandomAccessStream IID (905A0FE1-BC53-11DF-8C49-001E4FC686DA) so the new marshalling code can reference the correct GUID. This enables proper runtime interop for random-access stream operations. --- .../Windows.Storage.Streams/IInputStream.cs | 2 +- .../Windows.Storage.Streams/IOutputStream.cs | 2 +- .../IRandomAccessStream.cs | 584 ++++++++++++++++++ .../WellKnownWindowsInterfaceIIDs.g.cs | 25 + .../WellKnownWindowsInterfaceIIDs.tt | 1 + 5 files changed, 612 insertions(+), 2 deletions(-) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs index 47902b32e..21a87c682 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs @@ -146,7 +146,7 @@ public static nint Vtable get => (nint)Unsafe.AsPointer(in Vftbl); } - /// + /// [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] private static HRESULT ReadAsync(void* thisPtr, void* buffer, uint count, InputStreamOptions options, void** result) { diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs index 15933f98f..18fbe62d2 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs @@ -170,7 +170,7 @@ public static nint Vtable get => (nint)Unsafe.AsPointer(in Vftbl); } - /// + /// [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] private static HRESULT WriteAsync(void* thisPtr, void* buffer, void** result) { diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs new file mode 100644 index 000000000..761b5f4f7 --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs @@ -0,0 +1,584 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Foundation; +using Windows.Storage.Streams; +using WindowsRuntime; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; +using static System.Runtime.InteropServices.ComWrappers; + +#pragma warning disable CA2256, IDE0008, IDE1006 + +#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code +[assembly: TypeMap( + value: "Windows.Storage.Streams.IRandomAccessStream", + target: typeof(IRandomAccessStream), + trimTarget: typeof(IRandomAccessStream))] +#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + +[assembly: TypeMapAssociation( + source: typeof(IRandomAccessStream), + proxy: typeof(ABI.Windows.Storage.Streams.IRandomAccessStreamInterfaceImpl))] + +namespace ABI.Windows.Storage.Streams; + +/// +/// Marshaller for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IRandomAccessStreamMarshaller +{ + /// + public static WindowsRuntimeObjectReferenceValue ConvertToUnmanaged(IRandomAccessStream? value) + { + return WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(value, in WellKnownWindowsInterfaceIIDs.IID_IRandomAccessStream); + } + + /// + public static IRandomAccessStream? ConvertToManaged(void* value) + { + return (IRandomAccessStream?)WindowsRuntimeObjectMarshaller.ConvertToManaged(value); + } +} + +/// +/// Interop methods for . +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IRandomAccessStreamMethods +{ + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool CanRead(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + bool result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->get_CanRead(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool CanWrite(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + bool result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->get_CanWrite(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong Position(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ulong result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->get_Position(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong Size(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + ulong result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->get_Size(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + return result; + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Size(WindowsRuntimeObjectReference thisReference, ulong value) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->put_Size(thisPtr, value); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IInputStream GetInputStreamAt(WindowsRuntimeObjectReference thisReference, ulong position) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->GetInputStreamAt( + thisPtr, + position, + &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + return IInputStreamMarshaller.ConvertToManaged(result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IOutputStream GetOutputStreamAt(WindowsRuntimeObjectReference thisReference, ulong position) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->GetOutputStreamAt( + thisPtr, + position, + &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + return IOutputStreamMarshaller.ConvertToManaged(result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Seek(WindowsRuntimeObjectReference thisReference, ulong position) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->Seek(thisPtr, position); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + } + + /// + [MethodImpl(MethodImplOptions.NoInlining)] + public static IRandomAccessStream CloneStream(WindowsRuntimeObjectReference thisReference) + { + using WindowsRuntimeObjectReferenceValue thisValue = thisReference.AsValue(); + + void* thisPtr = thisValue.GetThisPtrUnsafe(); + void* result; + + HRESULT hresult = ((IRandomAccessStreamVftbl*)*(void***)thisPtr)->CloneStream(thisPtr, &result); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); + + try + { + return IRandomAccessStreamMarshaller.ConvertToManaged(result)!; + } + finally + { + WindowsRuntimeUnknownMarshaller.Free(result); + } + } +} + +/// +/// Binding type for . +/// +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct IRandomAccessStreamVftbl +{ + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetIids; + public delegate* unmanaged[MemberFunction] GetRuntimeClassName; + public delegate* unmanaged[MemberFunction] GetTrustLevel; + public delegate* unmanaged[MemberFunction] get_Size; + public delegate* unmanaged[MemberFunction] put_Size; + public delegate* unmanaged[MemberFunction] GetInputStreamAt; + public delegate* unmanaged[MemberFunction] GetOutputStreamAt; + public delegate* unmanaged[MemberFunction] get_Position; + public delegate* unmanaged[MemberFunction] Seek; + public delegate* unmanaged[MemberFunction] CloneStream; + public delegate* unmanaged[MemberFunction] get_CanRead; + public delegate* unmanaged[MemberFunction] get_CanWrite; +} + +/// +/// The implementation. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static unsafe class IRandomAccessStreamImpl +{ + /// + /// The value for the managed implementation. + /// + [FixedAddressValueType] + private static readonly IRandomAccessStreamVftbl Vftbl; + + /// + /// Initializes . + /// + static IRandomAccessStreamImpl() + { + *(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable; + + Vftbl.get_Size = &get_Size; + Vftbl.put_Size = &put_Size; + Vftbl.GetInputStreamAt = &GetInputStreamAt; + Vftbl.GetOutputStreamAt = &GetOutputStreamAt; + Vftbl.get_Position = &get_Position; + Vftbl.Seek = &Seek; + Vftbl.CloneStream = &CloneStream; + Vftbl.get_CanRead = &get_CanRead; + Vftbl.get_CanWrite = &get_CanWrite; + } + + /// + /// Gets a pointer to the managed implementation. + /// + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_Size(void* thisPtr, ulong* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.Size; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT put_Size(void* thisPtr, ulong value) + { + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + thisObject.Size = value; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT GetInputStreamAt(void* thisPtr, ulong position, void** result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IInputStream inputStream = thisObject.GetInputStreamAt(position); + + *result = IInputStreamMarshaller.ConvertToUnmanaged(inputStream).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT GetOutputStreamAt(void* thisPtr, ulong position, void** result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IOutputStream outputStream = thisObject.GetOutputStreamAt(position); + + *result = IOutputStreamMarshaller.ConvertToUnmanaged(outputStream).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_Position(void* thisPtr, ulong* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.Position; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT Seek(void* thisPtr, ulong position) + { + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + thisObject.Seek(position); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT CloneStream(void* thisPtr, void** result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + IRandomAccessStream clonedStream = thisObject.CloneStream(); + + *result = IRandomAccessStreamMarshaller.ConvertToUnmanaged(clonedStream).DetachThisPtrUnsafe(); + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_CanRead(void* thisPtr, bool* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.CanRead; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } + + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static HRESULT get_CanWrite(void* thisPtr, bool* result) + { + if (result is null) + { + return WellKnownErrorCodes.E_POINTER; + } + + try + { + var thisObject = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + *result = thisObject.CanWrite; + + return WellKnownErrorCodes.S_OK; + } + catch (Exception e) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(e); + } + } +} + +/// +/// The implementation for . +/// +[DynamicInterfaceCastableImplementation] +[Guid("00000036-0000-0000-C000-000000000046")] +file interface IRandomAccessStreamInterfaceImpl : IRandomAccessStream +{ + /// + bool IRandomAccessStream.CanRead + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.CanRead(thisReference); + } + } + + /// + bool IRandomAccessStream.CanWrite + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.CanWrite(thisReference); + } + } + + /// + ulong IRandomAccessStream.Position + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.Position(thisReference); + } + } + + /// + ulong IRandomAccessStream.Size + { + get + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.Size(thisReference); + } + set + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + IRandomAccessStreamMethods.Size(thisReference, value); + } + } + + /// + IInputStream IRandomAccessStream.GetInputStreamAt(ulong position) + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.GetInputStreamAt(thisReference, position); + } + + /// + IOutputStream IRandomAccessStream.GetOutputStreamAt(ulong position) + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.GetOutputStreamAt(thisReference, position); + } + + /// + void IRandomAccessStream.Seek(ulong position) + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + IRandomAccessStreamMethods.Seek(thisReference, position); + } + + /// + IRandomAccessStream IRandomAccessStream.CloneStream() + { + var thisReference = ((WindowsRuntimeObject)this).GetObjectReferenceForInterface(typeof(IRandomAccessStream).TypeHandle); + + return IRandomAccessStreamMethods.CloneStream(thisReference); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs index 4cf7d7f2d..81cb36ca5 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs @@ -360,6 +360,31 @@ public static ref readonly Guid IID_IOutputStream } } + /// The IID for IRandomAccessStream (905A0FE1-BC53-11DF-8C49-001E4FC686DA). + public static ref readonly Guid IID_IRandomAccessStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xE1, 0x0F, 0x5A, 0x90, + 0x53, 0xBC, + 0xDF, 0x11, + 0x8C, + 0x49, + 0x00, + 0x1E, + 0x4F, + 0xC6, + 0x86, + 0xDA + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + /// The IID for IContextCallback (000001DA-0000-0000-C000-000000000046). public static ref readonly Guid IID_IContextCallback { diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt index dd29ca83a..d16ffc10b 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt @@ -31,6 +31,7 @@ var entries = new (string Name, string IID)[] ("IMemoryBufferByteAccess", "5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D"), ("IInputStream", "905A0FE2-BC53-11DF-8C49-001E4FC686DA"), ("IOutputStream", "905A0FE6-BC53-11DF-8C49-001E4FC686DA"), + ("IRandomAccessStream", "905A0FE1-BC53-11DF-8C49-001E4FC686DA"), ("IContextCallback", "000001DA-0000-0000-C000-000000000046"), ("ICallbackWithNoReentrancyToApplicationSTA", "0A299774-3E4E-FC42-1D9D-72CEE105CA57"), ("IErrorInfo", "1CF2B120-547D-101B-8E65-08002B2BD119"), From 9b05aaacbb41abe27133813534a42ca874447091 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 09:59:34 -0800 Subject: [PATCH 16/23] Add IRandomAccessStream support Add support for Windows.Storage.Streams.IRandomAccessStream across the generator and helpers: register IRandomAccessStream in cswinrt/helpers.h, add an IRandomAccessStream TypeReference in InteropReferences, and include it in WindowsRuntimeExtensions.IsStream checks so it is recognized as a stream type. --- .../Extensions/WindowsRuntimeExtensions.cs | 3 ++- src/WinRT.Interop.Generator/References/InteropReferences.cs | 5 +++++ src/cswinrt/helpers.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 1567a1b46..da9d56ac4 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -252,7 +252,8 @@ public bool IsManuallyProjectedWindowsRuntimeNonGenericInterfaceType(InteropRefe SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IStringable) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IBuffer) || SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IInputStream) || - SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IOutputStream); + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IOutputStream) || + SignatureComparer.IgnoreVersion.Equals(type, interopReferences.IRandomAccessStream); } /// diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index a0f3d2916..e0b1f24c8 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -1504,6 +1504,11 @@ public InteropReferences( /// public TypeReference IOutputStream => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IOutputStream"u8); + /// + /// Gets the for Windows.Storage.Streams.IRandomAccessStream. + /// + public TypeReference IRandomAccessStream => field ??= _windowsRuntimeModule.CreateTypeReference("Windows.Storage.Streams"u8, "IRandomAccessStream"u8); + /// /// Gets the for Windows.Storage.Streams.InputStreamOptions. /// diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 5d43a732f..6cd6c4fbb 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -897,6 +897,7 @@ namespace cswinrt { "IBuffer", "Windows.Storage.Streams", "IBuffer" }, { "IInputStream", "Windows.Storage.Streams", "IInputStream" }, { "IOutputStream", "Windows.Storage.Streams", "IOutputStream" }, + { "IRandomAccessStream", "Windows.Storage.Streams", "IRandomAccessStream" }, { "InputStreamOptions", "Windows.Storage.Streams", "InputStreamOptions" }, } }, From 5ce2da409439ec5266907413b415d47790e5f4a8 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 11:52:11 -0800 Subject: [PATCH 17/23] Update GUIDs for Windows.Storage.Streams interfaces Replace incorrect/common GUID used for DynamicInterfaceCastableImplementation with the correct WinRT interface IDs for several Storage.Streams types. Updated IBuffer, IInputStream, IOutputStream, and IRandomAccessStream implementations to use their proper GUIDs to ensure correct runtime casting/interop. Changes: - IBuffer: 00000036-0000-0000-C000-000000000046 -> 905A0FE0-BC53-11DF-8C49-001E4FC686DA - IRandomAccessStream: 00000036-0000-0000-C000-000000000046 -> 905A0FE1-BC53-11DF-8C49-001E4FC686DA - IInputStream: 00000036-0000-0000-C000-000000000046 -> 905A0FE2-BC53-11DF-8C49-001E4FC686DA - IOutputStream: 00000036-0000-0000-C000-000000000046 -> 905A0FE6-BC53-11DF-8C49-001E4FC686DA This fixes interface identity mismatches that could break dynamic casting or ABI interop. --- src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs | 2 +- src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs | 2 +- src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs | 2 +- .../ABI/Windows.Storage.Streams/IRandomAccessStream.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs index 24392e088..fb1c0d584 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IBuffer.cs @@ -226,7 +226,7 @@ private static HRESULT set_Length(void* thisPtr, uint value) /// The implementation for . /// [DynamicInterfaceCastableImplementation] -[Guid("00000036-0000-0000-C000-000000000046")] +[Guid("905A0FE0-BC53-11DF-8C49-001E4FC686DA")] file interface IBufferInterfaceImpl : IBuffer { /// diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs index 21a87c682..9ac20015b 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IInputStream.cs @@ -184,7 +184,7 @@ static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( /// The implementation for . /// [DynamicInterfaceCastableImplementation] -[Guid("00000036-0000-0000-C000-000000000046")] +[Guid("905A0FE2-BC53-11DF-8C49-001E4FC686DA")] file interface IInputStreamInterfaceImpl : IInputStream { /// diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs index 18fbe62d2..cac59c774 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IOutputStream.cs @@ -235,7 +235,7 @@ static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged( /// The implementation for . /// [DynamicInterfaceCastableImplementation] -[Guid("00000036-0000-0000-C000-000000000046")] +[Guid("905A0FE6-BC53-11DF-8C49-001E4FC686DA")] file interface IOutputStreamInterfaceImpl : IOutputStream { /// diff --git a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs index 761b5f4f7..7be716954 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Storage.Streams/IRandomAccessStream.cs @@ -497,7 +497,7 @@ private static HRESULT get_CanWrite(void* thisPtr, bool* result) /// The implementation for . /// [DynamicInterfaceCastableImplementation] -[Guid("00000036-0000-0000-C000-000000000046")] +[Guid("905A0FE1-BC53-11DF-8C49-001E4FC686DA")] file interface IRandomAccessStreamInterfaceImpl : IRandomAccessStream { /// From 6df8974165ec6ee04cc6489549bf299f3556ce55 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 12:45:40 -0800 Subject: [PATCH 18/23] Remove unnecessary null-forgiving operators Remove redundant null-forgiving ('!') operators from enumerator and collection code where runtime checks already guarantee non-null values. Cleans up nullable handling and improves code clarity across several files: - src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs - src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs - src/WinRT.Runtime2/Collections/DictionaryKeyCollection{TKey, TValue}.cs - src/WinRT.Runtime2/Collections/DictionaryValueCollection{TKey, TValue}.cs - src/WinRT.Runtime2/Collections/ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs - src/WinRT.Runtime2/Collections/ReadOnlyDictionaryValueCollection{TKey, TValue}.cs - src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs --- src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs | 2 +- .../Collections/DictionaryKeyCollection{TKey, TValue}.cs | 2 +- .../Collections/DictionaryValueCollection{TKey, TValue}.cs | 2 +- .../ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs | 2 +- .../ReadOnlyDictionaryValueCollection{TKey, TValue}.cs | 2 +- src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs | 2 +- .../IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs b/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs index 428ed44b1..d0a4312bc 100644 --- a/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs +++ b/src/WinRT.Runtime2/Bindables/WindowsRuntimeEnumerator.cs @@ -61,7 +61,7 @@ public object? Current throw new InvalidOperationException("InvalidOperation_EnumEnded"); } - return field!; + return field; } private set; } diff --git a/src/WinRT.Runtime2/Collections/DictionaryKeyCollection{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/DictionaryKeyCollection{TKey, TValue}.cs index 01be60c17..41e3c32a5 100644 --- a/src/WinRT.Runtime2/Collections/DictionaryKeyCollection{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/DictionaryKeyCollection{TKey, TValue}.cs @@ -135,7 +135,7 @@ public Enumerator(IEnumerable> enumerable) } /// - public TKey Current => _enumerator!.Current.Key; + public TKey Current => _enumerator.Current.Key; /// object IEnumerator.Current => Current!; diff --git a/src/WinRT.Runtime2/Collections/DictionaryValueCollection{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/DictionaryValueCollection{TKey, TValue}.cs index 11b4bd25a..b382430ed 100644 --- a/src/WinRT.Runtime2/Collections/DictionaryValueCollection{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/DictionaryValueCollection{TKey, TValue}.cs @@ -144,7 +144,7 @@ public Enumerator(IEnumerable> enumerable) } /// - public TValue Current => _enumerator!.Current.Value; + public TValue Current => _enumerator.Current.Value; /// object IEnumerator.Current => Current!; diff --git a/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs index c93854e0c..7b9ef3047 100644 --- a/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryKeyCollection{TKey, TValue}.cs @@ -78,7 +78,7 @@ public Enumerator(IEnumerable> enumerable) } /// - public TKey Current => _enumerator!.Current.Key; + public TKey Current => _enumerator.Current.Key; /// object IEnumerator.Current => Current!; diff --git a/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryValueCollection{TKey, TValue}.cs b/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryValueCollection{TKey, TValue}.cs index 7c6b90715..00ed96700 100644 --- a/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryValueCollection{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/Collections/ReadOnlyDictionaryValueCollection{TKey, TValue}.cs @@ -78,7 +78,7 @@ public Enumerator(IEnumerable> enumerable) } /// - public TValue Current => _enumerator!.Current.Value; + public TValue Current => _enumerator.Current.Value; /// object IEnumerator.Current => Current!; diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs index 6019439e6..c7f968aaa 100644 --- a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs +++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs @@ -70,7 +70,7 @@ public T Current throw new InvalidOperationException("InvalidOperation_EnumEnded"); } - return field!; + return field; } private set; } diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs index aeb2ebd97..1eda80045 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs @@ -99,7 +99,7 @@ public TValue this[TKey key] ThrowKeyNotFoundException(); } - return value!; + return value; } } From 885b6caa15cf39edb4aee341a38d1d92e694d970 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 12:47:46 -0800 Subject: [PATCH 19/23] Suppress CS9264 in WindowsRuntimeEnumerator Add CS9264 to the #pragma warning disable list in WindowsRuntimeEnumerator{T}.cs so the CS9264 compiler warning is suppressed alongside the existing CA1816 and IDE0046 disables. --- src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs index c7f968aaa..ea2737327 100644 --- a/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs +++ b/src/WinRT.Runtime2/Collections/WindowsRuntimeEnumerator{T}.cs @@ -8,7 +8,7 @@ using System.Diagnostics.CodeAnalysis; using WindowsRuntime.InteropServices; -#pragma warning disable CA1816, IDE0046 +#pragma warning disable CS9264, CA1816, IDE0046 namespace WindowsRuntime; From ca0a0ee2fb7528c80edc7a451af9710da0d7504f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 13:34:44 -0800 Subject: [PATCH 20/23] Fix IID for IReferenceOfInputStreamOptions Replace placeholder IID for IReferenceOfInputStreamOptions with the correct GUID (AEA3CA95-06D3-5B19-A94D-907D1E6BCF18). Update the generated byte array in WellKnownWindowsInterfaceIIDs.g.cs and the source template WellKnownWindowsInterfaceIIDs.tt (remove TODO). This ensures the runtime exposes the correct interface identifier. --- .../WellKnownWindowsInterfaceIIDs.g.cs | 24 +++++++++---------- .../WellKnownWindowsInterfaceIIDs.tt | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs index 81cb36ca5..a07806bcc 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.g.cs @@ -2385,7 +2385,7 @@ public static ref readonly Guid IID_IReferenceOfCollectionChange } } - /// The IID for IReferenceOfInputStreamOptions (00000000-0000-0000-C000-000000000046). + /// The IID for IReferenceOfInputStreamOptions (AEA3CA95-06D3-5B19-A94D-907D1E6BCF18). public static ref readonly Guid IID_IReferenceOfInputStreamOptions { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -2393,17 +2393,17 @@ public static ref readonly Guid IID_IReferenceOfInputStreamOptions { ReadOnlySpan data = [ - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xC0, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x46 + 0x95, 0xCA, 0xA3, 0xAE, + 0xD3, 0x06, + 0x19, 0x5B, + 0xA9, + 0x4D, + 0x90, + 0x7D, + 0x1E, + 0x6B, + 0xCF, + 0x18 ]; return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt index d16ffc10b..0ecfcde1c 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownWindowsInterfaceIIDs.tt @@ -112,7 +112,7 @@ var entries = new (string Name, string IID)[] ("IReferenceOfAsyncStatus", "A4B74936-2947-5FE8-88D5-51CD35050E71"), ("IReferenceOfAsyncActionCompletedHandler", "F31DBB29-606D-5A89-A23A-C09AB9605B8F"), ("IReferenceOfCollectionChange", "25BCAF91-880D-537D-82FC-9BBF0CACCB8B"), - ("IReferenceOfInputStreamOptions", "00000000-0000-0000-C000-000000000046"), // TODO + ("IReferenceOfInputStreamOptions", "AEA3CA95-06D3-5B19-A94D-907D1E6BCF18"), ("IReferenceArrayOfInt32", "A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7"), ("IReferenceArrayOfString", "0385688E-E3C7-5C5E-A389-5524EDE349F1"), ("IReferenceArrayOfByte", "2AF22683-3734-56D0-A60E-688CC85D1619"), From 53fbaa30571584b0f926c0c3bb5b019e9dc42185 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 13:35:35 -0800 Subject: [PATCH 21/23] Fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Windows.Storage.Streams/IRandomAccessStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs b/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs index b8c176b5a..35ec70bb8 100644 --- a/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs +++ b/src/WinRT.Runtime2/Windows.Storage.Streams/IRandomAccessStream.cs @@ -60,7 +60,7 @@ public interface IRandomAccessStream : IDisposable, IInputStream, IOutputStream IOutputStream GetOutputStreamAt(ulong position); /// - /// Sets the position of the stream to the specified valu + /// Sets the position of the stream to the specified value. /// /// The new position of the stream. /// From badfd580f77a2f550b4871a70cc595505dba8ea2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 14:15:33 -0800 Subject: [PATCH 22/23] Add Storage.Streams interface IIDs Add IID mappings for Windows.Storage.Streams interfaces. Updated the generator references to recognize IBuffer, IInputStream, IOutputStream and IRandomAccessStream; added corresponding ref readonly Guid properties in WellKnownInterfaceIIDs.g.cs that delegate to WellKnownWindowsInterfaceIIDs; and updated the WellKnownInterfaceIIDs.tt template to include the new entries. This enables correct projection/mapping of storage stream interfaces. --- .../References/WellKnownInterfaceIIDs.cs | 8 +++++++ .../WellKnownInterfaceIIDs.g.cs | 21 +++++++++++++++++++ .../InteropServices/WellKnownInterfaceIIDs.tt | 5 ++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs index c65320373..ec98eb000 100644 --- a/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs +++ b/src/WinRT.Interop.Generator/References/WellKnownInterfaceIIDs.cs @@ -88,6 +88,14 @@ _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.I => "Windows_Foundation_IAsyncAction", _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IVectorChangedEventArgs) => "Windows_Foundation_Collections_IVectorChangedEventArgs", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IBuffer) + => "Windows_Storage_Streams_IBuffer", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IInputStream) + => "Windows_Storage_Streams_IInputStream", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IOutputStream) + => "Windows_Storage_Streams_IOutputStream", + _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IRandomAccessStream) + => "Windows_Storage_Streams_IRandomAccessStream", // XAML types _ when SignatureComparer.IgnoreVersion.Equals(interfaceType, interopReferences.IEnumerable) && useWindowsUIXamlProjections diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs index 95b3757eb..2ca6a8e85 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs @@ -211,4 +211,25 @@ public static ref readonly Guid IID_Windows_Storage_Streams_IBuffer [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBuffer; } + + /// The IID for Windows.Storage.Streams.IInputStream (mapped to . + public static ref readonly Guid IID_Windows_Storage_Streams_IInputStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IInputStream; + } + + /// The IID for Windows.Storage.Streams.IOutputStream (mapped to . + public static ref readonly Guid IID_Windows_Storage_Streams_IOutputStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IOutputStream; + } + + /// The IID for Windows.Storage.Streams.IRandomAccessStream (mapped to . + public static ref readonly Guid IID_Windows_Storage_Streams_IRandomAccessStream + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IRandomAccessStream; + } } \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt index a790bc7bd..6ddf0ca64 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt @@ -50,7 +50,10 @@ var entries = new (string ProjectedFullyQualifiedName, string WindowsRuntimeFull ("Windows.Foundation.Collections.IVectorChangedEventArgs", null, false), ("Windows.Foundation.IAsyncAction", null, false), ("Windows.Foundation.IAsyncInfo", null, false), - ("Windows.Storage.Streams.IBuffer", null, false) + ("Windows.Storage.Streams.IBuffer", null, false), + ("Windows.Storage.Streams.IInputStream", null, false), + ("Windows.Storage.Streams.IOutputStream", null, false), + ("Windows.Storage.Streams.IRandomAccessStream", null, false), }; for (int i = 0; i < entries.Length; i++) From 130ee6997f4deaa90b89dedb71bdaf4c2756c343 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 13 Feb 2026 16:27:51 -0800 Subject: [PATCH 23/23] Use WellKnownInterfaceIIDs for IBuffer IID Replace the IID reference for IBuffer in WindowsRuntimeBuffer static initialization to use WindowsRuntime.InteropServices.WellKnownInterfaceIIDs instead of ABI.InterfaceIIDs. This unifies the IID source with the WellKnownInterfaceIIDs set and aligns the code with other interface IID usages. --- .../additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs index 6d1be03c0..1a522bcb2 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs @@ -246,7 +246,7 @@ file static class WindowsRuntimeBufferInterfaceEntriesImpl /// static WindowsRuntimeBufferInterfaceEntriesImpl() { - Entries.IBuffer.IID = global::ABI.InterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; + Entries.IBuffer.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; Entries.IBuffer.Vtable = global::ABI.Windows.Storage.Streams.IBufferImpl.Vtable; Entries.IBufferByteAccess.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IBufferByteAccess; Entries.IBufferByteAccess.Vtable = global::ABI.Windows.Storage.Streams.IBufferByteAccessImpl.Vtable;