From c57b57fdc6d7eb8243c2c4dede84e7602921b3cb Mon Sep 17 00:00:00 2001 From: Robert LaDuca Date: Wed, 17 Jul 2019 12:09:33 -0700 Subject: [PATCH 1/6] Initial creation of utility wrapper for PackagePart streams and modifying previous seekable stream fix to use new functions. --- .../System/Windows/Documents/WpfPayload.cs | 3 +- .../Windows/Documents/XamlToRtfWriter.cs | 17 ++++------ .../IO/Packaging/PackagingUtilities.cs | 34 ++++++++++++++++--- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs index 40a69e8a890..ce73f414087 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs @@ -38,6 +38,7 @@ namespace System.Windows.Documents using MS.Internal.PresentationFramework; // SecurityHelper using InternalPackUriHelper = MS.Internal.IO.Packaging.PackUriHelper; + using PackagingUtilities = MS.Internal.IO.Packaging.PackagingUtilities; // An object supporting flow content packaging with images and other resources. /// /// WpfPayload is a class providing services for creating, @@ -723,7 +724,7 @@ internal Stream GetImageStream(string imageSourceString) imageSourceString = imageSourceString.Substring(1); // cut the leading dot out Uri imagePartUri = new Uri(XamlPayloadDirectory + imageSourceString, UriKind.Relative); PackagePart imagePart = _package.GetPart(imagePartUri); - return imagePart.GetStream(); + return PackagingUtilities.GetSeekablePackagePartStream(imagePart); } // ------------------------------------------------------------- diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/XamlToRtfWriter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/XamlToRtfWriter.cs index 615130cb0e4..f1238c48fa2 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/XamlToRtfWriter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/XamlToRtfWriter.cs @@ -2019,18 +2019,13 @@ private void WriteImage(DocumentNode documentNode) // Read the image binary data from WpfPayLoad that contains Xaml and Images. // Xaml content have the image source like as "./Image1.png" so that we can // query the image from the container of WpfPayLoad with the image source name. - Stream imageStream = _wpfPayload.GetImageStream(documentNode.FormatState.ImageSource); - - // Get image type to be added to rtf content - RtfImageFormat imageFormat = GetImageFormatFromImageSourceName(documentNode.FormatState.ImageSource); - - // Write the shape image like as "\pngblip" or "\jpegblip" rtf control. We wrap the stream that comes - // from the package because we require the stream to be seekable. - Debug.Assert(!imageStream.CanSeek); - using (var seekableStream = new MemoryStream((int)imageStream.Length)) + using (Stream imageStream = _wpfPayload.GetImageStream(documentNode.FormatState.ImageSource)) { - imageStream.CopyTo(seekableStream); - WriteShapeImage(documentNode, seekableStream, imageFormat); + // Get image type to be added to rtf content + RtfImageFormat imageFormat = GetImageFormatFromImageSourceName(documentNode.FormatState.ImageSource); + + // Write the shape image like as "\pngblip" or "\jpegblip" rtf control. + WriteShapeImage(documentNode, imageStream, imageFormat); } #if WindowsMetaFile diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs index d030be45606..6d34ffa6d30 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs @@ -13,6 +13,7 @@ using System; using System.IO; using System.IO.IsolatedStorage; +using System.IO.Packaging; using MS.Internal.WindowsBase; // FriendAccessAllowed using System.Xml; // For XmlReader using System.Diagnostics; // For Debug.Assert @@ -474,6 +475,29 @@ internal static Object IsolatedStorageFileLock } } + internal static Stream GetSeekablePackagePartStream(PackagePart packPart) + { + return GetSeekablePackagePartStream(packPart, FileMode.OpenOrCreate, packPart.Package.FileOpenAccess); + } + + internal static Stream GetSeekablePackagePartStream(PackagePart packPart, FileMode mode) + { + return GetSeekablePackagePartStream(packPart, mode, packPart.Package.FileOpenAccess); + } + + internal static Stream GetSeekablePackagePartStream(PackagePart packPart, FileMode mode, FileAccess access) + { + var packStream = packPart.GetStream(mode, access); + + Debug.Assert(!packStream.CanSeek); + + var seekableStream = new MemoryStream((int)packStream.Length); + + packStream.CopyTo(seekableStream); + + return seekableStream; + } + #endregion Internal Methods //------------------------------------------------------ @@ -626,7 +650,7 @@ protected override void Dispose(bool disposing) //------------------------------------------------------ private string _path; private ReliableIsolatedStorageFileFolder _folder; - private bool _disposed; + private bool _disposed; } @@ -820,9 +844,9 @@ void CheckDisposed() // //------------------------------------------------------ private static IsolatedStorageFile _file; - private static bool _userHasProfile; - private int _refCount; // number of outstanding "streams" - private bool _disposed; + private static bool _userHasProfile; + private int _refCount; // number of outstanding "streams" + private bool _disposed; } //------------------------------------------------------ @@ -835,7 +859,7 @@ void CheckDisposed() /// /// See PS 1468964 for details. private static Object _isoStoreSyncObject = new Object(); - private static Object _isolatedStorageFileLock = new Object(); + private static Object _isolatedStorageFileLock = new Object(); private static ReliableIsolatedStorageFileFolder _defaultFile; private const string XmlNamespace = "xmlns"; private const string _encodingAttribute = "encoding"; From a328b3b098c82de3700110679d7f9de53855e861 Mon Sep 17 00:00:00 2001 From: Robert LaDuca Date: Wed, 17 Jul 2019 15:04:36 -0700 Subject: [PATCH 2/6] Fixing wrapping stream functions to allow passthrough of seekable streams and to ensure we close non-seekable streams once we've fully read them into memory. --- .../MS/Internal/IO/Packaging/PackagingUtilities.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs index 6d34ffa6d30..51dd42b9dd2 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs @@ -489,11 +489,19 @@ internal static Stream GetSeekablePackagePartStream(PackagePart packPart, FileMo { var packStream = packPart.GetStream(mode, access); - Debug.Assert(!packStream.CanSeek); + // If the stream returned is seekable it meets all requirements and can be used directly. + if(packStream.CanSeek) + { + return packStream; + } - var seekableStream = new MemoryStream((int)packStream.Length); + // 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); + packStream.CopyTo(seekableStream); + } return seekableStream; } From fa5f4aea5043ee5c32205a6debdf86c5186224c6 Mon Sep 17 00:00:00 2001 From: Robert LaDuca Date: Wed, 17 Jul 2019 16:19:41 -0700 Subject: [PATCH 3/6] Ensuring uses of PackagePart.GetStream go through our utility wrapper that will create a seekable stream when appropriate. --- .../System/IO/Packaging/PackWebResponse.cs | 5 +++-- .../MS/Internal/IO/Packaging/PackageFilter.cs | 4 ++-- .../System/Windows/Application.cs | 10 +++++----- .../System/Windows/Documents/WpfPayload.cs | 12 ++++++------ .../MS/Internal/IO/Packaging/Certificate.cs | 2 +- .../IO/Packaging/XmlDigitalSignatureProcessor.cs | 6 +++--- .../MS/Internal/IO/Packaging/XmlSignatureManifest.cs | 2 +- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs index 0ae910e7445..c6e1c72b5c9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs @@ -250,7 +250,8 @@ public override Stream GetResponseStream() throw new WebException(SR.Get(SRID.WebResponsePartNotFound)); PackagePart p = c.GetPart(_partName); - Stream s = p.GetStream(FileMode.Open, FileAccess.Read); + + Stream s = PackagingUtilities.GetSeekablePackagePartStream(p, FileMode.Open, FileAccess.Read); _mimeType = new MS.Internal.ContentType(p.ContentType); // save this for use in ContentType property - may still be null _fullStreamLength = s.Length; // just this stream @@ -648,7 +649,7 @@ internal Stream GetResponseStream() System.Threading.Thread.CurrentThread.ManagedThreadId + ": " + "CachedResponse - Getting part stream "); #endif - Stream s = p.GetStream(FileMode.Open, FileAccess.Read); + Stream s = PackagingUtilities.GetSeekablePackagePartStream(p, FileMode.Open, FileAccess.Read); // Unless package is thread-safe, wrap the returned stream so that // package access is serialized diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs index 2f68588d677..20bdba7dc8a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs @@ -378,7 +378,7 @@ IndexingFilterMarshaler corePropertiesFilterMarshaler _currentFilter = GetFilterFromClsid(new Guid(filterClsid)); if (_currentFilter != null) { - _currentStream = currentPart.GetStream(); + _currentStream = PackagingUtilities.GetSeekablePackagePartStream(currentPart); ManagedIStream stream = new ManagedIStream(_currentStream); try { @@ -415,7 +415,7 @@ IndexingFilterMarshaler corePropertiesFilterMarshaler { if (_currentStream == null) { - _currentStream = currentPart.GetStream(); + _currentStream = PackagingUtilities.GetSeekablePackagePartStream(currentPart); } IndexingFilterMarshaler xamlFilterMarshaler diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs index 7964f5d4455..e1208368102 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs @@ -445,7 +445,7 @@ public static void LoadComponent(Object component, Uri resourceLocator) // if not, this is a first time regular load of the component. PackagePart part = GetResourceOrContentPart(resourceLocator); ContentType contentType = new ContentType(part.ContentType); - stream = part.GetStream(); + stream = PackagingUtilities.GetSeekablePackagePartStream(part); bCloseStream = true; // @@ -509,7 +509,7 @@ internal static object LoadComponent(Uri resourceLocator, bool bSkipJournaledPro PackagePart part = GetResourceOrContentPart(packUri); ContentType contentType = new ContentType(part.ContentType); - Stream stream = part.GetStream(); + Stream stream = PackagingUtilities.GetSeekablePackagePartStream(part); ParserContext pc = new ParserContext(); pc.BaseUri = packUri; @@ -623,7 +623,7 @@ public static StreamResourceInfo GetResourceStream(Uri uriResource) } ResourcePart part = GetResourceOrContentPart(uriResource) as ResourcePart; - return (part == null) ? null : new StreamResourceInfo(part.GetStream(), part.ContentType); + return (part == null) ? null : new StreamResourceInfo(PackagingUtilities.GetSeekablePackagePartStream(part), part.ContentType); } /// @@ -656,7 +656,7 @@ public static StreamResourceInfo GetContentStream(Uri uriContent) } ContentFilePart part = GetResourceOrContentPart(uriContent) as ContentFilePart; - return (part == null) ? null : new StreamResourceInfo(part.GetStream(), part.ContentType); + return (part == null) ? null : new StreamResourceInfo(PackagingUtilities.GetSeekablePackagePartStream(part), part.ContentType); } /// @@ -714,7 +714,7 @@ public static StreamResourceInfo GetRemoteStream(Uri uriRemote) { try { - stream = sooPart.GetStream(); + stream = PackagingUtilities.GetSeekablePackagePartStream(sooPart); if (stream == null) { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs index ce73f414087..453b1864da3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs @@ -222,7 +222,7 @@ internal static string SaveRange(ITextRange range, ref Stream stream, bool useFl PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart(); // Write the part's content - Stream xamlPartStream = xamlEntryPart.GetStream(); + Stream xamlPartStream = PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -262,7 +262,7 @@ internal static MemoryStream SaveImage(BitmapSource bitmapSource, string imageCo PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart(); // Write the part's content - Stream xamlPartStream = xamlEntryPart.GetStream(); + Stream xamlPartStream = PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -345,7 +345,7 @@ internal static object LoadElement(Stream stream) // Call xaml parser bool useRestrictiveXamlReader = !Clipboard.UseLegacyDangerousClipboardDeserializationMode(); - xamlObject = XamlReader.Load(xamlEntryPart.GetStream(), parserContext, useRestrictiveXamlReader); + xamlObject = XamlReader.Load(PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart), parserContext, useRestrictiveXamlReader); // Remove the temporary uri from the PackageStore PackageStore.RemovePackage(packageUri); @@ -485,7 +485,7 @@ private void CreateImagePart(PackagePart sourcePart, BitmapSource imageSource, s bitmapEncoder.Frames.Add(BitmapFrame.Create(imageSource)); // Save encoded image data into the image part in the package - Stream imageStream = imagePart.GetStream(); + Stream imageStream = PackagingUtilities.GetSeekablePackagePartStream(imagePart); using (imageStream) { bitmapEncoder.Save(imageStream); @@ -693,7 +693,7 @@ internal Stream CreateXamlStream() PackagePart part = this.CreateWpfEntryPart(); // Return a stream opened for writing an image data - return part.GetStream(); + return PackagingUtilities.GetSeekablePackagePartStream(part); } internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString) @@ -715,7 +715,7 @@ internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString = GetImageReference(imagePartUriString); // Return a stream opened for writing an image data - return imagePart.GetStream(); + return PackagingUtilities.GetSeekablePackagePartStream(imagePart); } internal Stream GetImageStream(string imageSourceString) diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs index 9cf0929b3a2..e3874f75337 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs @@ -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 = PackagingUtilities.GetSeekablePackagePartStream(_part, FileMode.Create, FileAccess.Write)) { s.Write(byteArray, 0, byteArray.Length); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlDigitalSignatureProcessor.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlDigitalSignatureProcessor.cs index 5706e3438f6..2a7079dd432 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlDigitalSignatureProcessor.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlDigitalSignatureProcessor.cs @@ -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 - using (Stream s = part.GetStream(FileMode.Open, FileAccess.Read)) + using (Stream s = PackagingUtilities.GetSeekablePackagePartStream(part, FileMode.Open, FileAccess.Read)) { reference.AppendChild(GenerateDigestValueNode(xDoc, hashAlgorithm, s, transformName)); } From 2a4b99d37de9f6ebfe96022e42c95e69980fa4d3 Mon Sep 17 00:00:00 2001 From: Rob LaDuca Date: Thu, 18 Jul 2019 11:04:20 -0700 Subject: [PATCH 4/6] Swapping new stream functions to extension methods on PackagePart. --- .../System/IO/Packaging/PackWebResponse.cs | 4 +- .../MS/Internal/IO/Packaging/PackageFilter.cs | 4 +- .../System/Windows/Application.cs | 10 ++--- .../System/Windows/Documents/WpfPayload.cs | 20 ++++----- .../IO/Packaging/PackagePartExtensions.cs | 45 +++++++++++++++++++ .../IO/Packaging/PackagingUtilities.cs | 31 ------------- .../MS/Internal/IO/Packaging/Certificate.cs | 2 +- .../IO/Packaging/XmlSignatureManifest.cs | 2 +- .../src/WindowsBase/WindowsBase.csproj | 1 + 9 files changed, 67 insertions(+), 52 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs index c6e1c72b5c9..9cea47eebaa 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/IO/Packaging/PackWebResponse.cs @@ -251,7 +251,7 @@ public override Stream GetResponseStream() PackagePart p = c.GetPart(_partName); - Stream s = PackagingUtilities.GetSeekablePackagePartStream(p, FileMode.Open, FileAccess.Read); + Stream s = p.GetSeekableStream(FileMode.Open, FileAccess.Read); _mimeType = new MS.Internal.ContentType(p.ContentType); // save this for use in ContentType property - may still be null _fullStreamLength = s.Length; // just this stream @@ -649,7 +649,7 @@ internal Stream GetResponseStream() System.Threading.Thread.CurrentThread.ManagedThreadId + ": " + "CachedResponse - Getting part stream "); #endif - Stream s = PackagingUtilities.GetSeekablePackagePartStream(p, FileMode.Open, FileAccess.Read); + Stream s = p.GetSeekableStream(FileMode.Open, FileAccess.Read); // Unless package is thread-safe, wrap the returned stream so that // package access is serialized diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs index 20bdba7dc8a..3c4888ac43b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/PackageFilter.cs @@ -378,7 +378,7 @@ IndexingFilterMarshaler corePropertiesFilterMarshaler _currentFilter = GetFilterFromClsid(new Guid(filterClsid)); if (_currentFilter != null) { - _currentStream = PackagingUtilities.GetSeekablePackagePartStream(currentPart); + _currentStream = currentPart.GetSeekableStream(); ManagedIStream stream = new ManagedIStream(_currentStream); try { @@ -415,7 +415,7 @@ IndexingFilterMarshaler corePropertiesFilterMarshaler { if (_currentStream == null) { - _currentStream = PackagingUtilities.GetSeekablePackagePartStream(currentPart); + _currentStream = currentPart.GetSeekableStream(); } IndexingFilterMarshaler xamlFilterMarshaler diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs index e1208368102..0593bbdb3c4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs @@ -445,7 +445,7 @@ public static void LoadComponent(Object component, Uri resourceLocator) // if not, this is a first time regular load of the component. PackagePart part = GetResourceOrContentPart(resourceLocator); ContentType contentType = new ContentType(part.ContentType); - stream = PackagingUtilities.GetSeekablePackagePartStream(part); + stream = part.GetSeekableStream(); bCloseStream = true; // @@ -509,7 +509,7 @@ internal static object LoadComponent(Uri resourceLocator, bool bSkipJournaledPro PackagePart part = GetResourceOrContentPart(packUri); ContentType contentType = new ContentType(part.ContentType); - Stream stream = PackagingUtilities.GetSeekablePackagePartStream(part); + Stream stream = part.GetSeekableStream(); ParserContext pc = new ParserContext(); pc.BaseUri = packUri; @@ -623,7 +623,7 @@ public static StreamResourceInfo GetResourceStream(Uri uriResource) } ResourcePart part = GetResourceOrContentPart(uriResource) as ResourcePart; - return (part == null) ? null : new StreamResourceInfo(PackagingUtilities.GetSeekablePackagePartStream(part), part.ContentType); + return (part == null) ? null : new StreamResourceInfo(part.GetSeekableStream(), part.ContentType); } /// @@ -656,7 +656,7 @@ public static StreamResourceInfo GetContentStream(Uri uriContent) } ContentFilePart part = GetResourceOrContentPart(uriContent) as ContentFilePart; - return (part == null) ? null : new StreamResourceInfo(PackagingUtilities.GetSeekablePackagePartStream(part), part.ContentType); + return (part == null) ? null : new StreamResourceInfo(part.GetSeekableStream(), part.ContentType); } /// @@ -714,7 +714,7 @@ public static StreamResourceInfo GetRemoteStream(Uri uriRemote) { try { - stream = PackagingUtilities.GetSeekablePackagePartStream(sooPart); + stream = sooPart.GetSeekableStream(); if (stream == null) { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs index 453b1864da3..7627c4c9fc8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/WpfPayload.cs @@ -21,6 +21,7 @@ namespace System.Windows.Documents { using MS.Internal; // Invariant + using MS.Internal.IO.Packaging; using System; using System.Xml; using System.Windows.Markup; // TypeConvertContext, ParserContext @@ -38,7 +39,6 @@ namespace System.Windows.Documents using MS.Internal.PresentationFramework; // SecurityHelper using InternalPackUriHelper = MS.Internal.IO.Packaging.PackUriHelper; - using PackagingUtilities = MS.Internal.IO.Packaging.PackagingUtilities; // An object supporting flow content packaging with images and other resources. /// /// WpfPayload is a class providing services for creating, @@ -222,7 +222,7 @@ internal static string SaveRange(ITextRange range, ref Stream stream, bool useFl PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart(); // Write the part's content - Stream xamlPartStream = PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart); + Stream xamlPartStream = xamlEntryPart.GetSeekableStream(); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -262,7 +262,7 @@ internal static MemoryStream SaveImage(BitmapSource bitmapSource, string imageCo PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart(); // Write the part's content - Stream xamlPartStream = PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart); + Stream xamlPartStream = xamlEntryPart.GetSeekableStream(); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -335,8 +335,8 @@ internal static object LoadElement(Stream stream) // Uniqueness is required to make sure that cached images are not mixed up. int newWpfPayoutCount = Interlocked.Increment(ref _wpfPayloadCount); Uri payloadUri = new Uri("payload://wpf" + newWpfPayoutCount, UriKind.Absolute); - Uri entryPartUri = PackUriHelper.Create(payloadUri, xamlEntryPart.Uri); // gives an absolute uri of the entry part - Uri packageUri = PackUriHelper.GetPackageUri(entryPartUri); // extracts package uri from combined package+part uri + Uri entryPartUri = System.IO.Packaging.PackUriHelper.Create(payloadUri, xamlEntryPart.Uri); // gives an absolute uri of the entry part + Uri packageUri = System.IO.Packaging.PackUriHelper.GetPackageUri(entryPartUri); // extracts package uri from combined package+part uri PackageStore.AddPackage(packageUri, wpfPayload.Package); // Register the package // Set this temporary uri as a base uri for xaml parser @@ -345,7 +345,7 @@ internal static object LoadElement(Stream stream) // Call xaml parser bool useRestrictiveXamlReader = !Clipboard.UseLegacyDangerousClipboardDeserializationMode(); - xamlObject = XamlReader.Load(PackagingUtilities.GetSeekablePackagePartStream(xamlEntryPart), parserContext, useRestrictiveXamlReader); + xamlObject = XamlReader.Load(xamlEntryPart.GetSeekableStream(), parserContext, useRestrictiveXamlReader); // Remove the temporary uri from the PackageStore PackageStore.RemovePackage(packageUri); @@ -485,7 +485,7 @@ private void CreateImagePart(PackagePart sourcePart, BitmapSource imageSource, s bitmapEncoder.Frames.Add(BitmapFrame.Create(imageSource)); // Save encoded image data into the image part in the package - Stream imageStream = PackagingUtilities.GetSeekablePackagePartStream(imagePart); + Stream imageStream = imagePart.GetSeekableStream(); using (imageStream) { bitmapEncoder.Save(imageStream); @@ -693,7 +693,7 @@ internal Stream CreateXamlStream() PackagePart part = this.CreateWpfEntryPart(); // Return a stream opened for writing an image data - return PackagingUtilities.GetSeekablePackagePartStream(part); + return part.GetSeekableStream(); } internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString) @@ -715,7 +715,7 @@ internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString = GetImageReference(imagePartUriString); // Return a stream opened for writing an image data - return PackagingUtilities.GetSeekablePackagePartStream(imagePart); + return imagePart.GetSeekableStream(); } internal Stream GetImageStream(string imageSourceString) @@ -724,7 +724,7 @@ internal Stream GetImageStream(string imageSourceString) imageSourceString = imageSourceString.Substring(1); // cut the leading dot out Uri imagePartUri = new Uri(XamlPayloadDirectory + imageSourceString, UriKind.Relative); PackagePart imagePart = _package.GetPart(imagePartUri); - return PackagingUtilities.GetSeekablePackagePartStream(imagePart); + return imagePart.GetSeekableStream(); } // ------------------------------------------------------------- diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs new file mode 100644 index 00000000000..7734cae1f07 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs @@ -0,0 +1,45 @@ +// 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 +{ + [FriendAccessAllowed] + internal static class PackagePartExtensions + { + internal static Stream GetSeekableStream(this PackagePart packPart) + { + return GetSeekableStream(packPart, FileMode.OpenOrCreate, packPart.Package.FileOpenAccess); + } + + internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode) + { + return GetSeekableStream(packPart, mode, packPart.Package.FileOpenAccess); + } + + internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode, FileAccess access) + { + 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); + + return seekableStream; + } + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs index 51dd42b9dd2..399e1174f61 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs @@ -475,37 +475,6 @@ internal static Object IsolatedStorageFileLock } } - internal static Stream GetSeekablePackagePartStream(PackagePart packPart) - { - return GetSeekablePackagePartStream(packPart, FileMode.OpenOrCreate, packPart.Package.FileOpenAccess); - } - - internal static Stream GetSeekablePackagePartStream(PackagePart packPart, FileMode mode) - { - return GetSeekablePackagePartStream(packPart, mode, packPart.Package.FileOpenAccess); - } - - internal static Stream GetSeekablePackagePartStream(PackagePart packPart, FileMode mode, FileAccess access) - { - 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); - } - - return seekableStream; - } - #endregion Internal Methods //------------------------------------------------------ diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs index e3874f75337..c417ad3e1b6 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/Certificate.cs @@ -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 = PackagingUtilities.GetSeekablePackagePartStream(_part, FileMode.Create, FileAccess.Write)) + using (Stream s = _part.GetSeekableStream(FileMode.Create, FileAccess.Write)) { s.Write(byteArray, 0, byteArray.Length); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs index 8e661e98cb2..b4af9e3a751 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs @@ -783,7 +783,7 @@ private static XmlNode GeneratePartSigningReference( reference.AppendChild(GenerateDigestMethod(manager, xDoc)); // - using (Stream s = PackagingUtilities.GetSeekablePackagePartStream(part, FileMode.Open, FileAccess.Read)) + using (Stream s = part.GetSeekableStream(FileMode.Open, FileAccess.Read)) { reference.AppendChild(GenerateDigestValueNode(xDoc, hashAlgorithm, s, transformName)); } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index f5cc90b0a55..871caa101d6 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -40,6 +40,7 @@ + From 1f4ef98b280205e62f46f6f6521a0f491e76e56d Mon Sep 17 00:00:00 2001 From: Rob LaDuca Date: Thu, 18 Jul 2019 12:37:41 -0700 Subject: [PATCH 5/6] Adding comments and fixing the stream position after the initial copy. --- .../IO/Packaging/PackagePartExtensions.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs index 7734cae1f07..fa371aa3589 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs @@ -8,19 +8,55 @@ namespace MS.Internal.IO.Packaging { + /// + /// Extensions to provide wrappers for functionality that no longer exists in System.IO.Packaging.PackagePart + /// [FriendAccessAllowed] internal static class PackagePartExtensions { + /// + /// Gets a seekable stream from the PackagePart. + /// for details. + /// + /// + /// A seekable stream representing the data in the PackagePart. internal static Stream GetSeekableStream(this PackagePart packPart) { return GetSeekableStream(packPart, FileMode.OpenOrCreate, packPart.Package.FileOpenAccess); } + /// + /// Gets a seekable stream from the PackagePart. + /// for details. + /// + /// + /// The FileMode to open the PackagePart + /// A seekable stream representing the data in the PackagePart. internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode) { return GetSeekableStream(packPart, mode, packPart.Package.FileOpenAccess); } + /// + /// Gets a seekable stream from the PackagePart. + /// + /// + /// 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 returned by calls + /// when the 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 or . + /// + /// To fix this, we read the entire into a , 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 was opened write or read-write) then we just pass the resulting + /// stream back as we're already guaranteed it meets our needs. + /// + /// + /// The FileMode to open the PackagePart + /// The FileAccess used to open the PackagePart + /// A seekable stream representing the data in the PackagePart. internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mode, FileAccess access) { var packStream = packPart.GetStream(mode, access); @@ -38,6 +74,10 @@ internal static Stream GetSeekableStream(this PackagePart packPart, FileMode mod 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; + return seekableStream; } } From 4ed3f7b442a2cc8f9c68c53acae9154a46e3ba85 Mon Sep 17 00:00:00 2001 From: Rob LaDuca Date: Thu, 18 Jul 2019 15:25:05 -0700 Subject: [PATCH 6/6] Reverting changes to PackagingUtilities --- .../src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs index 399e1174f61..d7e5bad4189 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagingUtilities.cs @@ -13,7 +13,6 @@ using System; using System.IO; using System.IO.IsolatedStorage; -using System.IO.Packaging; using MS.Internal.WindowsBase; // FriendAccessAllowed using System.Xml; // For XmlReader using System.Diagnostics; // For Debug.Assert