Skip to content

Commit cdc3579

Browse files
authored
Ensure ComManagedStream seekable wrapper stream has position of 0 (#12953)
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. Fixes #12951
1 parent c246b22 commit cdc3579

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)