diff --git a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json index d72c17b401bd..7bfc9fa2442b 100644 --- a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json +++ b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json @@ -1709,7 +1709,8 @@ }, "AssemblyVersionInPackageVersion": { "4.0.0.0": "4.5.0", - "4.0.0.1": "4.5.1" + "4.0.0.1": "4.5.1", + "4.0.0.2": "4.5.2" } }, "System.Drawing.Design": { diff --git a/src/System.Drawing.Common/dir.props b/src/System.Drawing.Common/dir.props index 73d9ff2c9564..88e68540387f 100644 --- a/src/System.Drawing.Common/dir.props +++ b/src/System.Drawing.Common/dir.props @@ -2,8 +2,8 @@ - 4.0.0.1 + 4.0.0.2 Open - 4.5.1 + 4.5.2 diff --git a/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs b/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs index c7d8c1caf5b3..603977c98060 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs +++ b/src/System.Drawing.Common/src/System/Drawing/Graphics.Unix.cs @@ -51,6 +51,7 @@ public sealed partial class Graphics : MarshalByRefObject, IDisposable private static float defDpiX = 0; private static float defDpiY = 0; private IntPtr deviceContextHdc; + private Metafile.MetafileHolder _metafileHolder; public delegate bool EnumerateMetafileProc(EmfPlusRecordType recordType, int flags, @@ -65,6 +66,14 @@ internal Graphics(IntPtr nativeGraphics) nativeObject = nativeGraphics; } + internal Graphics(IntPtr nativeGraphics, Image image) : this(nativeGraphics) + { + if (image is Metafile mf) + { + _metafileHolder = mf.AddMetafileHolder(); + } + } + ~Graphics() { Dispose(); @@ -287,6 +296,14 @@ public void Dispose() status = SafeNativeMethods.Gdip.GdipDeleteGraphics(nativeObject); nativeObject = IntPtr.Zero; SafeNativeMethods.Gdip.CheckStatus(status); + + if (_metafileHolder != null) + { + var mh = _metafileHolder; + _metafileHolder = null; + mh.GraphicsDisposed(); + } + disposed = true; } @@ -1735,7 +1752,7 @@ public static Graphics FromImage(Image image) int status = SafeNativeMethods.Gdip.GdipGetImageGraphicsContext(image.nativeImage, out graphics); SafeNativeMethods.Gdip.CheckStatus(status); - Graphics result = new Graphics(graphics); + Graphics result = new Graphics(graphics, image); Rectangle rect = new Rectangle(0, 0, image.Width, image.Height); SafeNativeMethods.Gdip.GdipSetVisibleClip_linux(result.NativeObject, ref rect); diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs index d61256ca0a60..0c2520f1d58e 100644 --- a/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.Unix.cs +++ b/src/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; namespace System.Drawing.Imaging @@ -47,6 +48,90 @@ namespace System.Drawing.Imaging public sealed 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 + // + // + // 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 = SafeNativeMethods.Gdip.GdipDisposeImage(nativeImage); + SafeNativeMethods.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; + } + // constructors internal Metafile(IntPtr ptr) => SetNativeImage(ptr); @@ -311,6 +396,21 @@ public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect, Meta SafeNativeMethods.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/packages.builds b/src/packages.builds index aaf7881718f5..5f4d49c67406 100644 --- a/src/packages.builds +++ b/src/packages.builds @@ -24,6 +24,9 @@ $(AdditionalProperties) + + $(AdditionalProperties) +