Skip to content

Commit 51a7727

Browse files
Sergio0694Copilot
andauthored
Port 'WindowsRuntimeBufferExtensions' to 'WinRT.Runtime.dll' (#2298)
* Convert Buffer property to Buffer() method Change WindowsRuntimePinnedArrayBuffer.Buffer from an internal property getter to a public unsafe Buffer() method (inlined) and update call sites. Also make GetArraySegment public, add an XML <returns> doc and AggressiveInlining, and remove a redundant Debug.Assert for Capacity. Update ABI and WindowsRuntimeBufferMarshal usages to call Buffer(). These changes expose the buffer access method and unify callers to the new signature. * Add WindowsRuntimeExternalArrayBuffer for external arrays Introduce WindowsRuntimeExternalArrayBuffer, an internal sealed implementation of IBuffer that wraps an external byte[] with support for offset, length and capacity. Provides Buffer() and GetArraySegment() APIs and a Length setter that enforces capacity (throws E_BOUNDS). Implements a pinning strategy using PinnedGCHandle and caches the pinned data pointer with Interlocked.CompareExchange to be thread-safe, and disposes the pinned handle in a finalizer. Includes debug assertions for constructor parameters and is marked as a managed-only WinRT type. * Add WindowsRuntimeExternalArrayBuffer marshaller Introduce ABI support and a custom marshaller for WindowsRuntimeExternalArrayBuffer. Adds TypeMapAssociation, a file-scoped ABI type, and a fixed set of ComInterfaceEntry vtables (IBuffer, IBufferByteAccess, IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, IUnknown). Implements a WindowsRuntimeComWrappersMarshallerAttribute that provides vtable computation and GetOrCreateComInterfaceForObject behavior, and a native IBufferByteAccess implementation with an unmanaged Buffer method that returns the underlying byte pointer and converts exceptions to HRESULTs. * Support WindowsRuntimeExternalArrayBuffer cases Add handling for the WindowsRuntimeExternalArrayBuffer type in WindowsRuntimeBufferMarshal to extract underlying memory and array segments. Introduces early-return branches that call Buffer()/GetArraySegment() on external array buffers and adds clarifying comments; existing pinned-array handling remains unchanged. * Flatten Windows.Storage.Streams paths Remove an extra 'Streams' nesting by relocating several Windows.Storage.Streams files into the Windows.Storage/Streams folder. This cleans up the directory layout to better match namespaces and simplify imports. Files moved: - Windows.Storage.Streams/Buffers/WindowsRuntimeBuffer.cs (from ...Streams/Buffers/...) - Windows.Storage/Streams/IBuffer.cs (from ...Streams/Streams/...) - Windows.Storage/Streams/IInputStream.cs (from ...Streams/Streams/...) - Windows.Storage/Streams/IOutputStream.cs (from ...Streams/Streams/...) - Windows.Storage/Streams/IRandomAccessStream.cs (from ...Streams/Streams/...) - Windows.Storage/Streams/InputStreamOptions.cs (from ...Streams/Streams/...) * Use ThrowIfBufferLengthExceedsCapacity helper Replace the manual bounds check and construction of ArgumentOutOfRangeException in the Length setter with ArgumentOutOfRangeException.ThrowIfBufferLengthExceedsCapacity(value, Capacity). This centralizes validation, simplifies the code, and ensures consistent exception details (including HResult) when the buffer length exceeds capacity. * Add GetSpan to external and pinned buffers Introduce ReadOnlySpan<byte> GetSpan() implementations for WindowsRuntimePinnedArrayBuffer and WindowsRuntimeExternalArrayBuffer. Both methods use MemoryMarshal.GetArrayDataReference and Unsafe.Add to create a ReadOnlySpan via MemoryMarshal.CreateReadOnlySpan and are annotated with AggressiveInlining. The pinned variant notes that parameters were validated at construction so offset/capacity checks are skipped; the external variant references the same rationale to avoid extra overhead while providing efficient span access to the buffer data. * Rename GetSpan to GetSpanForCapacity Rename WindowsRuntimePinnedArrayBuffer.GetSpan to GetSpanForCapacity to make the semantics explicit: the returned ReadOnlySpan<byte> covers Capacity (not Length). Update WindowsRuntimeExternalArrayBuffer XML doc cref to point to the new member and add a remark documenting the capacity-based length. Method inlining and implementation remain unchanged. * Add IBuffer AsBuffer extensions for byte[] Introduce WindowsRuntimeBufferExtensions with three AsBuffer overloads for byte[]: AsBuffer(source), AsBuffer(source, offset, length) and AsBuffer(source, offset, length, capacity). The methods validate arguments and return a WindowsRuntimeExternalArrayBuffer that exposes the byte array as an IBuffer with specified length and capacity. Includes MIT header and preserved commented exception message checks for range/capacity validation. * Use Span for buffer capacity and add CopyTo Change ReadOnlySpan<byte> -> Span<byte> for GetSpanForCapacity in WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer to allow mutable access and use MemoryMarshal.CreateSpan. Add a new Argument_InvalidIBufferInstance message. Extend WindowsRuntimeBufferExtensions: adjust AsBuffer overloads to use named args, add CopyTo(ReadOnlySpan<byte>, IBuffer[, uint]) methods to efficiently copy into IBuffer (with GC.KeepAlive and Length update semantics), and add a private GetSpanForCapacity(IBuffer) helper that retrieves a Span<byte> for native, external-array, or pinned-array backed buffers and throws the new argument exception for unsupported IBuffer instances. Also add a few using directives required by the new code. * Add byte[] CopyTo overloads and improve docs Refine XML docs for ReadOnlySpan<byte>.CopyTo overloads (singular "value" wording) and add missing <exception> tags documenting ArgumentNullException, ArgumentException and ArgumentOutOfRangeException cases. Introduce two new byte[] CopyTo overloads: CopyTo(byte[] source, IBuffer destination) and CopyTo(byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count). Both delegate to the existing ReadOnlySpan<byte> implementations (using AsSpan) and perform null checks before calling into the span-based logic. * Add IBuffer-to-Span CopyTo overloads Add two CopyTo overloads to copy from IBuffer to Span<byte> (full-buffer and ranged variants). Implement argument checks, early-return for zero-length copies, use GetSpanForCapacity(...).Slice for span slicing and call GC.KeepAlive(source) after the copy. Also replace a range-based span slice with explicit Slice, add a pragma to suppress IDE0057, and update XML docs and exception wording to better describe destination capacity requirements. * Add byte[] overloads for IBuffer.CopyTo Add two convenience overloads for copying IBuffer contents to byte arrays: CopyTo(this IBuffer, byte[]) and CopyTo(this IBuffer, uint sourceIndex, byte[] destination, int destinationIndex, int count). Both perform null checks and forward to the existing Span-based CopyTo implementation. Also update the XML summary on the Span-based CopyTo to correctly reference Span<T>. These changes make it easier for callers using arrays to copy buffer data without manually creating spans. * Add IBuffer CopyTo overloads and safety checks Add two IBuffer.CopyTo overloads to copy entire buffers or a specified range between IBuffer instances, including XML docs. Introduce System.Diagnostics and Debug.Assert checks for int.MaxValue bounds, early-return for zero-length copies, Span-based copy logic, GC.KeepAlive calls, and update destination.Length after copy. Also add Debug.Assert guards in existing copy helpers and argument null checks to improve safety and correctness. * Add IBuffer.ToArray extension methods Introduce two ToArray extension overloads for IBuffer: a parameterless variant that copies the entire buffer and an overload that accepts a sourceIndex and count. Both perform null/argument validation, handle a zero-length fast path (returning an empty array), allocate an uninitialized byte[] via GC.AllocateUninitializedArray, and copy data using the buffer's CopyTo method. Some additional range checks (Array.MaxLength and capacity/remaining-space checks) are present but commented out. * Add IsSameData and TryGetSpan helpers Introduce IsSameData(IBuffer, IBuffer?) to detect whether two IBuffer instances share the same underlying memory (handles both managed and native-backed buffers). Add TryGetNativeSpanForCapacity and TryGetManagedSpanForCapacity to safely obtain Span<byte> for a buffer's Capacity without throwing/pinning, and refactor GetSpanForCapacity to use these helpers. Document potential exceptions for IBufferByteAccess.Buffer failures on various CopyTo/ToArray APIs and add necessary using/imports and attributes. These changes avoid unnecessary pinning, centralize native/managed span retrieval, and improve diagnostics when interacting with IBuffer internals. * Add IBuffer.GetByte extension method Introduce GetByte(this IBuffer, uint) to WindowsRuntimeBufferExtensions with XML docs. The method validates the source is not null, obtains a Span<byte> via GetSpanForCapacity, reads the byte at the specified offset, and calls GC.KeepAlive to ensure the buffer remains alive. Provides a convenient, safe way to read a single byte from an IBuffer instance. * Add IBuffer-backed MemoryStream wrapper Introduce WindowsRuntimeBufferMemoryStream, an internal sealed MemoryStream implementation backed by a Windows.Storage.Streams.IBuffer. The stream constructor accepts an IBuffer, a backing byte[] and an offset and initializes the stream length from the buffer. Overrides SetLength, Write (sync and span), WriteAsync (task and ValueTask) and WriteByte to keep the IBuffer.Length in sync with the stream. This supports managed-to-WinRT buffer interop without parameter validation in the constructor. * Add UnmanagedMemoryStream backed by IBuffer Introduce WindowsRuntimeBufferUnmanagedMemoryStream: an UnmanagedMemoryStream implementation backed by a native IBuffer. The new class exposes a constructor accepting an IBuffer and native byte* memory, overrides SetLength and various Write APIs (sync/async/Span/byte) to keep the IBuffer.Length in sync, and uses FileAccess.ReadWrite. Also add a private _buffer field (with comment) to WindowsRuntimeBufferMemoryStream.cs. Note: the UnmanagedMemoryStream constructor does not validate parameters and uses unsafe pointer input. * Add GetArray and IBuffer AsStream helpers Expose underlying array accessors on WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer (GetArray(out int offset)), and add MemoryStream <-> IBuffer helpers in WindowsRuntimeBufferExtensions: GetWindowsRuntimeBuffer overloads to create IBuffer from MemoryStream, and AsStream to wrap an IBuffer as a Stream (handles array-backed and native buffers). Also add System.IO using and fix inverted logic in GetSpanForCapacity to correctly throw for invalid IBuffer instances. These changes enable sharing memory between MemoryStream and IBuffer and provide a convenient Stream view over IBuffer data. * Use exception helpers for buffer checks Add a set of ArgumentException/ArgumentOutOfRange/UnauthorizedAccess helpers to WindowsRuntimeExceptionExtensions and corresponding message constants to WindowsRuntimeExceptionMessages. Replace in-file commented/manual validation checks in WindowsRuntimeBufferExtensions with calls to the new ThrowIf* helpers (e.g. ThrowIfInsufficientArrayElementsAfterOffset, ThrowIfInsufficientBufferCapacity, ThrowIfBufferIndexExceedsCapacity/Length, ThrowIfInsufficientSpaceInSourceBuffer/TargetBuffer, ThrowIfStreamPositionBeyondEndOfStream, ThrowIfBufferLengthExceedsArrayMaxLength, ThrowInvalidIBufferInstance, ThrowInternalBufferAccess). Helpers are annotated for inlining/stack-trace hiding and centralize error text creation for clearer, consistent validation and minor performance improvements. * Remove WindowsRuntimeBuffer and related files Delete WinRT buffer implementation and helpers for Windows.Storage.Streams (IBufferByteAccess.cs, IMarshal.cs, IMemoryBufferByteAccess.cs, WindowsRuntimeBuffer.cs, WindowsRuntimeBufferExtensions.cs) and remove their entries from src/cswinrt/cswinrt.vcxproj and cswinrt.vcxproj.filters. Cleans up deprecated/unneeded buffer wrapper code and project file references. * Fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix typos Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Use BufferOffset exception message and add constant Replace the incorrect Argument_BufferIndexExceedsLength usage with a new Argument_BufferOffsetExceedsLength in WindowsRuntimeExceptionExtensions to provide a more accurate error for offset checks. Add the corresponding constant message to WindowsRuntimeExceptionMessages. * Refactor IBuffer unwrap and access helpers Introduce clearer managed/native unwrapping helpers and simplify buffer logic. Adds TryGetArray and TryGetData to handle managed arrays (array + offset) and native pointers respectively, replaces span-based helpers, and updates equality, span retrieval and stream creation paths to use the new helpers. GetSpanForCapacity now prefers managed arrays, then native data, and throws for unrecognized buffers. Also removes the now-unused ThrowInvalidIBufferInstance helper from WindowsRuntimeExceptionExtensions.cs. These changes reduce low-level unsafe span usage, centralize native access, and make equality/stream logic rely on array/offset or raw pointers. * Remove unused usings in buffer extensions Remove unused using directives System.Runtime.CompilerServices and System.Runtime.InteropServices from WindowsRuntimeBufferExtensions.cs to clean up imports. No functional changes; reduces compiler warnings for unused namespaces. * Add Windows Buffers using; remove attribute Remove the special-case emission of the TypeMapAssociation assembly attribute from src/cswinrt/main.cpp. Add using Windows.Storage.Buffers and tidy up using directives in StreamOperationsImplementation.cs, WinRtIOHelper.cs and WinRtToNetFxStreamAdapter.cs (also adjust placement of System.Diagnostics.CodeAnalysis). These changes prepare the additions to use Windows.Storage.Buffers types and stop emitting the assembly-level TypeMapAssociation here. * Remove unused WindowsRuntime usings Remove the unused System.Runtime.InteropServices.WindowsRuntime using directives from WinRtIOHelper.cs and WinRtToNetFxStreamAdapter.cs. This is a tidy-up to eliminate redundant imports (and related compiler warnings); no functional changes. * Stub WindowsRuntimeBuffer handling with TODOs Replace direct WindowsRuntimeBuffer cast and TryGetUnderlyingData usage with null/placeholder assignments and TODO comments in StreamOperationsImplementation.cs. This temporarily disables the optimized managed-array path (the buffer.TryGetUnderlyingData branch) and keeps the generic allocation fallback intact. Restore the original cast and TryGetUnderlyingData calls later to recover the optimized behavior. * Remove WindowsRuntime using; add Buffers namespace Remove obsolete System.Runtime.InteropServices.WindowsRuntime using directives from multiple test files and add using Windows.Storage.Buffers where needed. Affected files: FunctionalTests/Async/Program.cs, ObjectLifetimeTests/{App,MainWindow,MyControl,ObjectLifetimePage}.xaml.cs, UnitTest/ApiCompatTests.cs, and UnitTest/TestComponentCSharp_Tests.cs. This updates imports to use Windows.Storage.Buffers for buffer-related types and cleans up unnecessary WindowsRuntime interop using statements. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent db15e77 commit 51a7727

34 files changed

Lines changed: 1531 additions & 1353 deletions

src/Tests/FunctionalTests/Async/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using System;
22
using System.IO;
33
using System.Runtime.InteropServices;
4-
using System.Runtime.InteropServices.WindowsRuntime;
54
using System.Threading.Tasks;
65
using TestComponentCSharp;
76
using Windows.Foundation;
87
using Windows.Foundation.Tasks;
8+
using Windows.Storage.Buffers;
99
using Windows.Storage.Streams;
1010
using Windows.Web.Http;
1111
using WindowsRuntime.InteropServices;

src/Tests/ObjectLifetimeTests/App.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Collections.Generic;
1111
using System.IO;
1212
using System.Linq;
13-
using System.Runtime.InteropServices.WindowsRuntime;
1413
using Windows.ApplicationModel;
1514
using Windows.ApplicationModel.Activation;
1615
using Windows.Foundation;

src/Tests/ObjectLifetimeTests/MainWindow.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Collections.Generic;
1010
using System.IO;
1111
using System.Linq;
12-
using System.Runtime.InteropServices.WindowsRuntime;
1312
using Windows.Foundation;
1413
using Windows.Foundation.Collections;
1514

src/Tests/ObjectLifetimeTests/MyControl.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Runtime.InteropServices.WindowsRuntime;
65
using Windows.Foundation;
76
using Windows.Foundation.Collections;
87
using Microsoft.UI.Xaml;

src/Tests/ObjectLifetimeTests/ObjectLifetimePage.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5-
using System.Runtime.InteropServices.WindowsRuntime;
65
using Windows.Foundation;
76
using Windows.Foundation.Collections;
87
using Microsoft.UI.Xaml;

src/Tests/UnitTest/ApiCompatTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
using Windows.Data.Json;
2222

2323
using Windows.Storage;
24+
using Windows.Storage.Buffers;
2425
using Windows.Storage.Streams;
2526
using Windows.Storage.FileProperties;
2627

2728
using System.Diagnostics;
28-
using System.Runtime.InteropServices.WindowsRuntime;
2929

3030
namespace UnitTest
3131
{

src/Tests/UnitTest/TestComponentCSharp_Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
using System.Runtime.CompilerServices;
1414
using System.Runtime.InteropServices;
1515
using System.Runtime.InteropServices.Marshalling;
16-
using System.Runtime.InteropServices.WindowsRuntime;
1716
using System.Text.RegularExpressions;
1817
using System.Threading;
1918
using System.Threading.Tasks;
@@ -36,6 +35,7 @@
3635
using Windows.Security.Cryptography;
3736
using Windows.Security.Cryptography.Core;
3837
using Windows.Storage;
38+
using Windows.Storage.Buffers;
3939
using Windows.Storage.Streams;
4040
using Windows.UI;
4141
using Windows.UI.Notifications;
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.ComponentModel;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
using WindowsRuntime;
9+
using WindowsRuntime.InteropServices;
10+
using WindowsRuntime.InteropServices.Marshalling;
11+
using static System.Runtime.InteropServices.ComWrappers;
12+
13+
#pragma warning disable CS0723, IDE0008, IDE0046, IDE1006
14+
15+
[assembly: TypeMapAssociation<WindowsRuntimeComWrappersTypeMapGroup>(
16+
source: typeof(WindowsRuntimeExternalArrayBuffer),
17+
proxy: typeof(ABI.WindowsRuntime.InteropServices.WindowsRuntimeExternalArrayBuffer))]
18+
19+
namespace ABI.WindowsRuntime.InteropServices;
20+
21+
/// <summary>
22+
/// ABI type for <see cref="WindowsRuntimeExternalArrayBuffer"/>.
23+
/// </summary>
24+
[WindowsRuntimeClassName("Windows.Storage.Streams.IBuffer")]
25+
[WindowsRuntimeExternalArrayBufferComWrappersMarshaller]
26+
file static class WindowsRuntimeExternalArrayBuffer;
27+
28+
/// <summary>
29+
/// The set of <see cref="ComInterfaceEntry"/> values for <see cref="WindowsRuntimeExternalArrayBuffer"/>.
30+
/// </summary>
31+
file struct WindowsRuntimeExternalArrayBufferInterfaceEntries
32+
{
33+
public ComInterfaceEntry IBuffer;
34+
public ComInterfaceEntry IBufferByteAccess;
35+
public ComInterfaceEntry IStringable;
36+
public ComInterfaceEntry IWeakReferenceSource;
37+
public ComInterfaceEntry IMarshal;
38+
public ComInterfaceEntry IAgileObject;
39+
public ComInterfaceEntry IInspectable;
40+
public ComInterfaceEntry IUnknown;
41+
}
42+
43+
/// <summary>
44+
/// The implementation of <see cref="WindowsRuntimeExternalArrayBufferInterfaceEntries"/>.
45+
/// </summary>
46+
file static class WindowsRuntimeExternalArrayBufferInterfaceEntriesImpl
47+
{
48+
/// <summary>
49+
/// The <see cref="WindowsRuntimeExternalArrayBufferInterfaceEntries"/> value for <see cref="WindowsRuntimeExternalArrayBuffer"/>.
50+
/// </summary>
51+
[FixedAddressValueType]
52+
public static readonly WindowsRuntimeExternalArrayBufferInterfaceEntries Entries;
53+
54+
/// <summary>
55+
/// Initializes <see cref="Entries"/>.
56+
/// </summary>
57+
static WindowsRuntimeExternalArrayBufferInterfaceEntriesImpl()
58+
{
59+
Entries.IBuffer.IID = WellKnownWindowsInterfaceIIDs.IID_IBuffer;
60+
Entries.IBuffer.Vtable = Windows.Storage.Streams.IBufferImpl.Vtable;
61+
Entries.IBufferByteAccess.IID = WellKnownWindowsInterfaceIIDs.IID_IBufferByteAccess;
62+
Entries.IBufferByteAccess.Vtable = WindowsRuntimeExternalArrayBufferByteAccessImpl.Vtable;
63+
Entries.IStringable.IID = WellKnownWindowsInterfaceIIDs.IID_IStringable;
64+
Entries.IStringable.Vtable = IStringableImpl.Vtable;
65+
Entries.IWeakReferenceSource.IID = WellKnownWindowsInterfaceIIDs.IID_IWeakReferenceSource;
66+
Entries.IWeakReferenceSource.Vtable = IWeakReferenceSourceImpl.Vtable;
67+
Entries.IMarshal.IID = WellKnownWindowsInterfaceIIDs.IID_IMarshal;
68+
Entries.IMarshal.Vtable = IMarshalImpl.RoBufferVtable;
69+
Entries.IAgileObject.IID = WellKnownWindowsInterfaceIIDs.IID_IAgileObject;
70+
Entries.IAgileObject.Vtable = IAgileObjectImpl.Vtable;
71+
Entries.IInspectable.IID = WellKnownWindowsInterfaceIIDs.IID_IInspectable;
72+
Entries.IInspectable.Vtable = IInspectableImpl.Vtable;
73+
Entries.IUnknown.IID = WellKnownWindowsInterfaceIIDs.IID_IUnknown;
74+
Entries.IUnknown.Vtable = IUnknownImpl.Vtable;
75+
}
76+
}
77+
78+
/// <summary>
79+
/// A custom <see cref="WindowsRuntimeComWrappersMarshallerAttribute"/> implementation for <see cref="WindowsRuntimeExternalArrayBuffer"/>.
80+
/// </summary>
81+
[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage,
82+
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
83+
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
84+
[EditorBrowsable(EditorBrowsableState.Never)]
85+
public sealed unsafe class WindowsRuntimeExternalArrayBufferComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute
86+
{
87+
/// <inheritdoc/>
88+
public override void* GetOrCreateComInterfaceForObject(object value)
89+
{
90+
// No reference tracking is needed, see notes in the marshaller attribute for 'WindowsRuntimePinnedArrayBuffer'
91+
return (void*)WindowsRuntimeComWrappers.Default.GetOrCreateComInterfaceForObject(value, CreateComInterfaceFlags.None);
92+
}
93+
94+
/// <inheritdoc/>
95+
public override ComInterfaceEntry* ComputeVtables(out int count)
96+
{
97+
count = sizeof(WindowsRuntimeExternalArrayBufferInterfaceEntries) / sizeof(ComInterfaceEntry);
98+
99+
return (ComInterfaceEntry*)Unsafe.AsPointer(in WindowsRuntimeExternalArrayBufferInterfaceEntriesImpl.Entries);
100+
}
101+
}
102+
103+
/// <summary>
104+
/// The native implementation of <c>IBufferByteAccess</c> for <see cref="WindowsRuntimeExternalArrayBuffer"/>.
105+
/// </summary>
106+
file static unsafe class WindowsRuntimeExternalArrayBufferByteAccessImpl
107+
{
108+
/// <summary>
109+
/// The <see cref="IBufferByteAccessVftbl"/> value for the <see cref="WindowsRuntimeExternalArrayBuffer"/> implementation.
110+
/// </summary>
111+
[FixedAddressValueType]
112+
private static readonly IBufferByteAccessVftbl Vftbl;
113+
114+
/// <summary>
115+
/// Initializes <see cref="Vftbl"/>.
116+
/// </summary>
117+
static WindowsRuntimeExternalArrayBufferByteAccessImpl()
118+
{
119+
*(IInspectableVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IInspectableVftbl*)IInspectableImpl.Vtable;
120+
121+
Vftbl.Buffer = &Buffer;
122+
}
123+
124+
/// <summary>
125+
/// Gets a pointer to the <see cref="WindowsRuntimeExternalArrayBuffer"/> implementation.
126+
/// </summary>
127+
public static nint Vtable
128+
{
129+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
130+
get => (nint)Unsafe.AsPointer(in Vftbl);
131+
}
132+
133+
/// <see href="https://learn.microsoft.com/windows/win32/api/robuffer/nf-robuffer-ibufferbyteaccess-buffer"/>
134+
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])]
135+
private static HRESULT Buffer(void* thisPtr, byte** value)
136+
{
137+
if (value is null)
138+
{
139+
return WellKnownErrorCodes.E_POINTER;
140+
}
141+
142+
try
143+
{
144+
var thisObject = ComInterfaceDispatch.GetInstance<global::WindowsRuntime.InteropServices.WindowsRuntimeExternalArrayBuffer>((ComInterfaceDispatch*)thisPtr);
145+
146+
*value = thisObject.Buffer();
147+
148+
return WellKnownErrorCodes.S_OK;
149+
}
150+
catch (Exception ex)
151+
{
152+
return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex);
153+
}
154+
}
155+
}

