Separate registration for global ComWrappers instance for tracker support and marshal...
authorElinor Fung <47805090+elinor-fung@users.noreply.github.com>
Tue, 5 May 2020 01:57:40 +0000 (18:57 -0700)
committerGitHub <noreply@github.com>
Tue, 5 May 2020 01:57:40 +0000 (18:57 -0700)
14 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs
src/coreclr/src/vm/ecalllist.h
src/coreclr/src/vm/interopconverter.cpp
src/coreclr/src/vm/interoplibinterface.cpp
src/coreclr/src/vm/interoplibinterface.h
src/coreclr/src/vm/metasig.h
src/coreclr/src/vm/mscorlib.h
src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.cs [moved from src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/Program.cs with 93% similarity]
src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceMarshallingTests.csproj [moved from src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTests.csproj with 95% similarity]
src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs
src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs

index 0229ac9..323e60a 100644 (file)
@@ -57,6 +57,16 @@ namespace System.Runtime.InteropServices
     }
 
     /// <summary>
+    /// Internal enumeration used by the runtime to indicate the scenario for which ComWrappers is being used.
+    /// </summary>
+    internal enum ComWrappersScenario
+    {
+        Instance = 0,
+        TrackerSupportGlobalInstance = 1,
+        MarshallingGlobalInstance = 2,
+    }
+
+    /// <summary>
     /// Class for managing wrappers of COM IUnknown types.
     /// </summary>
     [CLSCompliant(false)]
@@ -107,9 +117,14 @@ namespace System.Runtime.InteropServices
         }
 
         /// <summary>
-        /// Globally registered instance of the ComWrappers class.
+        /// Globally registered instance of the ComWrappers class for reference tracker support.
         /// </summary>
-        private static ComWrappers? s_globalInstance;
+        private static ComWrappers? s_globalInstanceForTrackerSupport;
+
+        /// <summary>
+        /// Globally registered instance of the ComWrappers class for marshalling.
+        /// </summary>
+        private static ComWrappers? s_globalInstanceForMarshalling;
 
         /// <summary>
         /// Create a COM representation of the supplied object that can be passed to a non-managed environment.
@@ -164,10 +179,23 @@ namespace System.Runtime.InteropServices
         /// </remarks>
         protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
 
