34
34
using System . IO ;
35
35
using System . Reflection ;
36
36
using System . ComponentModel ;
37
+ using System . Diagnostics ;
37
38
using System . Runtime . InteropServices ;
38
39
using Gdip = System . Drawing . SafeNativeMethods . Gdip ;
39
40
using System . Runtime . Serialization ;
@@ -42,6 +43,93 @@ namespace System.Drawing.Imaging
42
43
{
43
44
public sealed partial class Metafile : Image
44
45
{
46
+ // Non-null if a graphics instance was created using
47
+ // Graphics.FromImage(this) The metadata holder is responsible for
48
+ // freeing the nativeImage if the Metadata instance is disposed before
49
+ // the Graphics instance.
50
+ private MetafileHolder ? _metafileHolder ;
51
+
52
+ // A class responsible for disposing of the native Metafile instance
53
+ // if it needs to outlive the managed Metafile instance.
54
+ //
55
+ // The following are both legal with win32 GDI+:
56
+ // Metafile mf = ...; // get a metafile instance
57
+ // Graphics g = Graphics.FromImage(mf); // get a graphics instance
58
+ // g.Dispose(); mf.Dispose(); // dispose of the graphics instance first
59
+ // OR
60
+ // mf.Dispose(); g.Dispose(); // dispose of the metafile instance first
61
+ //
62
+ // ligbgdiplus has a bug where disposing of the metafile instance first will
63
+ // trigger a use of freed memory when the graphics instance is disposed, which
64
+ // could lead to crashes when the native memory is reused.
65
+ //
66
+ // The metafile holder is designed to take ownership of the native metafile image
67
+ // when the managed Metafile instance is disposed while a Graphics instance is still
68
+ // not disposed (ie the second code pattern above) and to keep the native image alive until the graphics
69
+ // instance is disposed.
70
+ //
71
+ // Note that the following throws, so we only ever need to keep track of one Graphics
72
+ // instance at a time:
73
+ // Metafile mf = ...; // get a metafile instance
74
+ // Graphics g = Graphics.FromImage(mf);
75
+ // Graphics g2 = Graphics.FromImage(mf); // throws OutOfMemoryException on GDI+ on Win32
76
+ internal sealed class MetafileHolder : IDisposable
77
+ {
78
+ private bool _disposed ;
79
+ private IntPtr _nativeImage ;
80
+
81
+
82
+ internal bool Disposed { get => _disposed ; }
83
+ internal MetafileHolder ( )
84
+ {
85
+ _disposed = false ;
86
+ _nativeImage = IntPtr . Zero ;
87
+ }
88
+
89
+ ~ MetafileHolder ( ) => Dispose ( false ) ;
90
+
91
+ public void Dispose ( )
92
+ {
93
+ Dispose ( true ) ;
94
+ GC . SuppressFinalize ( this ) ;
95
+ }
96
+
97
+ internal void Dispose ( bool disposing )
98
+ {
99
+ if ( ! _disposed )
100
+ {
101
+ IntPtr nativeImage = _nativeImage ;
102
+ _nativeImage = IntPtr . Zero ;
103
+ _disposed = true ;
104
+ if ( nativeImage != IntPtr . Zero )
105
+ {
106
+ int status = Gdip . GdipDisposeImage ( nativeImage ) ;
107
+ Gdip . CheckStatus ( status ) ;
108
+ }
109
+ }
110
+ }
111
+
112
+ internal void MetafileDisposed ( IntPtr nativeImage )
113
+ {
114
+ _nativeImage = nativeImage ;
115
+ }
116
+
117
+ internal void GraphicsDisposed ( )
118
+ {
119
+ Dispose ( ) ;
120
+ }
121
+ }
122
+
123
+ internal MetafileHolder ? AddMetafileHolder ( )
124
+ {
125
+ // If _metafileHolder is not null and hasn't been disposed yet, there's already a graphics instance associated with
126
+ // this metafile, the native code will return an error status.
127
+ if ( _metafileHolder != null && ! _metafileHolder . Disposed )
128
+ return null ;
129
+ _metafileHolder = new MetafileHolder ( ) ;
130
+ return _metafileHolder ;
131
+ }
132
+
45
133
// Usually called when cloning images that need to have
46
134
// not only the handle saved, but also the underlying stream
47
135
// (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
142
230
Gdip . CheckStatus ( status ) ;
143
231
}
144
232
233
+ protected override void Dispose ( bool disposing )
234
+ {
235
+ if ( _metafileHolder != null && ! _metafileHolder . Disposed )
236
+ {
237
+ // There's a graphics instance created from this Metafile,
238
+ // transfer responsibility for disposing the nativeImage to the
239
+ // MetafileHolder
240
+ _metafileHolder . MetafileDisposed ( nativeImage ) ;
241
+ _metafileHolder = null ;
242
+ nativeImage = IntPtr . Zero ;
243
+ }
244
+
245
+ base . Dispose ( disposing ) ;
246
+ }
247
+
145
248
// methods
146
249
147
250
public IntPtr GetHenhmetafile ( )
0 commit comments