Convert some COM object checking functions to managed code (#54471)
authorAaron Robinson <arobins@microsoft.com>
Tue, 22 Jun 2021 06:44:16 +0000 (23:44 -0700)
committerGitHub <noreply@github.com>
Tue, 22 Jun 2021 06:44:16 +0000 (23:44 -0700)
* Convert COM object checking to managed code

* Convert IsComWrapperClass to a managed "can cast to" implementation.

* Add testing for updates.

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
src/coreclr/vm/ecalllist.h
src/coreclr/vm/interoputil.cpp
src/coreclr/vm/interoputil.h
src/coreclr/vm/marshalnative.cpp
src/coreclr/vm/marshalnative.h
src/coreclr/vm/runtimehandles.cpp
src/coreclr/vm/runtimehandles.h
src/tests/Interop/COM/NETClients/Aggregation/Program.cs
src/tests/Interop/COM/NETClients/ConsumeNETServer/Program.cs

index 688e4f8..e064c36 100644 (file)
@@ -464,8 +464,15 @@ namespace System.Runtime.InteropServices
         /// <summary>
         /// Checks if the object is classic COM component.
         /// </summary>
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        public static extern bool IsComObject(object o);
+        public static bool IsComObject(object o)
+        {
+            if (o is null)
+            {
+                throw new ArgumentNullException(nameof(o));
+            }
+
+            return o is __ComObject;
+        }
 
         /// <summary>
         /// Release the COM component and if the reference hits 0 zombie this object.
index 75aff55..dd17a2f 100644 (file)
@@ -460,8 +460,17 @@ namespace System
             return GetInterfaceMethodImplementation(new QCallTypeHandle(ref nativeHandle), new QCallTypeHandle(ref nativeInterfaceHandle), interfaceMethodHandle);
         }
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern bool IsComObject(RuntimeType type, bool isGenericCOM);
+        internal static bool IsComObject(RuntimeType type, bool isGenericCOM)
+        {
+#if FEATURE_COMINTEROP
+            if (isGenericCOM)
+                return type == typeof(__ComObject);
+
+            return RuntimeTypeHandle.CanCastTo(type, (RuntimeType)typeof(__ComObject));
+#else
+            return false;
+#endif
+        }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern bool IsInterface(RuntimeType type);
index 48a2d7d..1822422 100644 (file)
@@ -209,7 +209,6 @@ FCFuncStart(gCOMTypeHandleFuncs)
     FCFuncElement("GetNumVirtualsAndStaticVirtuals", RuntimeTypeHandle::GetNumVirtualsAndStaticVirtuals)
     QCFuncElement("VerifyInterfaceIsImplemented", RuntimeTypeHandle::VerifyInterfaceIsImplemented)
     QCFuncElement("GetInterfaceMethodImplementation", RuntimeTypeHandle::GetInterfaceMethodImplementation)
-    FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject)
     FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType)
     FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface)
     FCFuncElement("IsByRefLike", RuntimeTypeHandle::IsByRefLike)
@@ -768,7 +767,6 @@ FCFuncStart(gInteropMarshalFuncs)
 
 #ifdef FEATURE_COMINTEROP
     FCFuncElement("GetHRForException", MarshalNative::GetHRForException)
-    FCFuncElement("IsComObject", MarshalNative::IsComObject)
     FCFuncElement("GetObjectForIUnknownNative", MarshalNative::GetObjectForIUnknownNative)
     FCFuncElement("GetUniqueObjectForIUnknownNative", MarshalNative::GetUniqueObjectForIUnknownNative)
     FCFuncElement("GetNativeVariantForObjectNative", MarshalNative::GetNativeVariantForObjectNative)
index e3797b1..2b66978 100644 (file)
@@ -820,25 +820,6 @@ BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT)
     }
 }
 