-        // Call to execute the abstract instance function
-        internal static unsafe void* CallComputeVtables(ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count)
+        // Called by the runtime to execute the abstract instance function
+        internal static unsafe void* CallComputeVtables(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, object obj, CreateComInterfaceFlags flags, out int count)
         {
-            ComWrappers? impl = comWrappersImpl ?? s_globalInstance;
+            ComWrappers? impl = null;
+            switch (scenario)
+            {
+                case ComWrappersScenario.Instance:
+                    impl = comWrappersImpl;
+                    break;
+                case ComWrappersScenario.TrackerSupportGlobalInstance:
+                    impl = s_globalInstanceForTrackerSupport;
+                    break;
+                case ComWrappersScenario.MarshallingGlobalInstance:
+                    impl = s_globalInstanceForMarshalling;
+                    break;
+            }
+
             if (impl is null)
             {
                 count = -1;
@@ -203,10 +231,23 @@ namespace System.Runtime.InteropServices
         /// </remarks>
         protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
 
-        // Call to execute the abstract instance function
-        internal static object? CallCreateObject(ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags)
+        // Called by the runtime to execute the abstract instance function.
+        internal static object? CallCreateObject(ComWrappersScenario scenario, ComWrappers? comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags)
         {
-            ComWrappers? impl = comWrappersImpl ?? s_globalInstance;
+            ComWrappers? impl = null;
+            switch (scenario)
+            {
+                case ComWrappersScenario.Instance:
+                    impl = comWrappersImpl;
+                    break;
+                case ComWrappersScenario.TrackerSupportGlobalInstance:
+                    impl = s_globalInstanceForTrackerSupport;
+                    break;
+                case ComWrappersScenario.MarshallingGlobalInstance:
+                    impl = s_globalInstanceForMarshalling;
+                    break;
+            }
+
             if (impl == null)
                 return null;
 
@@ -268,32 +309,62 @@ namespace System.Runtime.InteropServices
 
         // Call to execute the virtual instance function
         internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects)
-            => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects);
+            => (comWrappersImpl ?? s_globalInstanceForTrackerSupport!).ReleaseObjects(objects);
 
         /// <summary>
-        /// Register this class's implementation to be used as the single global instance.
+        /// Register a <see cref="ComWrappers" /> instance to be used as the global instance for reference tracker support.
         /// </summary>
+        /// <param name="instance">Instance to register</param>
         /// <remarks>
         /// This function can only be called a single time. Subsequent calls to this function will result
         /// in a <see cref="System.InvalidOperationException"/> being thrown.
         ///
-        /// Scenarios where the global instance may be used are:
+        /// Scenarios where this global instance may be used are:
         ///  * Object tracking via the <see cref="CreateComInterfaceFlags.TrackerSupport" /> and <see cref="CreateObjectFlags.TrackerObject" /> flags.
-        ///  * Usage of COM related Marshal APIs.
         /// </remarks>
-        public void RegisterAsGlobalInstance()
+        public static void RegisterForTrackerSupport(ComWrappers instance)
         {
-            if (null != Interlocked.CompareExchange(ref s_globalInstance, this, null))
+            if (instance == null)
+                throw new ArgumentNullException(nameof(instance));
+
+            if (null != Interlocked.CompareExchange(ref s_globalInstanceForTrackerSupport, instance, null))
+            {
+                throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance);
+            }
+        }
+
+        /// <summary>
+        /// Register a <see cref="ComWrappers" /> instance to be used as the global instance for marshalling in the runtime.
+        /// </summary>
+        /// <param name="instance">Instance to register</param>
+        /// <remarks>
+        /// This function can only be called a single time. Subsequent calls to this function will result
+        /// in a <see cref="System.InvalidOperationException"/> being thrown.
+        ///
+        /// Scenarios where this global instance may be used are:
+        ///  * Usage of COM-related Marshal APIs
+        ///  * P/Invokes with COM-related types
+        ///  * COM activation
+        /// </remarks>
+        public static void RegisterForMarshalling(ComWrappers instance)
+        {
+            if (instance == null)
+                throw new ArgumentNullException(nameof(instance));
+
+            if (null != Interlocked.CompareExchange(ref s_globalInstanceForMarshalling, instance, null))
             {
                 throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance);
             }
 
-            SetGlobalInstanceRegistered();
+            // Indicate to the runtime that a global instance has been registered for marshalling.
+            // This allows the native runtime know to call into the managed ComWrappers only if a
+            // global instance is registered for marshalling.
+            SetGlobalInstanceRegisteredForMarshalling();
         }
 
         [DllImport(RuntimeHelpers.QCall)]
         [SuppressGCTransition]
-        private static extern void SetGlobalInstanceRegistered();
+        private static extern void SetGlobalInstanceRegisteredForMarshalling();
 
         /// <summary>
         /// Get the runtime provided IUnknown implementation.
index 89d6bd5..87aa7b6 100644 (file)
@@ -985,7 +985,7 @@ FCFuncStart(gComWrappersFuncs)
     QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl)
     QCFuncElement("TryGetOrCreateComInterfaceForObjectInternal", ComWrappersNative::TryGetOrCreateComInterfaceForObject)
     QCFuncElement("TryGetOrCreateObjectForComInstanceInternal", ComWrappersNative::TryGetOrCreateObjectForComInstance)
-    QCFuncElement("SetGlobalInstanceRegistered", GlobalComWrappers::SetGlobalInstanceRegistered)
+    QCFuncElement("SetGlobalInstanceRegisteredForMarshalling", GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling)
 FCFuncEnd()
 #endif // FEATURE_COMWRAPPERS
 
