-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Convert non-seekable PackagePart streams to seekable #1311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c57b57f
a328b3b
fa5f4ae
2a4b99d
1f4ef98
4ed3f7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using MS.Internal.WindowsBase; | ||
using System.IO; | ||
using System.IO.Packaging; | ||
|
||
namespace MS.Internal.IO.Packaging | ||
{ | ||
/// <summary> | ||
/// Extensions to provide wrappers for functionality that no longer exists in System.IO.Packaging.PackagePart | ||
/// </summary> | ||
[FriendAccessAllowed] | ||
rladuca marked this conversation as resolved.
Show resolved
Hide resolved
|
||
internal static class PackagePartExtensions | ||
{ | ||
/// <summary> | ||
/// Gets a seekable stream from the PackagePart. | ||
/// <see cref="GetSeekableStream(PackagePart, FileMode, FileAccess)"/> for details. | ||
/// </summary> | ||
/// <param name="packPart"></param> | ||
/// <returns>A seekable stream representing the data in the PackagePart.</returns> | ||
internal static Stream GetSeekableStream(this PackagePart packPart) | ||
{ | ||
return GetSeekableStream(packPart, FileMode.OpenOrCreate, packPart.Package.FileOpenAccess); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a seekable stream from the PackagePart. | ||
/// <see cref="GetSeekableStream(PackagePart, FileMode, FileAccess)"/> for details. | ||
/// </summary> | ||
/// <param name="packPart"></param> | ||
/// <param name="mode">The FileMode to open the PackagePart</param> | ||
/// <returns>A seekable stream representing the data in the PackagePart.</returns> | ||
internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode) | ||
{ | ||
return GetSeekableStream(packPart, mode, packPart.Package.FileOpenAccess); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a seekable stream from the PackagePart. | ||
/// </summary> | ||
/// <remarks> | ||
/// In .NET Core 3.0, System.IO.Packaging was removed, in part, from WPF. WPF now uses the implementation | ||
/// contained in System.IO.Packaging.dll. This implementation has distinct differences from the .NET Framework | ||
/// WPF implementation. One such difference is that the <see cref="DeflateStream"/> returned by <see cref="PackagePart.GetStream"/> calls | ||
/// when the <see cref="PackagePart"/> is opened read-only is not a seekable stream. This breaks several assumptions in WPF | ||
/// and causes crashes when various parts of the code-base call into <see cref="Stream.Seek"/> or <see cref="Stream.Position"/>. | ||
/// | ||
/// To fix this, we read the entire <see cref="DeflateStream"/> into a <see cref="MemoryStream"/>, allowing callers to fully seek the stream. | ||
/// This is, generally, what would be the case in .NET Framework. | ||
/// | ||
/// Note that if the stream returned is seekable (the <see cref="PackagePart"/> was opened write or read-write) then we just pass the resulting | ||
/// stream back as we're already guaranteed it meets our needs. | ||
/// </remarks> | ||
/// <param name="packPart"></param> | ||
/// <param name="mode">The FileMode to open the PackagePart</param> | ||
/// <param name="access">The FileAccess used to open the PackagePart</param> | ||
/// <returns>A seekable stream representing the data in the PackagePart.</returns> | ||
internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode, FileAccess access) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it even make sense to specify FileMode/FileAccess, shouldn't it always be open/read? When wrapping a writable stream into a |
||
{ | ||
var packStream = packPart.GetStream(mode, access); | ||
|
||
// If the stream returned is seekable it meets all requirements and can be used directly. | ||
if (packStream.CanSeek) | ||
{ | ||
return packStream; | ||
} | ||
|
||
// Non-seekable streams need to be copied out into memory so they are seekable. | ||
using (packStream) | ||
{ | ||
var seekableStream = new MemoryStream((int)packStream.Length); | ||
|
||
packStream.CopyTo(seekableStream); | ||
|
||
// Reset the stream to the beginning. If this is not done, attempts to read the stream | ||
// from the current position will fail. E.G. XAML/XML parsing. | ||
seekableStream.Position = 0; | ||
vatsan-madhavan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return seekableStream; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,7 +131,7 @@ internal void SetCertificate(X509Certificate2 certificate) | |
Byte[] byteArray = _certificate.GetRawCertData(); | ||
|
||
// FileMode.Create will ensure that the stream will shrink if overwritten | ||
using (Stream s = _part.GetStream(FileMode.Create, FileAccess.Write)) | ||
using (Stream s = _part.GetSeekableStream(FileMode.Create, FileAccess.Write)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who writes back the writable stream? |
||
{ | ||
s.Write(byteArray, 0, byteArray.Length); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,7 +157,7 @@ internal bool Verify(X509Certificate2 signer) | |
result = false; // content type mismatch | ||
break; | ||
} | ||
s = part.GetStream(FileMode.Open, FileAccess.Read); | ||
s = part.GetSeekableStream(FileMode.Open, FileAccess.Read); | ||
} | ||
|
||
using (s) | ||
|
@@ -609,7 +609,7 @@ private SignedXml EnsureXmlSignatureParsed() | |
// Load the XML | ||
XmlDocument xmlDocument = new XmlDocument(); | ||
xmlDocument.PreserveWhitespace = true; | ||
using (Stream s = SignaturePart.GetStream()) | ||
using (Stream s = SignaturePart.GetSeekableStream()) | ||
{ | ||
using (XmlTextReader xmlReader = new XmlTextReader(s)) | ||
{ | ||
|
@@ -937,7 +937,7 @@ private void AddCustomObjectTags(IEnumerable<System.Security.Cryptography.Xml.Da | |
private void UpdatePartFromSignature(Signature sig) | ||
{ | ||
// write to stream | ||
using (Stream s = SignaturePart.GetStream(FileMode.Create, FileAccess.Write)) | ||
using (Stream s = SignaturePart.GetSeekableStream(FileMode.Create, FileAccess.Write)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who writes back the writable stream? |
||
{ | ||
using (XmlTextWriter xWriter = new XmlTextWriter(s, System.Text.Encoding.UTF8)) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move the other
PackagePart
extension method in WindowsBase\MS\Internal\IO\Packaging\PackagingExtensions.cs to this file?