[corlib] Fix exception in __ComObject finalizer during shutdown
authorDamien Daspit <damien_daspit@sil.org>
Tue, 2 Dec 2014 08:35:09 +0000 (15:35 +0700)
committerEberhard Beilharz <eb1@sil.org>
Fri, 29 Jan 2016 21:50:10 +0000 (22:50 +0100)
* keep a reference to a __ComObject's proxy so it doesn't get garbage
  collected
* don't create __ComObjects unnecessarily
* Fix for bug 24974

Change-Id: Ia09237b7f1af996405134c250bbc29220f81f067

mcs/class/corlib/Mono.Interop/ComInteropProxy.cs
mcs/class/corlib/System/__ComObject.cs

index b97f416a83d50549462a94c2bb9b9f9fd10a9c78..88d713ed8c2a87836d455ed7d2884e0e47c8bd06 100644 (file)
@@ -68,7 +68,7 @@ namespace Mono.Interop
                {
                        // called from unmanaged code after .ctor is invoked
                        // we need .ctor to create unmanaged object and thus IUnknown property value
-                       if (FindProxy(com_object.IUnknown) == null)
+                       if (FindProxy (com_object.IUnknown) == null)
                                AddProxy (com_object.IUnknown, this);
                        else
                                System.Threading.Interlocked.Increment (ref ref_count);
@@ -82,7 +82,7 @@ namespace Mono.Interop
                internal ComInteropProxy (IntPtr pUnk, Type t)
                        : base (t)
                {
-                       com_object = new __ComObject (pUnk);
+                       com_object = new __ComObject (pUnk, this);
                        CacheProxy ();
                }
 
@@ -108,17 +108,20 @@ namespace Mono.Interop
                // already known, a cached proxy will be returned.
                internal static ComInteropProxy CreateProxy (Type t)
                {
-                       ComInteropProxy proxy = new ComInteropProxy (t);
-                       proxy.com_object.Initialize (t);
-
-                       ComInteropProxy cachedProxy = FindProxy (proxy.com_object.IUnknown);
+                       IntPtr iunknown = __ComObject.CreateIUnknown (t);
+                       ComInteropProxy proxy;
+                       ComInteropProxy cachedProxy = FindProxy (iunknown);
                        if (cachedProxy != null) {
                                // check that the COM type of the cached proxy matches
                                // the requested type. See 2nd part of bug #520437.
                                Type cachedType = cachedProxy.com_object.GetType ();
                                if (cachedType != t)
                                        throw new InvalidCastException (String.Format ("Unable to cast object of type '{0}' to type '{1}'.", cachedType, t));
-                               return cachedProxy;
+                               proxy = cachedProxy;
+                               Marshal.Release (iunknown);
+                       } else {
+                               proxy = new ComInteropProxy (t);
+                               proxy.com_object.Initialize (iunknown, proxy);
                        }
                        return proxy;
                }
index 18f0c43ec49761d5097b6a32b97c2910c4031816..ac50ebfaea236153d53a873183519beb741e7e10 100644 (file)
@@ -63,6 +63,9 @@ namespace System
                #endregion
 #pragma warning restore 169
 
+               // keep a reference to the proxy so it doesn't get garbage collected before the RCW
+               ComInteropProxy proxy;
+
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                internal static extern __ComObject CreateRCW (Type t);
 
@@ -71,10 +74,13 @@ namespace System
 
                ~__ComObject ()
                {       
-                       if (synchronization_context != null)
-                               synchronization_context.Post ((state) => ReleaseInterfaces (), this);
-                       else
-                               ReleaseInterfaces ();                           
+                       if (hash_table != IntPtr.Zero) {
+                               if (synchronization_context != null)
+                                       synchronization_context.Post ((state) => ReleaseInterfaces (), this);
+                               else
+                                       ReleaseInterfaces ();
+                       }
+                       proxy = null;
                }
 
                public __ComObject ()
@@ -86,14 +92,22 @@ namespace System
                        Initialize (t);
                }
 
-               internal __ComObject (IntPtr pItf)
+               internal __ComObject (IntPtr pItf, ComInteropProxy p)
                {
+                       proxy = p;
                        InitializeApartmentDetails ();
                        Guid iid = IID_IUnknown;
                        int hr = Marshal.QueryInterface (pItf, ref iid, out iunknown);
                        Marshal.ThrowExceptionForHR (hr);
                }
 
+               internal void Initialize (IntPtr pUnk, ComInteropProxy p)
+               {
+                       proxy = p;
+                       InitializeApartmentDetails ();
+                       iunknown = pUnk;
+               }
+
                internal void Initialize (Type t)
                {
                        InitializeApartmentDetails ();
@@ -101,8 +115,14 @@ namespace System
                        if (iunknown != IntPtr.Zero)
                                return;
 
+                       iunknown = CreateIUnknown (t);
+               }
+
+               internal static IntPtr CreateIUnknown(Type t)
+               {
                        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (t.TypeHandle);
-                       
+
+                       IntPtr iunknown;
                        ObjectCreationDelegate ocd = ExtensibleClassFactory.GetObjectCreationCallback (t);
                        if (ocd != null) {
                                iunknown = ocd (IntPtr.Zero);
@@ -113,6 +133,8 @@ namespace System
                                int hr = CoCreateInstance (GetCLSID (t), IntPtr.Zero, 0x1 | 0x4 | 0x10, IID_IUnknown, out iunknown);
                                Marshal.ThrowExceptionForHR (hr);
                        }
+
+                       return iunknown;
                }
 
                private void InitializeApartmentDetails ()