index 44470d7..64312e1 100644 (file)
@@ -28,7 +28,7 @@ namespace
         _Outptr_ IUnknown** wrapperRaw)
     {
 #ifdef FEATURE_COMWRAPPERS
-        return GlobalComWrappers::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
+        return GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(instance, (void**)wrapperRaw);
 #else
         return false;
 #endif // FEATURE_COMWRAPPERS
@@ -40,7 +40,7 @@ namespace
         _Out_ OBJECTREF *pObjOut)
     {
 #ifdef FEATURE_COMWRAPPERS
-        return GlobalComWrappers::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
+        return GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(pUnknown, dwFlags, pObjOut);
 #else
         return false;
 #endif // FEATURE_COMWRAPPERS
index 48bdbe7..934e0bb 100644 (file)
@@ -406,7 +406,17 @@ namespace
     // Defined handle types for the specific object uses.
     const HandleType InstanceHandleType{ HNDTYPE_STRONG };
 
+    // Scenarios for ComWrappers usage.
+    // These values should match the managed definition in ComWrappers.
+    enum class ComWrappersScenario
+    {
+        Instance = 0,
+        TrackerSupportGlobalInstance = 1,
+        MarshallingGlobalInstance = 2,
+    };
+
     void* CallComputeVTables(
+        _In_ ComWrappersScenario scenario,
         _In_ OBJECTREF* implPROTECTED,
         _In_ OBJECTREF* instancePROTECTED,
         _In_ INT32 flags,
@@ -425,17 +435,19 @@ namespace
         void* vtables = NULL;
 
         PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__COMPUTE_VTABLES);
-        DECLARE_ARGHOLDER_ARRAY(args, 4);
-        args[ARGNUM_0]  = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
-        args[ARGNUM_1]  = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED);
-        args[ARGNUM_2]  = DWORD_TO_ARGHOLDER(flags);
-        args[ARGNUM_3]  = PTR_TO_ARGHOLDER(vtableCount);
+        DECLARE_ARGHOLDER_ARRAY(args, 5);
+        args[ARGNUM_0]  = DWORD_TO_ARGHOLDER(scenario);
+        args[ARGNUM_1]  = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
+        args[ARGNUM_2]  = OBJECTREF_TO_ARGHOLDER(*instancePROTECTED);
+        args[ARGNUM_3]  = DWORD_TO_ARGHOLDER(flags);
+        args[ARGNUM_4]  = PTR_TO_ARGHOLDER(vtableCount);
         CALL_MANAGED_METHOD(vtables, void*, args);
 
         return vtables;
     }
 
-    OBJECTREF CallGetObject(
+    OBJECTREF CallCreateObject(
+        _In_ ComWrappersScenario scenario,
         _In_ OBJECTREF* implPROTECTED,
         _In_ IUnknown* externalComObject,
         _In_ INT32 flags)
@@ -452,10 +464,11 @@ namespace
         OBJECTREF retObjRef;
 
         PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__CREATE_OBJECT);
-        DECLARE_ARGHOLDER_ARRAY(args, 3);
-        args[ARGNUM_0]  = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
-        args[ARGNUM_1]  = PTR_TO_ARGHOLDER(externalComObject);
-        args[ARGNUM_2]  = DWORD_TO_ARGHOLDER(flags);
+        DECLARE_ARGHOLDER_ARRAY(args, 4);
+        args[ARGNUM_0]  = DWORD_TO_ARGHOLDER(scenario);
+        args[ARGNUM_1]  = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
+        args[ARGNUM_2]  = PTR_TO_ARGHOLDER(externalComObject);
+        args[ARGNUM_3]  = DWORD_TO_ARGHOLDER(flags);
         CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args);
 
         return retObjRef;
@@ -511,6 +524,7 @@ namespace
         _In_opt_ OBJECTREF impl,
         _In_ OBJECTREF instance,
         _In_ CreateComInterfaceFlags flags,
+        _In_ ComWrappersScenario scenario,
         _Outptr_ void** wrapperRaw)
     {
         CONTRACT(bool)
@@ -519,6 +533,7 @@ namespace
             MODE_COOPERATIVE;
             PRECONDITION(instance != NULL);
             PRECONDITION(wrapperRaw != NULL);
+            PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance)|| (impl == NULL && scenario != ComWrappersScenario::Instance));
         }
         CONTRACT_END;
 
@@ -552,7 +567,7 @@ namespace
             // is taken. However, a key assumption here is that the returned memory will be
             // idempotent for the same object.
             DWORD vtableCount;
-            void* vtables = CallComputeVTables(&gc.implRef, &gc.instRef, flags, &vtableCount);
+            void* vtables = CallComputeVTables(scenario, &gc.implRef, &gc.instRef, flags, &vtableCount);
 
             // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper.
             if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRawMaybe)
@@ -621,16 +636,11 @@ namespace
         RETURN (wrapperRawMaybe != NULL);
     }
 
