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..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 @@ -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 = 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 @@ -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 = 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 2f68588d677..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 = currentPart.GetStream(); + _currentStream = currentPart.GetSeekableStream(); ManagedIStream stream = new ManagedIStream(_currentStream); try { @@ -415,7 +415,7 @@ IndexingFilterMarshaler corePropertiesFilterMarshaler { if (_currentStream == null) { - _currentStream = currentPart.GetStream(); + _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 7964f5d4455..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 = part.GetStream(); + 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 = part.GetStream(); + 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(part.GetStream(), 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(part.GetStream(), part.ContentType); + return (part == null) ? null : new StreamResourceInfo(part.GetSeekableStream(), part.ContentType); } /// @@ -714,7 +714,7 @@ public static StreamResourceInfo GetRemoteStream(Uri uriRemote) { try { - stream = sooPart.GetStream(); + 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 40a69e8a890..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 @@ -221,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 = xamlEntryPart.GetSeekableStream(); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -261,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 = xamlEntryPart.GetSeekableStream(); using (xamlPartStream) { StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream); @@ -334,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 @@ -344,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(xamlEntryPart.GetSeekableStream(), parserContext, useRestrictiveXamlReader); // Remove the temporary uri from the PackageStore PackageStore.RemovePackage(packageUri); @@ -484,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 = imagePart.GetSeekableStream(); using (imageStream) { bitmapEncoder.Save(imageStream); @@ -692,7 +693,7 @@ internal Stream CreateXamlStream() PackagePart part = this.CreateWpfEntryPart(); // Return a stream opened for writing an image data - return part.GetStream(); + return part.GetSeekableStream(); } internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString) @@ -714,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 imagePart.GetSeekableStream(); } internal Stream GetImageStream(string imageSourceString) @@ -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 imagePart.GetSeekableStream(); } // ------------------------------------------------------------- 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/PackagePartExtensions.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs new file mode 100644 index 00000000000..fa371aa3589 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/IO/Packaging/PackagePartExtensions.cs @@ -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 +{ + /// + /// 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); + + // 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; + + 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 d030be45606..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 @@ -626,7 +626,7 @@ protected override void Dispose(bool disposing) //------------------------------------------------------ private string _path; private ReliableIsolatedStorageFileFolder _folder; - private bool _disposed; + private bool _disposed; } @@ -820,9 +820,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 +835,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"; 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..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 = _part.GetStream(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/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 = 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 @@ +