-// Returns TRUE iff the argument represents the "__ComObject" type or
-// any type derived from it (i.e. typelib-imported RCWs).
-BOOL IsComWrapperClass(TypeHandle type)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_ANY;
-    }
-    CONTRACTL_END;
-
-    MethodTable* pMT = type.GetMethodTable();
-    if (pMT == NULL)
-        return FALSE;
-
-    return pMT->IsComObjectType();
-}
-
 // Returns TRUE iff the argument represents the "__ComObject" type.
 BOOL IsComObjectClass(TypeHandle type)
 {
index c720946..b997646 100644 (file)
@@ -83,10 +83,6 @@ ULONG SafeReleasePreemp(IUnknown* pUnk, RCW* pRCW = NULL);
 // Determines if a COM object can be cast to the specified type.
 BOOL CanCastComObject(OBJECTREF obj, MethodTable * pTargetMT);
 
-// includes Types which hold a "ComObject" class
-// and types which are imported through typelib
-BOOL IsComWrapperClass(TypeHandle type);
-
 // includes Type which hold a "__ComObject" class
 BOOL IsComObjectClass(TypeHandle type);
 
index 6d1c38b..b28f34a 100644 (file)
@@ -947,30 +947,6 @@ FCIMPL0(FC_BOOL_RET, MarshalNative::AreComObjectsAvailableForCleanup)
 FCIMPLEND
 
 //====================================================================
-// check if the object is classic COM component
-//====================================================================
-FCIMPL1(FC_BOOL_RET, MarshalNative::IsComObject, Object* objUNSAFE)
-{
-    FCALL_CONTRACT;
-
-    BOOL retVal = FALSE;
-    OBJECTREF obj = (OBJECTREF) objUNSAFE;
-    HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
-
-    if(!obj)
-        COMPlusThrowArgumentNull(W("o"));
-
-    MethodTable* pMT = obj->GetMethodTable();
-    PREFIX_ASSUME(pMT != NULL);
-    retVal = pMT->IsComObjectType();
-
-    HELPER_METHOD_FRAME_END();
-    FC_RETURN_BOOL(retVal);
-}
-FCIMPLEND
-
-
-//====================================================================
 // free the COM component and zombie this object if the ref count hits 0
 // further usage of this Object might throw an exception,
 //====================================================================
index 790c731..8a36152 100644 (file)
@@ -104,11 +104,6 @@ public:
     static FCDECL2(IUnknown*, CreateAggregatedObjectNative, IUnknown* pOuter, Object* refObjUNSAFE);
 
     //====================================================================
-    // check if the object is classic COM component
-    //====================================================================
-    static FCDECL1(FC_BOOL_RET, IsComObject, Object* objUNSAFE);
-
-    //====================================================================
     // free the COM component and zombie this object
     // further usage of this Object might throw an exception,
     //====================================================================
index a8851f3..d3c2153 100644 (file)
@@ -1033,34 +1033,6 @@ RuntimeTypeHandle::IsVisible(
     return fIsExternallyVisible;
 } // RuntimeTypeHandle::IsVisible
 
-FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsComObject, ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL isGenericCOM) {
-    CONTRACTL {
-        FCALL_CHECK;
-    }
-    CONTRACTL_END;
-
-    BOOL ret = FALSE;
-
-    REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);
-
-    if (refType == NULL)
-        FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle"));
-
-    TypeHandle typeHandle = refType->GetType();
-
-    HELPER_METHOD_FRAME_BEGIN_RET_1(refType);
-    {
-        if (isGenericCOM)
-            ret = IsComObjectClass(typeHandle);
-        else
-            ret = IsComWrapperClass(typeHandle);
-    }
-    HELPER_METHOD_FRAME_END();
-
-    FC_RETURN_BOOL(ret);
-}
-FCIMPLEND
-
 FCIMPL1(LPCUTF8, RuntimeTypeHandle::GetUtf8Name, ReflectClassBaseObject* pTypeUNSAFE) {
     CONTRACTL {
         FCALL_CHECK;
index d40b454..33645ad 100644 (file)
@@ -191,7 +191,6 @@ public:
     static
     BOOL QCALLTYPE IsVisible(QCall::TypeHandle pTypeHandle);
 
-    static FCDECL2(FC_BOOL_RET, IsComObject, ReflectClassBaseObject *pType, CLR_BOOL isGenericCOM);
     static FCDECL2(FC_BOOL_RET, CanCastTo, ReflectClassBaseObject *pType, ReflectClassBaseObject *pTarget);
     static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object);
 
index ee1984f..2072b41 100644 (file)
@@ -21,6 +21,12 @@ namespace NetClient
             var managedInner = new ManagedInner();
             var nativeOuter = (AggregationTesting)managedInner;
 
+            Assert.IsTrue(typeof(ManagedInner).IsCOMObject);
+            Assert.IsTrue(typeof(AggregationTestingClass).IsCOMObject);
+            Assert.IsFalse(typeof(AggregationTesting).IsCOMObject);
+            Assert.IsTrue(Marshal.IsComObject(managedInner));
+            Assert.IsTrue(Marshal.IsComObject(nativeOuter));
+
             Assert.IsTrue(nativeOuter.IsAggregated());
             Assert.IsTrue(nativeOuter.AreAggregated(managedInner, nativeOuter));
             Assert.IsFalse(nativeOuter.AreAggregated(nativeOuter, new object()));
index a78274e..1bade41 100644 (file)
@@ -24,6 +24,9 @@ namespace NetClient
 
             // The CoClass should be the activated type, _not_ the activation interface.
             Assert.AreEqual(test.GetType(), typeof(CoClass.ConsumeNETServerTestingClass));
+            Assert.IsTrue(typeof(CoClass.ConsumeNETServerTestingClass).IsCOMObject);
+            Assert.IsFalse(typeof(CoClass.ConsumeNETServerTesting).IsCOMObject);
+            Assert.IsTrue(Marshal.IsComObject(test));
         }
 
         static void Validate_CCW_Wasnt_Unwrapped()