-    // The unwrap parameter indicates whether or not COM instances that are actually CCWs should
-    // be unwrapped to the original managed object.
-    // For implicit usage of ComWrappers (i.e. automatically called by the runtime when there is a global instance),
-    // CCWs should be unwrapped to allow for round-tripping object -> COM instance -> object.
-    // For explicit usage of ComWrappers (i.e. directly called via a ComWrappers APIs), CCWs should not be unwrapped.
     bool TryGetOrCreateObjectForComInstanceInternal(
         _In_opt_ OBJECTREF impl,
         _In_ IUnknown* identity,
         _In_ CreateObjectFlags flags,
-        _In_ bool unwrap,
+        _In_ ComWrappersScenario scenario,
         _In_opt_ OBJECTREF wrapperMaybe,
         _Out_ OBJECTREF* objRef)
     {
@@ -640,6 +650,7 @@ namespace
             MODE_COOPERATIVE;
             PRECONDITION(identity != NULL);
             PRECONDITION(objRef != NULL);
+            PRECONDITION((impl != NULL && scenario == ComWrappersScenario::Instance) || (impl == NULL && scenario != ComWrappersScenario::Instance));
         }
         CONTRACT_END;
 
@@ -670,8 +681,10 @@ namespace
             extObjCxt = cache->Find(identity);
 
             // If is no object found in the cache, check if the object COM instance is actually the CCW
-            // representing a managed object.
-            if (extObjCxt == NULL && unwrap)
+            // representing a managed object. For the scenario of marshalling through a global instance,
+            // COM instances that are actually CCWs should be unwrapped to the original managed object
+            // to allow for round-tripping object -> COM instance -> object.
+            if (extObjCxt == NULL && scenario == ComWrappersScenario::MarshallingGlobalInstance)
             {
                 // If the COM instance is a CCW that is not COM-activated, use the object of that wrapper object.
                 InteropLib::OBJECTHANDLE handleLocal;
@@ -717,7 +730,7 @@ namespace
             // If the wrapper hasn't been set yet, call the implementation to create one.
             if (gc.objRefMaybe == NULL)
             {
-                gc.objRefMaybe = CallGetObject(&gc.implRef, identity, flags);
+                gc.objRefMaybe = CallCreateObject(scenario, &gc.implRef, identity, flags);
             }
 
             // The object may be null if the specified ComWrapper implementation returns null
@@ -1017,14 +1030,13 @@ namespace InteropLibImports
 
             gc.implRef = NULL; // Use the globally registered implementation.
             gc.wrapperMaybeRef = NULL; // No supplied wrapper here.
-            bool unwrapIfManagedObjectWrapper = false; // Don't unwrap CCWs
 
             // Get wrapper for external object
             bool success = TryGetOrCreateObjectForComInstanceInternal(
                 gc.implRef,
                 externalComObject,
                 externalObjectFlags,
-                unwrapIfManagedObjectWrapper,
+                ComWrappersScenario::TrackerSupportGlobalInstance,
                 gc.wrapperMaybeRef,
                 &gc.objRef);
 
@@ -1036,6 +1048,7 @@ namespace InteropLibImports
                 gc.implRef,
                 gc.objRef,
                 trackerTargetFlags,
+                ComWrappersScenario::TrackerSupportGlobalInstance,
                 trackerTarget);
 
             if (!success)
@@ -1220,6 +1233,7 @@ BOOL QCALLTYPE ComWrappersNative::TryGetOrCreateComInterfaceForObject(
             ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
             ObjectToOBJECTREF(*instance.m_ppObject),
             (CreateComInterfaceFlags)flags,
+            ComWrappersScenario::Instance,
             wrapper);
     }
 
