Skip to content

Commit 34122aa

Browse files
Ensure ComManagedStream seekable wrapper stream has position of 0 (… (#12970)
#12953) PORTS #12953 - DO NOT SQUASH Until .NET 5, `ComManagedStream` (then `GPStream`) would wrap a non-seekable stream in a `MemoryStream` using the `MemoryStream(Byte[])` constructor. This results in a stream with a position of 0. dotnet/runtime commit 136527537e6 (Improve perfromance for loading from Stream on Windows (dotnet/corefx#31142), 2018-07-20) updated this logic to instead use `CopyTo` to populate the wrapping `MemoryStream`. This results in a stream with a non-zero position. It seems that this non-zero position causes some issues in downstream code when using `Image.FromStream` to load `.emf` and `.wmf` files, resulting in `LoadGdipImageFromStream` returning a status of 2 and thus an exception. Seek the wrapping `MemoryStream` back to the beginning after copying the source stream into it to prevent this exception. ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/dotnet/winforms/pull/12970)
2 parents 02a98c7 + 50d4a08 commit 34122aa

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

src/System.Private.Windows.Core/src/Windows/Win32/System/Com/ComManagedStream.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal ComManagedStream(Stream stream, bool makeSeekable = false)
1919
// Copy to a memory stream so we can seek
2020
MemoryStream memoryStream = new();
2121
stream.CopyTo(memoryStream);
22+
memoryStream.Seek(0, SeekOrigin.Begin);
2223
_dataStream = memoryStream;
2324
}
2425
else
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Windows.Win32.System.Com.Tests;
5+
6+
public class ComManagedStreamTests
7+
{
8+
[Fact]
9+
public void Ctor_NonSeekableStream_WrapsWithSeekableStreamAtPositionZero()
10+
{
11+
using TestStream nonSeekableStream = new(canSeek: false, numBytes: 4);
12+
ComManagedStream comManagedStream = new(nonSeekableStream, makeSeekable: true);
13+
comManagedStream.GetDataStream().CanSeek.Should().Be(true);
14+
comManagedStream.GetDataStream().Position.Should().Be(0);
15+
}
16+
17+
[Fact]
18+
public void Ctor_SeekableStream_UsesOriginalStream()
19+
{
20+
using TestStream seekableStream = new(canSeek: true, numBytes: 4);
21+
ComManagedStream comManagedStream = new(seekableStream, makeSeekable: true);
22+
comManagedStream.GetDataStream().Should().BeSameAs(seekableStream);
23+
}
24+
25+
private class TestStream : MemoryStream
26+
{
27+
private readonly bool _canSeek;
28+
29+
public override bool CanSeek => _canSeek;
30+
31+
public TestStream(bool canSeek, int numBytes) : base(new byte[numBytes])
32+
{
33+
_canSeek = canSeek;
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)