src/WinRT.Runtime2/ABI/WindowsRuntime.InteropServices/Buffers/WindowsRuntimePinnedArrayBuffer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private static HRESULT Buffer(void* thisPtr, byte** value)
145145
{
146146
var thisObject = ComInterfaceDispatch.GetInstance<global::WindowsRuntime.InteropServices.WindowsRuntimePinnedArrayBuffer>((ComInterfaceDispatch*)thisPtr);
147147

148-
*value = thisObject.Buffer;
148+
*value = thisObject.Buffer();
149149

150150
return WellKnownErrorCodes.S_OK;
151151
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.IO;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Windows.Storage.Streams;
9+
10+
namespace WindowsRuntime.InteropServices;
11+
12+
/// <summary>
13+
/// A <see cref="MemoryStream"/> implementation backed by a managed <see cref="IBuffer"/> instance.
14+
/// </summary>
15+
internal sealed class WindowsRuntimeBufferMemoryStream : MemoryStream
16+
{
17+
/// <summary>
18+
/// The <see cref="IBuffer"/> instance to back the stream.
19+
/// </summary>
20+
private readonly IBuffer _buffer;
21+
22+
/// <summary>
23+
/// Creates a new <see cref="WindowsRuntimeBufferMemoryStream"/> instance with the specified parameters.
24+
/// </summary>
25+
/// <param name="buffer">The <see cref="IBuffer"/> instance to back the stream.</param>
26+
/// <param name="array">The byte array to use as the underlying storage.</param>
27+
/// <param name="offset">The offset in the byte array where the stream starts.</param>
28+
/// <remarks>This constructor doesn't validate any of its parameters.</remarks>
29+
public WindowsRuntimeBufferMemoryStream(IBuffer buffer, byte[] array, int offset)
30+
: base(array, offset, (int)buffer.Capacity, writable: true)
31+
{
32+
_buffer = buffer;
33+
34+
SetLength(buffer.Length);
35+
}
36+
37+
/// <inheritdoc/>
38+
public override void SetLength(long value)
39+
{
40+
base.SetLength(value);
41+
42+
// The input value is limited by 'Capacity', so this cast is safe
43+
_buffer.Length = (uint)Length;
44+
}
45+
46+
/// <inheritdoc/>
47+
public override void Write(byte[] buffer, int offset, int count)
48+
{
49+
base.Write(buffer, offset, count);
50+
51+
_buffer.Length = (uint)Length;
52+
}
53+
54+
/// <inheritdoc/>
55+
public override void Write(ReadOnlySpan<byte> buffer)
56+
{
57+
base.Write(buffer);
58+
59+
_buffer.Length = (uint)Length;
60+
}
61+
62+
/// <inheritdoc/>
63+
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
64+
{
65+
await base.WriteAsync(buffer, offset, count, cancellationToken);
66+
67+
_buffer.Length = (uint)Length;
68+
}
69+
70+
/// <inheritdoc/>
71+
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
72+
{
73+
await base.WriteAsync(buffer, cancellationToken);
74+
75+
_buffer.Length = (uint)Length;
76+
}
77+
78+
/// <inheritdoc/>
79+
public override void WriteByte(byte value)
80+
{
81+
base.WriteByte(value);
82+
83+
_buffer.Length = (uint)Length;
84+
}
85+
}

0 commit comments

Comments
 (0)