@@ -1256,13 +1270,12 @@ BOOL QCALLTYPE ComWrappersNative::TryGetOrCreateObjectForComInstance(
     {
         GCX_COOP();
 
-        bool unwrapIfManagedObjectWrapper = false; // Don't unwrap CCWs
         OBJECTREF newObj;
         success = TryGetOrCreateObjectForComInstanceInternal(
             ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
             identity,
             (CreateObjectFlags)flags,
-            unwrapIfManagedObjectWrapper,
+            ComWrappersScenario::Instance,
             ObjectToOBJECTREF(*wrapperMaybe.m_ppObject),
             &newObj);
 
@@ -1369,7 +1382,7 @@ void ComWrappersNative::MarkWrapperAsComActivated(_In_ IUnknown* wrapperMaybe)
     _ASSERTE(SUCCEEDED(hr) || hr == E_INVALIDARG);
 }
 
-void QCALLTYPE GlobalComWrappers::SetGlobalInstanceRegistered()
+void QCALLTYPE GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling()
 {
     // QCALL contracts are not used here because the managed declaration
     // uses the SuppressGCTransition attribute
@@ -1378,7 +1391,7 @@ void QCALLTYPE GlobalComWrappers::SetGlobalInstanceRegistered()
     g_IsGlobalComWrappersRegistered = true;
 }
 
-bool GlobalComWrappers::TryGetOrCreateComInterfaceForObject(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject(
     _In_ OBJECTREF instance,
     _Outptr_ void** wrapperRaw)
 {
@@ -1397,11 +1410,12 @@ bool GlobalComWrappers::TryGetOrCreateComInterfaceForObject(
             NULL,
             instance,
             flags,
+            ComWrappersScenario::MarshallingGlobalInstance,
             wrapperRaw);
     }
 }
 
-bool GlobalComWrappers::TryGetOrCreateObjectForComInstance(
+bool GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance(
     _In_ IUnknown* externalComObject,
     _In_ INT32 objFromComIPFlags,
     _Out_ OBJECTREF* objRef)
@@ -1427,16 +1441,12 @@ bool GlobalComWrappers::TryGetOrCreateObjectForComInstance(
         if ((objFromComIPFlags & ObjFromComIP::UNIQUE_OBJECT) != 0)
             flags |= CreateObjectFlags::CreateObjectFlags_UniqueInstance;
 
-        // For implicit usage of ComWrappers (i.e. automatically called by the runtime when there is a global instance),
-        // unwrap CCWs to allow for round-tripping object -> COM instance -> object.
-        bool unwrapIfManagedObjectWrapper = true;
-
         // Passing NULL as the ComWrappers implementation indicates using the globally registered instance
         return TryGetOrCreateObjectForComInstanceInternal(
             NULL /*comWrappersImpl*/,
             identity,
             (CreateObjectFlags)flags,
-            unwrapIfManagedObjectWrapper,
+            ComWrappersScenario::MarshallingGlobalInstance,
             NULL /*wrapperMaybe*/,
             objRef);
     }
index 2985412..6a5884a 100644 (file)
@@ -39,14 +39,15 @@ public: // COM activation
     static void MarkWrapperAsComActivated(_In_ IUnknown* wrapperMaybe);
 };
 
-class GlobalComWrappers
+class GlobalComWrappersForMarshalling
 {
 public:
-    // Native QCall for the ComWrappers managed type to indicate a global instance is registered
-    // This should be set if the private static member representing the global instance on ComWrappers is non-null.
-    static void QCALLTYPE SetGlobalInstanceRegistered();
+    // Native QCall for the ComWrappers managed type to indicate a global instance
+    // is registered for marshalling. This should be set if the private static member
+    // representing the global instance for marshalling on ComWrappers is non-null.
+    static void QCALLTYPE SetGlobalInstanceRegisteredForMarshalling();
 
-public: // Functions operating on a registered global instance
+public: // Functions operating on a registered global instance for marshalling
     static bool TryGetOrCreateComInterfaceForObject(
         _In_ OBJECTREF instance,
         _Outptr_ void** wrapperRaw);
index 6447dc7..30ca8fe 100644 (file)
@@ -196,8 +196,8 @@ DEFINE_METASIG_T(SM(PtrTypeName_ArrType_RetVoid, P(g(TYPENAMENATIVE)) a(C(TYPE))
 DEFINE_METASIG_T(SM(PtrTypeName_RetVoid, P(g(TYPENAMENATIVE)), v))
 DEFINE_METASIG_T(SM(PtrTypeName_Int_RetVoid, P(g(TYPENAMENATIVE)) i, v))
 DEFINE_METASIG_T(SM(Exception_IntPtr_RetException, C(EXCEPTION) I, C(EXCEPTION)))
-DEFINE_METASIG_T(SM(ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v)))
-DEFINE_METASIG_T(SM(ComWrappers_IntPtr_CreateFlags_RetObj, C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j))
+DEFINE_METASIG_T(SM(Scenario_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v)))
+DEFINE_METASIG_T(SM(Scenario_ComWrappers_IntPtr_CreateFlags_RetObj, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j))
 DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v))
 DEFINE_METASIG_T(SM(Obj_RefGuid_RefIntPtr_RetInt, j r(g(GUID)) r(I), i))
 #endif // FEATURE_COMINTEROP
