diff --git a/eng/restore/harvestPackages.targets b/eng/restore/harvestPackages.targets index 2e9a8155be678..576ab1f6403d9 100644 --- a/eng/restore/harvestPackages.targets +++ b/eng/restore/harvestPackages.targets @@ -23,6 +23,7 @@ + <_OverridenPackageDownloads Include="@(_PackageDownload)" Condition="'@(PackageDownload)' == '@(_PackageDownload)' and %(Identity) != ''" /> <_PackageDownload Remove="@(_OverridenPackageDownloads)" /> <_PackageDownload Include="@(PackageDownload)" /> diff --git a/src/libraries/System.Drawing.Common/Directory.Build.props b/src/libraries/System.Drawing.Common/Directory.Build.props index 4784967b7f86a..4cd5dd6fad76e 100644 --- a/src/libraries/System.Drawing.Common/Directory.Build.props +++ b/src/libraries/System.Drawing.Common/Directory.Build.props @@ -4,5 +4,8 @@ Open true browser + 5.0.1 + 5.0.0.1 + 4.5.2 \ No newline at end of file diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs index 5527eba2bc88d..43421be35c6ee 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs @@ -49,9 +49,18 @@ public sealed partial class Graphics : MarshalByRefObject, IDisposable, IDeviceC private bool disposed; private static float defDpiX; private static float defDpiY; + private Metafile.MetafileHolder? _metafileHolder; internal Graphics(IntPtr nativeGraphics) => NativeGraphics = nativeGraphics; + internal Graphics(IntPtr nativeGraphics, Image image) : this(nativeGraphics) + { + if (image is Metafile mf) + { + _metafileHolder = mf.AddMetafileHolder(); + } + } + ~Graphics() { Dispose(); @@ -225,6 +234,14 @@ public void Dispose() status = Gdip.GdipDeleteGraphics(new HandleRef(this, NativeGraphics)); NativeGraphics = IntPtr.Zero; Gdip.CheckStatus(status); + + if (_metafileHolder != null) + { + var mh = _metafileHolder; + _metafileHolder = null; + mh.GraphicsDisposed(); + } + disposed = true; } @@ -487,7 +504,7 @@ public static Graphics FromImage(Image image) int status = Gdip.GdipGetImageGraphicsContext(image.nativeImage, out graphics); Gdip.CheckStatus(status); - Graphics result = new Graphics(graphics); + Graphics result = new Graphics(graphics, image); Rectangle rect = new Rectangle(0, 0, image.Width, image.Height); Gdip.GdipSetVisibleClip_linux(result.NativeGraphics, ref rect); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs index 18119d940d923..558e40bdffc95 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs @@ -34,6 +34,7 @@ using System.IO; using System.Reflection; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.InteropServices; using Gdip = System.Drawing.SafeNativeMethods.Gdip; using System.Runtime.Serialization; @@ -42,6 +43,93 @@ namespace System.Drawing.Imaging { public sealed partial class Metafile : Image { + // Non-null if a graphics instance was created using + // Graphics.FromImage(this) The metadata holder is responsible for + // freeing the nativeImage if the Metadata instance is disposed before + // the Graphics instance. + private MetafileHolder? _metafileHolder; + + // A class responsible for disposing of the native Metafile instance + // if it needs to outlive the managed Metafile instance. + // + // The following are both legal with win32 GDI+: + // Metafile mf = ...; // get a metafile instance + // Graphics g = Graphics.FromImage(mf); // get a graphics instance + // g.Dispose(); mf.Dispose(); // dispose of the graphics instance first + // OR + // mf.Dispose(); g.Dispose(); // dispose of the metafile instance first + // + // ligbgdiplus has a bug where disposing of the metafile instance first will + // trigger a use of freed memory when the graphics instance is disposed, which + // could lead to crashes when the native memory is reused. + // + // The metafile holder is designed to take ownership of the native metafile image + // when the managed Metafile instance is disposed while a Graphics instance is still + // not disposed (ie the second code pattern above) and to keep the native image alive until the graphics + // instance is disposed. + // + // Note that the following throws, so we only ever need to keep track of one Graphics + // instance at a time: + // Metafile mf = ...; // get a metafile instance + // Graphics g = Graphics.FromImage(mf); + // Graphics g2 = Graphics.FromImage(mf); // throws OutOfMemoryException on GDI+ on Win32 + internal sealed class MetafileHolder : IDisposable + { + private bool _disposed; + private IntPtr _nativeImage; + + + internal bool Disposed { get => _disposed; } + internal MetafileHolder() + { + _disposed = false; + _nativeImage = IntPtr.Zero; + } + + ~MetafileHolder() => Dispose(false); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal void Dispose(bool disposing) + { + if (!_disposed) + { + IntPtr nativeImage = _nativeImage; + _nativeImage = IntPtr.Zero; + _disposed = true; + if (nativeImage != IntPtr.Zero) + { + int status = Gdip.GdipDisposeImage(nativeImage); + Gdip.CheckStatus(status); + } + } + } + + internal void MetafileDisposed(IntPtr nativeImage) + { + _nativeImage = nativeImage; + } + + internal void GraphicsDisposed() + { + Dispose(); + } + } + + internal MetafileHolder? AddMetafileHolder() + { + // If _metafileHolder is not null and hasn't been disposed yet, there's already a graphics instance associated with + // this metafile, the native code will return an error status. + if (_metafileHolder != null && !_metafileHolder.Disposed) + return null; + _metafileHolder = new MetafileHolder(); + return _metafileHolder; + } + // Usually called when cloning images that need to have // not only the handle saved, but also the underlying stream // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image) @@ -142,6 +230,21 @@ public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, Metaf Gdip.CheckStatus(status); } + protected override void Dispose(bool disposing) + { + if (_metafileHolder != null && !_metafileHolder.Disposed) + { + // There's a graphics instance created from this Metafile, + // transfer responsibility for disposing the nativeImage to the + // MetafileHolder + _metafileHolder.MetafileDisposed(nativeImage); + _metafileHolder = null; + nativeImage = IntPtr.Zero; + } + + base.Dispose(disposing); + } + // methods public IntPtr GetHenhmetafile() diff --git a/src/libraries/libraries-packages.proj b/src/libraries/libraries-packages.proj index d51cc5ca657d1..bd8f663aa941c 100644 --- a/src/libraries/libraries-packages.proj +++ b/src/libraries/libraries-packages.proj @@ -22,6 +22,7 @@ + diff --git a/src/libraries/pkg/baseline/packageIndex.json b/src/libraries/pkg/baseline/packageIndex.json index b481ca1e21496..2a0efcad4035a 100644 --- a/src/libraries/pkg/baseline/packageIndex.json +++ b/src/libraries/pkg/baseline/packageIndex.json @@ -2862,6 +2862,7 @@ "StableVersions": [ "4.5.0", "4.5.1", + "4.5.2", "4.6.0", "4.6.1", "4.6.2", @@ -2883,7 +2884,8 @@ "4.0.0.1": "4.5.1", "4.0.1.0": "4.6.0", "4.0.1.1": "4.6.1", - "5.0.0.0": "5.0.0" + "5.0.0.0": "5.0.0", + "5.0.0.1": "5.0.1" } }, "System.Drawing.Design": {