index 5317a56..7c02584 100644 (file)
@@ -462,8 +462,9 @@ DEFINE_CLASS(CUSTOMQUERYINTERFACERESULT,  Interop,          CustomQueryInterface
 DEFINE_CLASS(COMWRAPPERS,                 Interop,          ComWrappers)
 DEFINE_CLASS(CREATECOMINTERFACEFLAGS,     Interop,          CreateComInterfaceFlags)
 DEFINE_CLASS(CREATEOBJECTFLAGS,           Interop,          CreateObjectFlags)
-DEFINE_METHOD(COMWRAPPERS,                COMPUTE_VTABLES,  CallComputeVtables,         SM_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid)
-DEFINE_METHOD(COMWRAPPERS,                CREATE_OBJECT,    CallCreateObject,           SM_ComWrappers_IntPtr_CreateFlags_RetObj)
+DEFINE_CLASS(COMWRAPPERSSCENARIO,         Interop,          ComWrappersScenario)
+DEFINE_METHOD(COMWRAPPERS,                COMPUTE_VTABLES,  CallComputeVtables,         SM_Scenario_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid)
+DEFINE_METHOD(COMWRAPPERS,                CREATE_OBJECT,    CallCreateObject,           SM_Scenario_ComWrappers_IntPtr_CreateFlags_RetObj)
 DEFINE_METHOD(COMWRAPPERS,                RELEASE_OBJECTS,  CallReleaseObjects,         SM_ComWrappers_IEnumerable_RetVoid)
 DEFINE_METHOD(COMWRAPPERS,     CALL_ICUSTOMQUERYINTERFACE,  CallICustomQueryInterface,  SM_Obj_RefGuid_RefIntPtr_RetInt)
 #endif //FEATURE_COMINTEROP
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.Marshalling.cs
new file mode 100644 (file)
index 0000000..b2315ff
--- /dev/null
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ComWrappersTests.GlobalInstance
+{
+    using System;
+
+    using ComWrappersTests.Common;
+    using TestLibrary;
+
+    partial class Program
+    {
+        private static void ValidateNotRegisteredForTrackerSupport()
+        {
+            Console.WriteLine($"Running {nameof(ValidateNotRegisteredForTrackerSupport)}...");
+
+            int hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread();
+            Assert.AreNotEqual(GlobalComWrappers.ReleaseObjectsCallAck, hr);
+        }
+
+        static int Main(string[] doNotUse)
+        {
+            try
+            {
+                // The first test registers a global ComWrappers instance for marshalling
+                // Subsequents tests assume the global instance has already been registered.
+                ValidateRegisterForMarshalling();
+
+                ValidateMarshalAPIs(validateUseRegistered: true);
+                ValidateMarshalAPIs(validateUseRegistered: false);
+
+                ValidatePInvokes(validateUseRegistered: true);
+                ValidatePInvokes(validateUseRegistered: false);
+
+                ValidateComActivation(validateUseRegistered: true);
+                ValidateComActivation(validateUseRegistered: false);
+
+                ValidateNotRegisteredForTrackerSupport();
+
+                // Register a global ComWrappers instance for tracker support
+                ValidateRegisterForTrackerSupport();
+
+                ValidateNotifyEndOfReferenceTrackingOnThread();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
+
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstance.TrackerSupport.cs
new file mode 100644 (file)
index 0000000..4d4f1c6
--- /dev/null
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace ComWrappersTests.GlobalInstance
+{
+    using System;
+    using System.Runtime.InteropServices;
+
+    using ComWrappersTests.Common;
+    using TestLibrary;
+
+    partial class Program
+    {
+        private static void ValidateNotRegisteredForMarshalling()
+        {
+            Console.WriteLine($"Running {nameof(ValidateNotRegisteredForMarshalling)}...");
+
+            var testObj = new Test();
+            IntPtr comWrapper1 = Marshal.GetIUnknownForObject(testObj);
+            Assert.IsNull(GlobalComWrappers.Instance.LastComputeVtablesObject, "ComWrappers instance should not have been called");
+
+            IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+            object objWrapper = Marshal.GetObjectForIUnknown(trackerObjRaw);
+            Assert.IsFalse(objWrapper is FakeWrapper, $"ComWrappers instance should not have been called");
+        }
+
+        static int Main(string[] doNotUse)
+        {
+            try
+            {
+                // The first test registers a global ComWrappers instance for tracker support.
+                // Subsequents tests assume the global instance has already been registered.
+                ValidateRegisterForTrackerSupport();
+
+                ValidateNotRegisteredForMarshalling();
+
+                IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+                var trackerObj = GlobalComWrappers.Instance.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject);
+                Marshal.Release(trackerObjRaw);
+
+                ValidateNotifyEndOfReferenceTrackingOnThread();
+
+                // Register a global ComWrappers instance for marshalling.
+                ValidateRegisterForMarshalling();
+
+                ValidateMarshalAPIs(validateUseRegistered: true);
+                ValidateMarshalAPIs(validateUseRegistered: false);
+
+                ValidatePInvokes(validateUseRegistered: true);
+                ValidatePInvokes(validateUseRegistered: false);
+
+                ValidateComActivation(validateUseRegistered: true);
+                ValidateComActivation(validateUseRegistered: false);
+
+                ValidateNotifyEndOfReferenceTrackingOnThread();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
+
@@ -12,7 +12,7 @@ namespace ComWrappersTests.GlobalInstance
     using ComWrappersTests.Common;
     using TestLibrary;
 
-    class Program
+    partial class Program
     {
         struct MarshalInterface
         {
@@ -105,7 +105,7 @@ namespace ComWrappersTests.GlobalInstance
 
             public bool ReturnInvalid { get; set; }
 
-            public object LastComputeVtablesObject { get; private set; }
+            public object LastComputeVtablesObject { get; private set; } = null;
 
             protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
             {
@@ -227,23 +227,43 @@ namespace ComWrappersTests.GlobalInstance
             }
         }
 
-        private static void ValidateRegisterAsGlobalInstance()
+        private static void ValidateRegisterForMarshalling()
         {
-            Console.WriteLine($"Running {nameof(ValidateRegisterAsGlobalInstance)}...");
+            Console.WriteLine($"Running {nameof(ValidateRegisterForMarshalling)}...");
 
             var wrappers1 = GlobalComWrappers.Instance;
-            wrappers1.RegisterAsGlobalInstance();
+            ComWrappers.RegisterForMarshalling(wrappers1);
             Assert.Throws<InvalidOperationException>(
                 () =>
                 {
-                    wrappers1.RegisterAsGlobalInstance();
+                    ComWrappers.RegisterForMarshalling(wrappers1);
                 }, "Should not be able to re-register for global ComWrappers");
 
             var wrappers2 = new GlobalComWrappers();
             Assert.Throws<InvalidOperationException>(
                 () =>
                 {
-                    wrappers2.RegisterAsGlobalInstance();
+                    ComWrappers.RegisterForMarshalling(wrappers2);
+                }, "Should not be able to reset for global ComWrappers");
+        }
+
+        private static void ValidateRegisterForTrackerSupport()
+        {
+            Console.WriteLine($"Running {nameof(ValidateRegisterForTrackerSupport)}...");
+
+            var wrappers1 = GlobalComWrappers.Instance;
+            ComWrappers.RegisterForTrackerSupport(wrappers1);
+            Assert.Throws<InvalidOperationException>(
+                () =>
+                {
+                    ComWrappers.RegisterForTrackerSupport(wrappers1);
+                }, "Should not be able to re-register for global ComWrappers");
+
+            var wrappers2 = new GlobalComWrappers();
+            Assert.Throws<InvalidOperationException>(
+                () =>
+                {
+                    ComWrappers.RegisterForTrackerSupport(wrappers2);
                 }, "Should not be able to reset for global ComWrappers");
         }
 
@@ -418,34 +438,6 @@ namespace ComWrappersTests.GlobalInstance
             int hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread();
             Assert.AreEqual(GlobalComWrappers.ReleaseObjectsCallAck, hr);
         }
-
-        static int Main(string[] doNotUse)
-        {
-            try
-            {
-                // The first test registereds a global ComWrappers instance
-                // Subsequents tests assume the global instance has already been registered.
-                ValidateRegisterAsGlobalInstance();
-
-                ValidateMarshalAPIs(validateUseRegistered: true);
-                ValidateMarshalAPIs(validateUseRegistered: false);
-
-                ValidatePInvokes(validateUseRegistered: true);
-                ValidatePInvokes(validateUseRegistered: false);
-
-                ValidateComActivation(validateUseRegistered: true);
-                ValidateComActivation(validateUseRegistered: false);
-
-                ValidateNotifyEndOfReferenceTrackingOnThread();
-            }
-            catch (Exception e)
-            {
-                Console.WriteLine($"Test Failure: {e}");
-                return 101;
-            }
-
-            return 100;
-        }
     }
 }
 
@@ -15,7 +15,8 @@
   </PropertyGroup>
   <Import Project="$([MSBuild]::GetPathOfFileAbove(Interop.settings.targets))" />
   <ItemGroup>
-    <Compile Include="Program.cs" />
+    <Compile Include="GlobalInstance.cs" />
+    <Compile Include="GlobalInstance.Marshalling.cs" />
     <Compile Include="../Common.cs" />
     <Compile Include="../../ServerContracts/Server.Contracts.cs" />
     <Compile Include="../../ServerContracts/ServerGuids.cs" />
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/GlobalInstance/GlobalInstanceTrackerSupportTests.csproj
new file mode 100644 (file)
index 0000000..4742bf4
--- /dev/null
@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <ApplicationManifest>App.manifest</ApplicationManifest>
+    <CLRTestScriptLocalCoreShim>true</CLRTestScriptLocalCoreShim>
+    <RequiresMockHostPolicy>true</RequiresMockHostPolicy>
+    <!-- Blocked on ILAsm supporting embedding resources. See https://github.com/dotnet/coreclr/issues/20819 -->
+    <IlrtTestKind>BuildOnly</IlrtTestKind>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsWindows)' != 'true'">true</DisableProjectBuild>
+    <!-- This test would require the runincontext.exe to include App.manifest describing the COM interfaces -->
+    <UnloadabilityIncompatible>true</UnloadabilityIncompatible>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Interop.settings.targets))" />
+  <ItemGroup>
+    <Compile Include="GlobalInstance.cs" />
+    <Compile Include="GlobalInstance.TrackerSupport.cs" />
+    <Compile Include="../Common.cs" />
+    <Compile Include="../../ServerContracts/Server.Contracts.cs" />
+    <Compile Include="../../ServerContracts/ServerGuids.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="../MockReferenceTrackerRuntime/CMakeLists.txt" />
+    <ProjectReference Include="../../NativeServer/CMakeLists.txt" />
+    <ProjectReference Include="../../NETServer/NETServer.csproj">
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+      <OutputItemType>Content</OutputItemType>
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="CoreShim.X.manifest">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+</Project>
index 05764b0..d78f3d6 100644 (file)
@@ -62,7 +62,12 @@ namespace System.Runtime.InteropServices
 
         protected abstract void ReleaseObjects(IEnumerable objects);
 
-        public void RegisterAsGlobalInstance()
+        public static void RegisterForTrackerSupport(ComWrappers instance)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        public static void RegisterForMarshalling(ComWrappers instance)
         {
             throw new PlatformNotSupportedException();
         }
index 965afd8..d2a0de7 100644 (file)
@@ -1006,7 +1006,8 @@ namespace System.Runtime.InteropServices
         protected abstract object CreateObject(System.IntPtr externalComObject, CreateObjectFlags flags);
         public object GetOrRegisterObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags, object wrapper) { throw null; }
         protected abstract void ReleaseObjects(System.Collections.IEnumerable objects);
-        public void RegisterAsGlobalInstance() { }
+        public static void RegisterForTrackerSupport(ComWrappers instance) { }
+        public static void RegisterForMarshalling(ComWrappers instance) { }
         protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; }
     }
     [System.AttributeUsageAttribute(System.AttributeTargets.Method)]