Implement new COM interop API for RCW/CCW creation/management (#32091)
authorAaron Robinson <arobins@microsoft.com>
Sat, 7 Mar 2020 07:37:47 +0000 (23:37 -0800)
committerGitHub <noreply@github.com>
Sat, 7 Mar 2020 07:37:47 +0000 (23:37 -0800)
* Implement RuntimeHelpers.AllocateTypeAssociatedMemory().

* Add tests for RuntimeHelpers.AllocateTypeAssociatedMemory().

* Implement ComWrappers API.

* Add tests for ComWrappers API.

* Add a FEATURE_COMWRAPPERS feature flag.

53 files changed:
src/coreclr/clr.featuredefines.props
src/coreclr/clrdefinitions.cmake
src/coreclr/src/CMakeLists.txt
src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs [new file with mode: 0644]
src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt
src/coreclr/src/inc/CrstTypes.def
src/coreclr/src/inc/crsttypes.h
src/coreclr/src/interop/CMakeLists.txt [new file with mode: 0644]
src/coreclr/src/interop/comwrappers.cpp [new file with mode: 0644]
src/coreclr/src/interop/comwrappers.hpp [new file with mode: 0644]
src/coreclr/src/interop/inc/interoplib.h [new file with mode: 0644]
src/coreclr/src/interop/inc/interoplibimports.h [new file with mode: 0644]
src/coreclr/src/interop/interoplib.cpp [new file with mode: 0644]
src/coreclr/src/interop/platform.h [new file with mode: 0644]
src/coreclr/src/interop/referencetrackertypes.hpp [new file with mode: 0644]
src/coreclr/src/interop/trackerobjectmanager.cpp [new file with mode: 0644]
src/coreclr/src/vm/CMakeLists.txt
src/coreclr/src/vm/ceemain.cpp
src/coreclr/src/vm/ecalllist.h
src/coreclr/src/vm/gcenv.ee.cpp
src/coreclr/src/vm/gcenv.ee.standalone.cpp
src/coreclr/src/vm/gcenv.ee.static.cpp
src/coreclr/src/vm/interoplibinterface.cpp [new file with mode: 0644]
src/coreclr/src/vm/interoplibinterface.h [new file with mode: 0644]
src/coreclr/src/vm/interoputil.cpp
src/coreclr/src/vm/metasig.h
src/coreclr/src/vm/mscorlib.cpp
src/coreclr/src/vm/mscorlib.h
src/coreclr/src/vm/rcwrefcache.cpp
src/coreclr/src/vm/rcwrefcache.h
src/coreclr/src/vm/rcwwalker.h
src/coreclr/src/vm/runtimehandles.cpp
src/coreclr/src/vm/runtimehandles.h
src/coreclr/src/vm/syncblk.h
src/coreclr/tests/src/Interop/CMakeLists.txt
src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs [new file with mode: 0644]
src/coreclr/tests/src/Interop/COM/NativeServer/Servers.h
src/coreclr/tests/src/Interop/common/ComHelpers.h
src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.cs
src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs [new file with mode: 0644]
src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.csproj [moved from src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/ExecuteCodeWithGuaranteedCleanup.csproj with 65% similarity]
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs
src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.Mono.cs

index 7ae2f2f..b407467 100644 (file)
@@ -28,6 +28,7 @@
         <FeatureArrayStubAsIL Condition="'$(Platform)' != 'x86'">true</FeatureArrayStubAsIL>
         <FeatureMulticastStubAsIL Condition="'$(Platform)' != 'x86'">true</FeatureMulticastStubAsIL>
         <FeatureStubsAsIL Condition="'$(Platform)' == 'arm64'">true</FeatureStubsAsIL>
+        <FeatureComWrappers>true</FeatureComWrappers>
         <FeatureCominterop>true</FeatureCominterop>
         <FeatureClassicCominterop>true</FeatureClassicCominterop>
         <FeatureCominteropUnmanagedActivation>true</FeatureCominteropUnmanagedActivation>
@@ -54,6 +55,7 @@
         <DefineConstants Condition="'$(FeatureStubsAsIL)' == 'true'">$(DefineConstants);FEATURE_STUBS_AS_IL</DefineConstants>
         <DefineConstants Condition="'$(FeatureClassicCominterop)' == 'true'">$(DefineConstants);FEATURE_CLASSIC_COMINTEROP</DefineConstants>
         <DefineConstants Condition="'$(FeatureCollectibleALC)' == 'true'">$(DefineConstants);FEATURE_COLLECTIBLE_ALC</DefineConstants>
+        <DefineConstants Condition="'$(FeatureComWrappers)' == 'true'">$(DefineConstants);FEATURE_COMWRAPPERS</DefineConstants>
         <DefineConstants Condition="'$(FeatureCominterop)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP</DefineConstants>
         <DefineConstants Condition="'$(FeatureCominteropApartmentSupport)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_APARTMENT_SUPPORT</DefineConstants>
         <DefineConstants Condition="'$(FeatureCominteropUnmanagedActivation)' == 'true'">$(DefineConstants);FEATURE_COMINTEROP_UNMANAGED_ACTIVATION</DefineConstants>
index 4747880..a58e3af 100644 (file)
@@ -98,6 +98,7 @@ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>>:F
 add_definitions(-DFEATURE_COLLECTIBLE_TYPES)
 
 if(CLR_CMAKE_TARGET_WIN32)
+    add_definitions(-DFEATURE_COMWRAPPERS)
     add_definitions(-DFEATURE_CLASSIC_COMINTEROP)
     add_definitions(-DFEATURE_COMINTEROP)
     add_definitions(-DFEATURE_COMINTEROP_APARTMENT_SUPPORT)
index 6a75325..b6584b2 100644 (file)
@@ -74,6 +74,7 @@ add_subdirectory(tools)
 add_subdirectory(unwinder)
 add_subdirectory(ildasm)
 add_subdirectory(ilasm)
+add_subdirectory(interop)
 
 if(CLR_CMAKE_HOST_UNIX)
     add_subdirectory(palrt)
index 881d460..7e215d1 100644 (file)
   <ItemGroup>
     <Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\XplatEventLogger.cs" Condition="'$(FeatureXplatEventSource)' == 'true'" />
   </ItemGroup>
+  <ItemGroup Condition="'$(FeatureComWrappers)' == 'true'">
+    <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ComWrappers.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(FeatureCominterop)' == 'true'">
     <Compile Include="$(CommonPath)System\Runtime\InteropServices\IDispatch.cs">
       <Link>Common\System\Runtime\InteropServices\IDispatch.cs</Link>
index 117e7e2..705e611 100644 (file)
@@ -264,6 +264,28 @@ namespace System.Runtime.CompilerServices
 
             return (MethodTable *)Unsafe.Add(ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData()), -1);
         }
+
+        /// <summary>
+        /// Allocate memory that is associated with the <paramref name="type"/> and
+        /// will be freed if and when the <see cref="System.Type"/> is unloaded.
+        /// </summary>
+        /// <param name="type">Type associated with the allocated memory.</param>
+        /// <param name="size">Amount of memory in bytes to allocate.</param>
+        /// <returns>The allocated memory</returns>
+        public static IntPtr AllocateTypeAssociatedMemory(Type type, int size)
+        {
+            RuntimeType? rt = type as RuntimeType;
+            if (rt == null)
+                throw new ArgumentException(SR.Arg_MustBeType, nameof(type));
+
+            if (size < 0)
+                throw new ArgumentOutOfRangeException(nameof(size));
+
+            return AllocateTypeAssociatedMemoryInternal(new QCallTypeHandle(ref rt), (uint)size);
+        }
+
+        [DllImport(RuntimeHelpers.QCall)]
+        private static extern IntPtr AllocateTypeAssociatedMemoryInternal(QCallTypeHandle type, uint size);
     }
 
     // Helper class to assist with unsafe pinning of arbitrary objects.
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs
new file mode 100644 (file)
index 0000000..f5d0e06
--- /dev/null
@@ -0,0 +1,252 @@
+// 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.
+
+using System;
+using System.Collections;
+using System.Threading;
+using System.Runtime.CompilerServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+    /// <summary>
+    /// Enumeration of flags for <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/>.
+    /// </summary>
+    [Flags]
+    public enum CreateComInterfaceFlags
+    {
+        None = 0,
+
+        /// <summary>
+        /// The caller will provide an IUnknown Vtable.
+        /// </summary>
+        /// <remarks>
+        /// This is useful in scenarios when the caller has no need to rely on an IUnknown instance
+        /// that is used when running managed code is not possible (i.e. during a GC). In traditional
+        /// COM scenarios this is common, but scenarios involving <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">Reference Tracker hosting</see>
+        /// calling of the IUnknown API during a GC is possible.
+        /// </remarks>
+        CallerDefinedIUnknown = 1,
+
+        /// <summary>
+        /// Flag used to indicate the COM interface should implement <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetrackertarget">IReferenceTrackerTarget</see>.
+        /// When this flag is passed, the resulting COM interface will have an internal implementation of IUnknown
+        /// and as such none should be supplied by the caller.
+        /// </summary>
+        TrackerSupport = 2,
+    }
+
+    /// <summary>
+    /// Enumeration of flags for <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/>.
+    /// </summary>
+    [Flags]
+    public enum CreateObjectFlags
+    {
+        None = 0,
+
+        /// <summary>
+        /// Indicate if the supplied external COM object implements the <see href="https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/nn-windows-ui-xaml-hosting-referencetracker-ireferencetracker">IReferenceTracker</see>.
+        /// </summary>
+        TrackerObject = 1,
+
+        /// <summary>
+        /// Ignore any internal caching and always create a unique instance.
+        /// </summary>
+        UniqueInstance = 2,
+    }
+
+    /// <summary>
+    /// Class for managing wrappers of COM IUnknown types.
+    /// </summary>
+    [CLSCompliant(false)]
+    public abstract partial class ComWrappers
+    {
+        /// <summary>
+        /// Interface type and pointer to targeted VTable.
+        /// </summary>
+        public struct ComInterfaceEntry
+        {
+            /// <summary>
+            /// Interface IID.
+            /// </summary>
+            public Guid IID;
+
+            /// <summary>
+            /// Memory must have the same lifetime as the memory returned from the call to <see cref="ComputeVtables(object, CreateComInterfaceFlags, out int)"/>.
+            /// </summary>
+            public IntPtr Vtable;
+        }
+
+        /// <summary>
+        /// ABI for function dispatch of a COM interface.
+        /// </summary>
+        public struct ComInterfaceDispatch
+        {
+            public IntPtr Vtable;
+
+            /// <summary>
+            /// Given a <see cref="System.IntPtr"/> from a generated Vtable, convert to the target type.
+            /// </summary>
+            /// <typeparam name="T">Desired type.</typeparam>
+            /// <param name="dispatchPtr">Pointer supplied to Vtable function entry.</param>
+            /// <returns>Instance of type associated with dispatched function call.</returns>
+            public static unsafe T GetInstance<T>(ComInterfaceDispatch* dispatchPtr) where T : class
+            {
+                // See the dispatch section in the runtime for details on the masking below.
+                const long DispatchThisPtrMask = ~0xfL;
+                var comInstance = *(ComInterfaceInstance**)(((long)dispatchPtr) & DispatchThisPtrMask);
+
+                return Unsafe.As<T>(GCHandle.InternalGet(comInstance->GcHandle));
+            }
+
+            private struct ComInterfaceInstance
+            {
+                public IntPtr GcHandle;
+            }
+        }
+
+        /// <summary>
+        /// Globally registered instance of the ComWrappers class.
+        /// </summary>
+        private static ComWrappers? s_globalInstance;
+
+        /// <summary>
+        /// Create a COM representation of the supplied object that can be passed to a non-managed environment.
+        /// </summary>
+        /// <param name="instance">The managed object to expose outside the .NET runtime.</param>
+        /// <param name="flags">Flags used to configure the generated interface.</param>
+        /// <returns>The generated COM interface that can be passed outside the .NET runtime.</returns>
+        public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags)
+        {
+            if (instance == null)
+                throw new ArgumentNullException(nameof(instance));
+
+            ComWrappers impl = this;
+            return GetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack.Create(ref impl), ObjectHandleOnStack.Create(ref instance), flags);
+        }
+
+        [DllImport(RuntimeHelpers.QCall)]
+        private static extern IntPtr GetOrCreateComInterfaceForObjectInternal(ObjectHandleOnStack comWrappersImpl, ObjectHandleOnStack instance, CreateComInterfaceFlags flags);
+
+        /// <summary>
+        /// Compute the desired Vtable for <paramref name="obj"/> respecting the values of <paramref name="flags"/>.
+        /// </summary>
+        /// <param name="obj">Target of the returned Vtables.</param>
+        /// <param name="flags">Flags used to compute Vtables.</param>
+        /// <param name="count">The number of elements contained in the returned memory.</param>
+        /// <returns><see cref="ComInterfaceEntry" /> pointer containing memory for all COM interface entries.</returns>
+        /// <remarks>
+        /// All memory returned from this function must either be unmanaged memory, pinned managed memory, or have been
+        /// allocated with the <see cref="System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(Type, int)"/> API.
+        ///
+        /// If the interface entries cannot be created and <code>null</code> is returned, the call to <see cref="ComWrappers.GetOrCreateComInterfaceForObject(object, CreateComInterfaceFlags)"/> will throw a <see cref="System.ArgumentNullException"/>.
+        /// </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)
+            => (comWrappersImpl ?? s_globalInstance!).ComputeVtables(obj, flags, out count);
+
+        /// <summary>
+        /// Get the currently registered managed object or creates a new managed object and registers it.
+        /// </summary>
+        /// <param name="externalComObject">Object to import for usage into the .NET runtime.</param>
+        /// <param name="flags">Flags used to describe the external object.</param>
+        /// <returns>Returns a managed object associated with the supplied external COM object.</returns>
+        public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags)
+        {
+            return GetOrCreateObjectForComInstanceInternal(externalComObject, flags, null);
+        }
+
+        /// <summary>
+        /// Create a managed object for the object pointed at by <paramref name="externalComObject"/> respecting the values of <paramref name="flags"/>.
+        /// </summary>
+        /// <param name="externalComObject">Object to import for usage into the .NET runtime.</param>
+        /// <param name="flags">Flags used to describe the external object.</param>
+        /// <returns>Returns a managed object associated with the supplied external COM object.</returns>
+        /// <remarks>
+        /// If the object cannot be created and <code>null</code> is returned, the call to <see cref="ComWrappers.GetOrCreateObjectForComInstance(IntPtr, CreateObjectFlags)"/> will throw a <see cref="System.ArgumentNullException"/>.
+        /// </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)
+            => (comWrappersImpl ?? s_globalInstance!).CreateObject(externalComObject, flags);
+
+        /// <summary>
+        /// Get the currently registered managed object or uses the supplied managed object and registers it.
+        /// </summary>
+        /// <param name="externalComObject">Object to import for usage into the .NET runtime.</param>
+        /// <param name="flags">Flags used to describe the external object.</param>
+        /// <param name="wrapper">The <see cref="object"/> to be used as the wrapper for the external object</param>
+        /// <returns>Returns a managed object associated with the supplied external COM object.</returns>
+        /// <remarks>
+        /// If the <paramref name="wrapper"/> instance already has an associated external object a <see cref="System.NotSupportedException"/> will be thrown.
+        /// </remarks>
+        public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper)
+        {
+            if (wrapper == null)
+                throw new ArgumentNullException(nameof(externalComObject));
+
+            return GetOrCreateObjectForComInstanceInternal(externalComObject, flags, wrapper);
+        }
+
+        private object GetOrCreateObjectForComInstanceInternal(IntPtr externalComObject, CreateObjectFlags flags, object? wrapperMaybe)
+        {
+            if (externalComObject == IntPtr.Zero)
+                throw new ArgumentNullException(nameof(externalComObject));
+
+            ComWrappers impl = this;
+            object? wrapperMaybeLocal = wrapperMaybe;
+            object? retValue = null;
+            GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack.Create(ref impl), externalComObject, flags, ObjectHandleOnStack.Create(ref wrapperMaybeLocal), ObjectHandleOnStack.Create(ref retValue));
+
+            return retValue!;
+        }
+
+        [DllImport(RuntimeHelpers.QCall)]
+        private static extern void GetOrCreateObjectForComInstanceInternal(ObjectHandleOnStack comWrappersImpl, IntPtr externalComObject, CreateObjectFlags flags, ObjectHandleOnStack wrapper, ObjectHandleOnStack retValue);
+
+        /// <summary>
+        /// Called when a request is made for a collection of objects to be released outside of normal object or COM interface lifetime.
+        /// </summary>
+        /// <param name="objects">Collection of objects to release.</param>
+        protected abstract void ReleaseObjects(IEnumerable objects);
+
+        // Call to execute the virtual instance function
+        internal static void CallReleaseObjects(ComWrappers? comWrappersImpl, IEnumerable objects)
+            => (comWrappersImpl ?? s_globalInstance!).ReleaseObjects(objects);
+
+        /// <summary>
+        /// Register this class's implementation to be used as the single global instance.
+        /// </summary>
+        /// <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:
+        ///  * Object tracking via the <see cref="CreateComInterfaceFlags.TrackerSupport" /> and <see cref="CreateObjectFlags.TrackerObject" /> flags.
+        ///  * Usage of COM related Marshal APIs.
+        /// </remarks>
+        public void RegisterAsGlobalInstance()
+        {
+            if (null != Interlocked.CompareExchange(ref s_globalInstance, this, null))
+            {
+                throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance);
+            }
+        }
+
+        /// <summary>
+        /// Get the runtime provided IUnknown implementation.
+        /// </summary>
+        /// <param name="fpQueryInterface">Function pointer to QueryInterface.</param>
+        /// <param name="fpAddRef">Function pointer to AddRef.</param>
+        /// <param name="fpRelease">Function pointer to Release.</param>
+        protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease)
+            => GetIUnknownImplInternal(out fpQueryInterface, out fpAddRef, out fpRelease);
+
+        [DllImport(RuntimeHelpers.QCall)]
+        private static extern void GetIUnknownImplInternal(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease);
+    }
+}
\ No newline at end of file
index 2fabad1..e86e91f 100644 (file)
@@ -104,7 +104,9 @@ set(CORECLR_LIBRARIES
     gcinfo # Condition="'$(TargetCpu)'=='amd64' or '$(TargetCpu)' == 'arm' or '$(TargetCpu)' == 'arm64'"
     ildbsymlib
     utilcode
+    v3binder
     libraries-native
+    interop
 )
 
 if(CLR_CMAKE_TARGET_WIN32)
index cf53f5b..5bbf1b2 100644 (file)
@@ -478,6 +478,9 @@ End
 Crst RCWCleanupList
 End
 
+Crst ExternalObjectContextCache
+End
+
 Crst ReDacl
 End
 
index 4bc0665..3638826 100644 (file)
@@ -63,113 +63,114 @@ enum CrstType
     CrstException = 44,
     CrstExecuteManLock = 45,
     CrstExecuteManRangeLock = 46,
-    CrstFCall = 47,
-    CrstFriendAccessCache = 48,
-    CrstFuncPtrStubs = 49,
-    CrstFusionAppCtx = 50,
-    CrstGCCover = 51,
-    CrstGCMemoryPressure = 52,
-    CrstGlobalStrLiteralMap = 53,
-    CrstHandleTable = 54,
-    CrstHostAssemblyMap = 55,
-    CrstHostAssemblyMapAdd = 56,
-    CrstIbcProfile = 57,
-    CrstIJWFixupData = 58,
-    CrstIJWHash = 59,
-    CrstILStubGen = 60,
-    CrstInlineTrackingMap = 61,
-    CrstInstMethodHashTable = 62,
-    CrstInterfaceVTableMap = 63,
-    CrstInterop = 64,
-    CrstInteropData = 65,
-    CrstIOThreadpoolWorker = 66,
-    CrstIsJMCMethod = 67,
-    CrstISymUnmanagedReader = 68,
-    CrstJit = 69,
-    CrstJitGenericHandleCache = 70,
-    CrstJitInlineTrackingMap = 71,
-    CrstJitPerf = 72,
-    CrstJumpStubCache = 73,
-    CrstLeafLock = 74,
-    CrstListLock = 75,
-    CrstLoaderAllocator = 76,
-    CrstLoaderAllocatorReferences = 77,
-    CrstLoaderHeap = 78,
-    CrstMda = 79,
-    CrstMetadataTracker = 80,
-    CrstMethodDescBackpatchInfoTracker = 81,
-    CrstModIntPairList = 82,
-    CrstModule = 83,
-    CrstModuleFixup = 84,
-    CrstModuleLookupTable = 85,
-    CrstMulticoreJitHash = 86,
-    CrstMulticoreJitManager = 87,
-    CrstMUThunkHash = 88,
-    CrstNativeBinderInit = 89,
-    CrstNativeImageCache = 90,
-    CrstNativeImageEagerFixups = 91,
-    CrstNls = 92,
-    CrstNotifyGdb = 93,
-    CrstObjectList = 94,
-    CrstOnEventManager = 95,
-    CrstPatchEntryPoint = 96,
-    CrstPEImage = 97,
-    CrstPEImagePDBStream = 98,
-    CrstPendingTypeLoadEntry = 99,
-    CrstPinHandle = 100,
-    CrstPinnedByrefValidation = 101,
-    CrstProfilerGCRefDataFreeList = 102,
-    CrstProfilingAPIStatus = 103,
-    CrstPublisherCertificate = 104,
-    CrstRCWCache = 105,
-    CrstRCWCleanupList = 106,
-    CrstRCWRefCache = 107,
-    CrstReadyToRunEntryPointToMethodDescMap = 108,
-    CrstReDacl = 109,
-    CrstReflection = 110,
-    CrstReJITGlobalRequest = 111,
-    CrstRemoting = 112,
-    CrstRetThunkCache = 113,
-    CrstRWLock = 114,
-    CrstSavedExceptionInfo = 115,
-    CrstSaveModuleProfileData = 116,
-    CrstSecurityStackwalkCache = 117,
-    CrstSharedAssemblyCreate = 118,
-    CrstSigConvert = 119,
-    CrstSingleUseLock = 120,
-    CrstSpecialStatics = 121,
-    CrstSqmManager = 122,
-    CrstStackSampler = 123,
-    CrstStressLog = 124,
-    CrstStrongName = 125,
-    CrstStubCache = 126,
-    CrstStubDispatchCache = 127,
-    CrstStubUnwindInfoHeapSegments = 128,
-    CrstSyncBlockCache = 129,
-    CrstSyncHashLock = 130,
-    CrstSystemBaseDomain = 131,
-    CrstSystemDomain = 132,
-    CrstSystemDomainDelayedUnloadList = 133,
-    CrstThreadIdDispenser = 134,
-    CrstThreadpoolEventCache = 135,
-    CrstThreadpoolTimerQueue = 136,
-    CrstThreadpoolWaitThreads = 137,
-    CrstThreadpoolWorker = 138,
-    CrstThreadStaticDataHashTable = 139,
-    CrstThreadStore = 140,
-    CrstTieredCompilation = 141,
-    CrstTPMethodTable = 142,
-    CrstTypeEquivalenceMap = 143,
-    CrstTypeIDMap = 144,
-    CrstUMEntryThunkCache = 145,
-    CrstUMThunkHash = 146,
-    CrstUniqueStack = 147,
-    CrstUnresolvedClassLock = 148,
-    CrstUnwindInfoTableLock = 149,
-    CrstVSDIndirectionCellLock = 150,
-    CrstWinRTFactoryCache = 151,
-    CrstWrapperTemplate = 152,
-    kNumberOfCrstTypes = 153
+    CrstExternalObjectContextCache = 47,
+    CrstFCall = 48,
+    CrstFriendAccessCache = 49,
+    CrstFuncPtrStubs = 50,
+    CrstFusionAppCtx = 51,
+    CrstGCCover = 52,
+    CrstGCMemoryPressure = 53,
+    CrstGlobalStrLiteralMap = 54,
+    CrstHandleTable = 55,
+    CrstHostAssemblyMap = 56,
+    CrstHostAssemblyMapAdd = 57,
+    CrstIbcProfile = 58,
+    CrstIJWFixupData = 59,
+    CrstIJWHash = 60,
+    CrstILStubGen = 61,
+    CrstInlineTrackingMap = 62,
+    CrstInstMethodHashTable = 63,
+    CrstInterfaceVTableMap = 64,
+    CrstInterop = 65,
+    CrstInteropData = 66,
+    CrstIOThreadpoolWorker = 67,
+    CrstIsJMCMethod = 68,
+    CrstISymUnmanagedReader = 69,
+    CrstJit = 70,
+    CrstJitGenericHandleCache = 71,
+    CrstJitInlineTrackingMap = 72,
+    CrstJitPerf = 73,
+    CrstJumpStubCache = 74,
+    CrstLeafLock = 75,
+    CrstListLock = 76,
+    CrstLoaderAllocator = 77,
+    CrstLoaderAllocatorReferences = 78,
+    CrstLoaderHeap = 79,
+    CrstMda = 80,
+    CrstMetadataTracker = 81,
+    CrstMethodDescBackpatchInfoTracker = 82,
+    CrstModIntPairList = 83,
+    CrstModule = 84,
+    CrstModuleFixup = 85,
+    CrstModuleLookupTable = 86,
+    CrstMulticoreJitHash = 87,
+    CrstMulticoreJitManager = 88,
+    CrstMUThunkHash = 89,
+    CrstNativeBinderInit = 90,
+    CrstNativeImageCache = 91,
+    CrstNativeImageEagerFixups = 92,
+    CrstNls = 93,
+    CrstNotifyGdb = 94,
+    CrstObjectList = 95,
+    CrstOnEventManager = 96,
+    CrstPatchEntryPoint = 97,
+    CrstPEImage = 98,
+    CrstPEImagePDBStream = 99,
+    CrstPendingTypeLoadEntry = 100,
+    CrstPinHandle = 101,
+    CrstPinnedByrefValidation = 102,
+    CrstProfilerGCRefDataFreeList = 103,
+    CrstProfilingAPIStatus = 104,
+    CrstPublisherCertificate = 105,
+    CrstRCWCache = 106,
+    CrstRCWCleanupList = 107,
+    CrstRCWRefCache = 108,
+    CrstReadyToRunEntryPointToMethodDescMap = 109,
+    CrstReDacl = 110,
+    CrstReflection = 111,
+    CrstReJITGlobalRequest = 112,
+    CrstRemoting = 113,
+    CrstRetThunkCache = 114,
+    CrstRWLock = 115,
+    CrstSavedExceptionInfo = 116,
+    CrstSaveModuleProfileData = 117,
+    CrstSecurityStackwalkCache = 118,
+    CrstSharedAssemblyCreate = 119,
+    CrstSigConvert = 120,
+    CrstSingleUseLock = 121,
+    CrstSpecialStatics = 122,
+    CrstSqmManager = 123,
+    CrstStackSampler = 124,
+    CrstStressLog = 125,
+    CrstStrongName = 126,
+    CrstStubCache = 127,
+    CrstStubDispatchCache = 128,
+    CrstStubUnwindInfoHeapSegments = 129,
+    CrstSyncBlockCache = 130,
+    CrstSyncHashLock = 131,
+    CrstSystemBaseDomain = 132,
+    CrstSystemDomain = 133,
+    CrstSystemDomainDelayedUnloadList = 134,
+    CrstThreadIdDispenser = 135,
+    CrstThreadpoolEventCache = 136,
+    CrstThreadpoolTimerQueue = 137,
+    CrstThreadpoolWaitThreads = 138,
+    CrstThreadpoolWorker = 139,
+    CrstThreadStaticDataHashTable = 140,
+    CrstThreadStore = 141,
+    CrstTieredCompilation = 142,
+    CrstTPMethodTable = 143,
+    CrstTypeEquivalenceMap = 144,
+    CrstTypeIDMap = 145,
+    CrstUMEntryThunkCache = 146,
+    CrstUMThunkHash = 147,
+    CrstUniqueStack = 148,
+    CrstUnresolvedClassLock = 149,
+    CrstUnwindInfoTableLock = 150,
+    CrstVSDIndirectionCellLock = 151,
+    CrstWinRTFactoryCache = 152,
+    CrstWrapperTemplate = 153,
+    kNumberOfCrstTypes = 154
 };
 
 #endif // __CRST_TYPES_INCLUDED
@@ -227,6 +228,7 @@ int g_rgCrstLevelMap[] =
     0,          // CrstException
     7,          // CrstExecuteManLock
     0,          // CrstExecuteManRangeLock
+    0,          // CrstExternalObjectContextCache
     3,          // CrstFCall
     7,          // CrstFriendAccessCache
     7,          // CrstFuncPtrStubs
@@ -385,6 +387,7 @@ LPCSTR g_rgCrstNameMap[] =
     "CrstException",
     "CrstExecuteManLock",
     "CrstExecuteManRangeLock",
+    "CrstExternalObjectContextCache",
     "CrstFCall",
     "CrstFriendAccessCache",
     "CrstFuncPtrStubs",
diff --git a/src/coreclr/src/interop/CMakeLists.txt b/src/coreclr/src/interop/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d7eaa1b
--- /dev/null
@@ -0,0 +1,36 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+include_directories(BEFORE inc)
+
+set(INTEROP_COMMON_SOURCES
+    interoplib.cpp
+)
+
+set(INTEROP_COMMON_HEADERS
+    inc/interoplibimports.h
+    inc/interoplib.h
+    platform.h
+)
+
+set(INTEROP_SOURCES
+    ${INTEROP_COMMON_SOURCES}
+)
+
+set(INTEROP_HEADERS
+    ${INTEROP_COMMON_HEADERS}
+)
+
+if (WIN32)
+    list(APPEND INTEROP_SOURCES
+        ${INTEROP_HEADERS}
+        comwrappers.cpp
+        comwrappers.hpp
+        trackerobjectmanager.cpp
+        referencetrackertypes.hpp)
+endif(WIN32)
+
+convert_to_absolute_path(INTEROP_SOURCES ${INTEROP_SOURCES})
+
+add_library_clr(interop
+    STATIC
+    ${INTEROP_SOURCES}
+)
diff --git a/src/coreclr/src/interop/comwrappers.cpp b/src/coreclr/src/interop/comwrappers.cpp
new file mode 100644 (file)
index 0000000..d7fa94b
--- /dev/null
@@ -0,0 +1,717 @@
+// 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.
+
+#include "comwrappers.hpp"
+#include <interoplibimports.h>
+
+#include <new> // placement new
+
+using OBJECTHANDLE = InteropLib::OBJECTHANDLE;
+using AllocScenario = InteropLibImports::AllocScenario;
+
+namespace ABI
+{
+    //---------------------------------------------------------------------------------
+    // Dispatch section of the ManagedObjectWrapper (MOW)
+    //
+    // Within the dispatch section, the ManagedObjectWrapper itself is inserted at a defined
+    // aligned location. This allows the simple masking of the any ComInterfaceDispatch* to get
+    // access to the ManagedObjectWrapper by masking the lower N bits. Below is a sketch of how
+    // the dispatch section would appear in a 32-bit process for a 16 bit alignment.
+    //
+    //           16 byte aligned                            Vtable
+    //           +-----------+
+    //           | MOW this  |
+    //           +-----------+                              +-----+
+    //  COM IP-->| VTable ptr|----------------------------->|slot1|
+    //           +-----------+           +-----+            +-----+
+    //           | VTable ptr|---------->|slot1|            |slot2|
+    //           +-----------+           +-----+            +     +
+    //           | VTable ptr|           | ....|            | ... |
+    //           +-----------+           +     +            +     +
+    //           | MOW this  |           |slotN|            |slotN|
+    //           +           +           +-----+            +-----+
+    //           |  ....     |
+    //           +-----------+
+    //
+    // A 16 byte alignment permits a ratio of 3:1 COM vtables to ManagedObjectWrapper 'this'
+    // pointers in a 32-bit process, but in a 64-bit process the mapping is only 1:1.
+    // See the dispatch section building API below for an example of how indexing works.
+    //--------------------------------------------------------------------------------
+
+    struct ComInterfaceDispatch
+    {
+        const void* vtable;
+    };
+    ABI_ASSERT(sizeof(ComInterfaceDispatch) == sizeof(void*));
+
+    const size_t DispatchAlignmentThisPtr = 16; // Should be a power of 2.
+    const intptr_t DispatchThisPtrMask = ~(DispatchAlignmentThisPtr - 1);
+    ABI_ASSERT(sizeof(void*) < DispatchAlignmentThisPtr);
+
+    const intptr_t AlignmentThisPtrMaxPadding = DispatchAlignmentThisPtr - sizeof(void*);
+    const size_t EntriesPerThisPtr = (DispatchAlignmentThisPtr / sizeof(void*)) - 1;
+
+    // Check if the instance can dispatch according to the ABI.
+    bool IsAbleToDispatch(_In_ ComInterfaceDispatch* disp)
+    {
+        return (reinterpret_cast<intptr_t>(disp) & DispatchThisPtrMask) != 0;
+    }
+
+    // Given the number of dispatch entries, compute the needed number of 'this' pointer entries.
+    constexpr size_t ComputeThisPtrForDispatchSection(_In_ size_t dispatchCount)
+    {
+        return (dispatchCount / ABI::EntriesPerThisPtr) + ((dispatchCount % ABI::EntriesPerThisPtr) == 0 ? 0 : 1);
+    }
+
+    // Given a pointer and a padding allowance, attempt to find an offset into
+    // the memory that is properly aligned for the dispatch section.
+    char* AlignDispatchSection(_In_ char* section, _In_ intptr_t extraPadding)
+    {
+        _ASSERTE(section != nullptr);
+
+        // If the dispatch section is not properly aligned by default, we
+        // utilize the padding to make sure the dispatch section is aligned.
+        while ((reinterpret_cast<intptr_t>(section) % ABI::DispatchAlignmentThisPtr) != 0)
+        {
+            // Check if there is padding to attempt an alignment.
+            if (extraPadding <= 0)
+                return nullptr;
+
+            extraPadding -= sizeof(void*);
+
+#ifdef _DEBUG
+            // Poison unused portions of the section.
+            ::memset(section, 0xff, sizeof(void*));
+#endif
+
+            section += sizeof(void*);
+        }
+
+        return section;
+    }
+
+    struct ComInterfaceEntry
+    {
+        GUID IID;
+        const void* Vtable;
+    };
+
+    struct EntrySet
+    {
+        const ComInterfaceEntry* start;
+        int32_t count;
+    };
+
+    // Populate the dispatch section with the entry sets
+    ComInterfaceDispatch* PopulateDispatchSection(
+        _In_ void* thisPtr,
+        _In_ void* dispatchSection,
+        _In_ size_t entrySetCount,
+        _In_ const EntrySet* entrySets)
+    {
+        // Define dispatch section iterator.
+        const void** currDisp = reinterpret_cast<const void**>(dispatchSection);
+
+        // Keep rolling count of dispatch entries.
+        int32_t dispCount = 0;
+
+        // Iterate over all interface entry sets.
+        const EntrySet* curr = entrySets;
+        const EntrySet* end = entrySets + entrySetCount;
+        for (; curr != end; ++curr)
+        {
+            const ComInterfaceEntry* currEntry = curr->start;
+            int32_t entryCount = curr->count;
+
+            // Update dispatch section with 'this' pointer and vtables.
+            for (int32_t i = 0; i < entryCount; ++i, ++dispCount, ++currEntry)
+            {
+                // Insert the 'this' pointer at the appropriate locations
+                // e.g.:
+                //       32-bit         |      64-bit
+                //   (0 * 4) % 16 =  0  |  (0 * 8) % 16 = 0
+                //   (1 * 4) % 16 =  4  |  (1 * 8) % 16 = 8
+                //   (2 * 4) % 16 =  8  |  (2 * 8) % 16 = 0
+                //   (3 * 4) % 16 = 12  |  (3 * 8) % 16 = 8
+                //   (4 * 4) % 16 =  0  |  (4 * 8) % 16 = 0
+                //   (5 * 4) % 16 =  4  |  (5 * 8) % 16 = 8
+                //
+                if (((dispCount * sizeof(void*)) % ABI::DispatchAlignmentThisPtr) == 0)
+                {
+                    *currDisp++ = thisPtr;
+                    ++dispCount;
+                }
+
+                // Fill in the dispatch entry
+                *currDisp++ = currEntry->Vtable;
+            }
+        }
+
+        return reinterpret_cast<ComInterfaceDispatch*>(dispatchSection);
+    }
+
+    // Given the entry index, compute the dispatch index.
+    ComInterfaceDispatch* IndexIntoDispatchSection(_In_ int32_t i, _In_ ComInterfaceDispatch* dispatches)
+    {
+        // Convert the supplied zero based index into what it represents as a count.
+        const size_t count = static_cast<size_t>(i) + 1;
+
+        // Based on the supplied count, compute how many previous 'this' pointers would be
+        // required in the dispatch section and add that to the supplied index to get the
+        // index into the dispatch section.
+        const size_t idx = ComputeThisPtrForDispatchSection(count) + i;
+
+        ComInterfaceDispatch* disp = dispatches + idx;
+        _ASSERTE(IsAbleToDispatch(disp));
+        return disp;
+    }
+
+    // Given a dispatcher instance, return the associated ManagedObjectWrapper.
+    ManagedObjectWrapper* ToManagedObjectWrapper(_In_ ComInterfaceDispatch* disp)
+    {
+        _ASSERTE(disp != nullptr && disp->vtable != nullptr);
+
+        intptr_t wrapperMaybe = reinterpret_cast<intptr_t>(disp) & DispatchThisPtrMask;
+        return *reinterpret_cast<ManagedObjectWrapper**>(wrapperMaybe);
+    }
+}
+
+namespace
+{
+    HRESULT STDMETHODCALLTYPE ManagedObjectWrapper_QueryInterface(
+        _In_ ABI::ComInterfaceDispatch* disp,
+        /* [in] */ REFIID riid,
+        /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+    {
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        return wrapper->QueryInterface(riid, ppvObject);
+    }
+
+    ULONG STDMETHODCALLTYPE ManagedObjectWrapper_AddRef(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        return wrapper->AddRef();
+    }
+
+    ULONG STDMETHODCALLTYPE ManagedObjectWrapper_Release(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        return wrapper->Release();
+    }
+
+    // Hard-coded ManagedObjectWrapper IUnknown vtable.
+    const struct
+    {
+        decltype(&ManagedObjectWrapper_QueryInterface) QueryInterface;
+        decltype(&ManagedObjectWrapper_AddRef) AddRef;
+        decltype(&ManagedObjectWrapper_Release) Release;
+    } ManagedObjectWrapper_IUnknownImpl {
+        &ManagedObjectWrapper_QueryInterface,
+        &ManagedObjectWrapper_AddRef,
+        &ManagedObjectWrapper_Release
+    };
+
+    static_assert(sizeof(ManagedObjectWrapper_IUnknownImpl) == (3 * sizeof(void*)), "Unexpected vtable size");
+}
+
+namespace
+{
+    const int32_t TrackerRefShift = 32;
+    const ULONGLONG TrackerRefCounter = ULONGLONG{ 1 } << TrackerRefShift;
+    const ULONGLONG ComRefCounter     = ULONGLONG{ 1 };
+    const ULONGLONG TrackerRefZero      = 0x0000000080000000;
+    const ULONGLONG TrackerRefCountMask = 0xffffffff00000000;
+    const ULONGLONG ComRefCountMask     = 0x000000007fffffff;
+    const ULONGLONG RefCountMask        = 0xffffffff7fffffff;
+
+    constexpr ULONG GetTrackerCount(_In_ ULONGLONG c)
+    {
+        return static_cast<ULONG>((c & TrackerRefCountMask) >> TrackerRefShift);
+    }
+
+    constexpr ULONG GetComCount(_In_ ULONGLONG c)
+    {
+        return static_cast<ULONG>(c & ComRefCountMask);
+    }
+
+    ULONG STDMETHODCALLTYPE TrackerTarget_AddRefFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        _ASSERTE(disp != nullptr && disp->vtable != nullptr);
+
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport));
+
+        return wrapper->AddRefFromReferenceTracker();
+    }
+
+    ULONG STDMETHODCALLTYPE TrackerTarget_ReleaseFromReferenceTracker(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        _ASSERTE(disp != nullptr && disp->vtable != nullptr);
+
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport));
+
+        return wrapper->ReleaseFromReferenceTracker();
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerTarget_Peg(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        _ASSERTE(disp != nullptr && disp->vtable != nullptr);
+
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport));
+
+        return wrapper->Peg();
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerTarget_Unpeg(_In_ ABI::ComInterfaceDispatch* disp)
+    {
+        _ASSERTE(disp != nullptr && disp->vtable != nullptr);
+
+        ManagedObjectWrapper* wrapper = ABI::ToManagedObjectWrapper(disp);
+        _ASSERTE(wrapper->IsSet(CreateComInterfaceFlagsEx::TrackerSupport));
+
+        return wrapper->Unpeg();
+    }
+
+    // Hard-coded IReferenceTrackerTarget vtable
+    const struct
+    {
+        decltype(ManagedObjectWrapper_IUnknownImpl) IUnknownImpl;
+        decltype(&TrackerTarget_AddRefFromReferenceTracker) AddRefFromReferenceTracker;
+        decltype(&TrackerTarget_ReleaseFromReferenceTracker) ReleaseFromReferenceTracker;
+        decltype(&TrackerTarget_Peg) Peg;
+        decltype(&TrackerTarget_Unpeg) Unpeg;
+    } ManagedObjectWrapper_IReferenceTrackerTargetImpl {
+        ManagedObjectWrapper_IUnknownImpl,
+        &TrackerTarget_AddRefFromReferenceTracker,
+        &TrackerTarget_ReleaseFromReferenceTracker,
+        &TrackerTarget_Peg,
+        &TrackerTarget_Unpeg
+    };
+
+    static_assert(sizeof(ManagedObjectWrapper_IReferenceTrackerTargetImpl) == (7 * sizeof(void*)), "Unexpected vtable size");
+}
+
+void ManagedObjectWrapper::GetIUnknownImpl(
+    _Out_ void** fpQueryInterface,
+    _Out_ void** fpAddRef,
+    _Out_ void** fpRelease)
+{
+    _ASSERTE(fpQueryInterface != nullptr
+            && fpAddRef != nullptr
+            && fpRelease != nullptr);
+
+    *fpQueryInterface = ManagedObjectWrapper_IUnknownImpl.QueryInterface;
+    *fpAddRef = ManagedObjectWrapper_IUnknownImpl.AddRef;
+    *fpRelease = ManagedObjectWrapper_IUnknownImpl.Release;
+}
+
+ManagedObjectWrapper* ManagedObjectWrapper::MapFromIUnknown(_In_ IUnknown* pUnk)
+{
+    _ASSERTE(pUnk != nullptr);
+
+    // If the first Vtable entry is part of the ManagedObjectWrapper IUnknown impl,
+    // we know how to interpret the IUnknown.
+    void** vtable = *reinterpret_cast<void***>(pUnk);
+    if (*vtable != ManagedObjectWrapper_IUnknownImpl.QueryInterface)
+        return nullptr;
+
+    ABI::ComInterfaceDispatch* disp = reinterpret_cast<ABI::ComInterfaceDispatch*>(pUnk);
+    return ABI::ToManagedObjectWrapper(disp);
+}
+
+HRESULT ManagedObjectWrapper::Create(
+    _In_ InteropLib::Com::CreateComInterfaceFlags flagsRaw,
+    _In_ OBJECTHANDLE objectHandle,
+    _In_ int32_t userDefinedCount,
+    _In_ ABI::ComInterfaceEntry* userDefined,
+    _Outptr_ ManagedObjectWrapper** mow)
+{
+    _ASSERTE(objectHandle != nullptr && mow != nullptr);
+
+    auto flags = static_cast<CreateComInterfaceFlagsEx>(flagsRaw);
+    _ASSERTE((flags & CreateComInterfaceFlagsEx::InternalMask) == CreateComInterfaceFlagsEx::None);
+
+    // Maximum number of runtime supplied vtables.
+    ABI::ComInterfaceEntry runtimeDefinedLocal[4];
+    int32_t runtimeDefinedCount = 0;
+
+    // Check if the caller will provide the IUnknown table.
+    if ((flags & CreateComInterfaceFlagsEx::CallerDefinedIUnknown) == CreateComInterfaceFlagsEx::None)
+    {
+        ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++];
+        curr.IID = __uuidof(IUnknown);
+        curr.Vtable = &ManagedObjectWrapper_IUnknownImpl;
+    }
+
+    // Check if the caller wants tracker support.
+    if ((flags & CreateComInterfaceFlagsEx::TrackerSupport) == CreateComInterfaceFlagsEx::TrackerSupport)
+    {
+        ABI::ComInterfaceEntry& curr = runtimeDefinedLocal[runtimeDefinedCount++];
+        curr.IID = __uuidof(IReferenceTrackerTarget);
+        curr.Vtable = &ManagedObjectWrapper_IReferenceTrackerTargetImpl;
+    }
+
+    _ASSERTE(runtimeDefinedCount <= ARRAYSIZE(runtimeDefinedLocal));
+
+    // Compute size for ManagedObjectWrapper instance.
+    const size_t totalRuntimeDefinedSize = runtimeDefinedCount * sizeof(ABI::ComInterfaceEntry);
+    const size_t totalDefinedCount = static_cast<size_t>(runtimeDefinedCount) + userDefinedCount;
+
+    // Compute the total entry size of dispatch section.
+    const size_t totalDispatchSectionCount = ABI::ComputeThisPtrForDispatchSection(totalDefinedCount) + totalDefinedCount;
+    const size_t totalDispatchSectionSize = totalDispatchSectionCount * sizeof(void*);
+
+    // Allocate memory for the ManagedObjectWrapper.
+    char* wrapperMem = (char*)InteropLibImports::MemAlloc(sizeof(ManagedObjectWrapper) + totalRuntimeDefinedSize + totalDispatchSectionSize + ABI::AlignmentThisPtrMaxPadding, AllocScenario::ManagedObjectWrapper);
+    if (wrapperMem == nullptr)
+        return E_OUTOFMEMORY;
+
+    // Compute Runtime defined offset.
+    char* runtimeDefinedOffset = wrapperMem + sizeof(ManagedObjectWrapper);
+
+    // Copy in runtime supplied COM interface entries.
+    ABI::ComInterfaceEntry* runtimeDefined = nullptr;
+    if (0 < runtimeDefinedCount)
+    {
+        ::memcpy(runtimeDefinedOffset, runtimeDefinedLocal, totalRuntimeDefinedSize);
+        runtimeDefined = reinterpret_cast<ABI::ComInterfaceEntry*>(runtimeDefinedOffset);
+    }
+
+    // Compute the dispatch section offset and ensure it is aligned.
+    char* dispatchSectionOffset = runtimeDefinedOffset + totalRuntimeDefinedSize;
+    dispatchSectionOffset = ABI::AlignDispatchSection(dispatchSectionOffset, ABI::AlignmentThisPtrMaxPadding);
+    if (dispatchSectionOffset == nullptr)
+        return E_UNEXPECTED;
+
+    // Define the sets for the tables to insert
+    const ABI::EntrySet AllEntries[] =
+    {
+        { runtimeDefined, runtimeDefinedCount },
+        { userDefined, userDefinedCount }
+    };
+
+    ABI::ComInterfaceDispatch* dispSection = ABI::PopulateDispatchSection(wrapperMem, dispatchSectionOffset, ARRAYSIZE(AllEntries), AllEntries);
+
+    ManagedObjectWrapper* wrapper = new (wrapperMem) ManagedObjectWrapper
+        {
+            flags,
+            objectHandle,
+            runtimeDefinedCount,
+            runtimeDefined,
+            userDefinedCount,
+            userDefined,
+            dispSection
+        };
+
+    *mow = wrapper;
+    return S_OK;
+}
+
+void ManagedObjectWrapper::Destroy(_In_ ManagedObjectWrapper* wrapper)
+{
+    _ASSERTE(wrapper != nullptr);
+
+    // Manually trigger the destructor since placement
+    // new was used to allocate the object.
+    wrapper->~ManagedObjectWrapper();
+    InteropLibImports::MemFree(wrapper, AllocScenario::ManagedObjectWrapper);
+}
+
+ManagedObjectWrapper::ManagedObjectWrapper(
+    _In_ CreateComInterfaceFlagsEx flags,
+    _In_ OBJECTHANDLE objectHandle,
+    _In_ int32_t runtimeDefinedCount,
+    _In_ const ABI::ComInterfaceEntry* runtimeDefined,
+    _In_ int32_t userDefinedCount,
+    _In_ const ABI::ComInterfaceEntry* userDefined,
+    _In_ ABI::ComInterfaceDispatch* dispatches)
+    : Target{ nullptr }
+    , _runtimeDefinedCount{ runtimeDefinedCount }
+    , _userDefinedCount{ userDefinedCount }
+    , _runtimeDefined{ runtimeDefined }
+    , _userDefined{ userDefined }
+    , _dispatches{ dispatches }
+    , _refCount{ 1 }
+    , _flags{ flags }
+{
+    bool wasSet = TrySetObjectHandle(objectHandle);
+    _ASSERTE(wasSet);
+}
+
+ManagedObjectWrapper::~ManagedObjectWrapper()
+{
+    // If the target isn't null, then a managed object
+    // is going to leak.
+    _ASSERTE(Target == nullptr);
+}
+
+ULONGLONG ManagedObjectWrapper::UniversalRelease(_In_ ULONGLONG dec)
+{
+    OBJECTHANDLE local = Target;
+
+    LONGLONG refCount;
+    if (dec == ComRefCounter)
+    {
+        _ASSERTE(dec == 1);
+        refCount = ::InterlockedDecrement64(&_refCount);
+    }
+    else
+    {
+        _ASSERTE(dec == TrackerRefCounter);
+        LONGLONG prev;
+        do
+        {
+            prev = _refCount;
+            refCount = prev - dec;
+        } while (::InterlockedCompareExchange64(&_refCount, refCount, prev) != prev);
+    }
+
+    // It is possible that a target wasn't set during an
+    // attempt to reactive the wrapper.
+    if ((RefCountMask & refCount) == 0 && local != nullptr)
+    {
+        _ASSERTE(!IsSet(CreateComInterfaceFlagsEx::IsPegged));
+        _ASSERTE(refCount == TrackerRefZero || refCount == 0);
+
+        // Attempt to reset the target if its current value is the same.
+        // It is possible the wrapper is in the middle of being reactivated.
+        (void)TrySetObjectHandle(nullptr, local);
+
+        // Tell the runtime to delete the managed object instance handle.
+        InteropLibImports::DeleteObjectInstanceHandle(local);
+    }
+
+    return refCount;
+}
+
+void* ManagedObjectWrapper::As(_In_ REFIID riid)
+{
+    // Find target interface and return dispatcher or null if not found.
+    for (int32_t i = 0; i < _runtimeDefinedCount; ++i)
+    {
+        if (IsEqualGUID(_runtimeDefined[i].IID, riid))
+        {
+            return ABI::IndexIntoDispatchSection(i, _dispatches);
+        }
+    }
+
+    for (int32_t i = 0; i < _userDefinedCount; ++i)
+    {
+        if (IsEqualGUID(_userDefined[i].IID, riid))
+        {
+            return ABI::IndexIntoDispatchSection(i + _runtimeDefinedCount, _dispatches);
+        }
+    }
+
+    return nullptr;
+}
+
+bool ManagedObjectWrapper::TrySetObjectHandle(_In_ OBJECTHANDLE objectHandle, _In_ OBJECTHANDLE current)
+{
+    return (::InterlockedCompareExchangePointer(&Target, objectHandle, current) == current);
+}
+
+bool ManagedObjectWrapper::IsSet(_In_ CreateComInterfaceFlagsEx flag) const
+{
+    return (_flags & flag) != CreateComInterfaceFlagsEx::None;
+}
+
+void ManagedObjectWrapper::SetFlag(_In_ CreateComInterfaceFlagsEx flag)
+{
+    LONG setMask = (LONG)flag;
+    ::InterlockedOr((LONG*)&_flags, setMask);
+}
+
+void ManagedObjectWrapper::ResetFlag(_In_ CreateComInterfaceFlagsEx flag)
+{
+    LONG resetMask = (LONG)~flag;
+    ::InterlockedAnd((LONG*)&_flags, resetMask);
+}
+
+ULONG ManagedObjectWrapper::IsActiveAddRef()
+{
+    ULONG count = GetComCount(::InterlockedIncrement64(&_refCount));
+    if (count == 1)
+    {
+        // Ensure the current target is null.
+        ::InterlockedExchangePointer(&Target, nullptr);
+    }
+
+    return count;
+}
+
+ULONG ManagedObjectWrapper::AddRefFromReferenceTracker()
+{
+    LONGLONG prev;
+    LONGLONG curr;
+    do
+    {
+        prev = _refCount;
+        curr = prev + TrackerRefCounter;
+    } while (::InterlockedCompareExchange64(&_refCount, curr, prev) != prev);
+
+    return GetTrackerCount(curr);
+}
+
+ULONG ManagedObjectWrapper::ReleaseFromReferenceTracker()
+{
+    return GetTrackerCount(UniversalRelease(TrackerRefCounter));
+}
+
+HRESULT ManagedObjectWrapper::Peg()
+{
+    SetFlag(CreateComInterfaceFlagsEx::IsPegged);
+    return S_OK;
+}
+
+HRESULT ManagedObjectWrapper::Unpeg()
+{
+    ResetFlag(CreateComInterfaceFlagsEx::IsPegged);
+    return S_OK;
+}
+
+HRESULT ManagedObjectWrapper::QueryInterface(
+    /* [in] */ REFIID riid,
+    /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+{
+    if (ppvObject == nullptr)
+        return E_POINTER;
+
+    // Find target interface
+    *ppvObject = As(riid);
+    if (*ppvObject == nullptr)
+        return E_NOINTERFACE;
+
+    (void)AddRef();
+    return S_OK;
+}
+
+ULONG ManagedObjectWrapper::AddRef(void)
+{
+    return GetComCount(::InterlockedIncrement64(&_refCount));
+}
+
+ULONG ManagedObjectWrapper::Release(void)
+{
+    return GetComCount(UniversalRelease(ComRefCounter));
+}
+
+namespace
+{
+    const size_t LiveContextSentinel = 0x0a110ced;
+    const size_t DeadContextSentinel = 0xdeaddead;
+}
+
+NativeObjectWrapperContext* NativeObjectWrapperContext::MapFromRuntimeContext(_In_ void* cxtMaybe)
+{
+    _ASSERTE(cxtMaybe != nullptr);
+
+    // Convert the supplied context
+    char* cxtRaw = reinterpret_cast<char*>(cxtMaybe);
+    cxtRaw -= sizeof(NativeObjectWrapperContext);
+    NativeObjectWrapperContext* cxt = reinterpret_cast<NativeObjectWrapperContext*>(cxtRaw);
+
+#ifdef _DEBUG
+    _ASSERTE(cxt->_sentinel == LiveContextSentinel);
+#endif
+
+    return cxt;
+}
+
+HRESULT NativeObjectWrapperContext::Create(
+    _In_ IUnknown* external,
+    _In_ InteropLib::Com::CreateObjectFlags flags,
+    _In_ size_t runtimeContextSize,
+    _Outptr_ NativeObjectWrapperContext** context)
+{
+    _ASSERTE(external != nullptr && context != nullptr);
+
+    HRESULT hr;
+
+    ComHolder<IReferenceTracker> trackerObject;
+    if (flags & InteropLib::Com::CreateObjectFlags_TrackerObject)
+    {
+        hr = external->QueryInterface(&trackerObject);
+        if (SUCCEEDED(hr))
+            RETURN_IF_FAILED(TrackerObjectManager::OnIReferenceTrackerFound(trackerObject));
+    }
+
+    // Allocate memory for the RCW
+    char* cxtMem = (char*)InteropLibImports::MemAlloc(sizeof(NativeObjectWrapperContext) + runtimeContextSize, AllocScenario::NativeObjectWrapper);
+    if (cxtMem == nullptr)
+        return E_OUTOFMEMORY;
+
+    void* runtimeContext = cxtMem + sizeof(NativeObjectWrapperContext);
+
+    // Contract specifically requires zeroing out runtime context.
+    ::memset(runtimeContext, 0, runtimeContextSize);
+
+    NativeObjectWrapperContext* contextLocal = new (cxtMem) NativeObjectWrapperContext{ runtimeContext, trackerObject };
+
+    if (trackerObject != nullptr)
+    {
+        // Inform the tracker object manager
+        _ASSERTE(flags & InteropLib::Com::CreateObjectFlags_TrackerObject);
+        hr = TrackerObjectManager::AfterWrapperCreated(trackerObject);
+        if (FAILED(hr))
+        {
+            Destroy(contextLocal);
+            return hr;
+        }
+    }
+
+    *context = contextLocal;
+    return S_OK;
+}
+
+void NativeObjectWrapperContext::Destroy(_In_ NativeObjectWrapperContext* wrapper)
+{
+    _ASSERTE(wrapper != nullptr);
+
+    // Manually trigger the destructor since placement
+    // new was used to allocate the object.
+    wrapper->~NativeObjectWrapperContext();
+    InteropLibImports::MemFree(wrapper, AllocScenario::NativeObjectWrapper);
+}
+
+NativeObjectWrapperContext::NativeObjectWrapperContext(_In_ void* runtimeContext, _In_opt_ IReferenceTracker* trackerObject)
+    : _trackerObject{ trackerObject }
+    , _runtimeContext{ runtimeContext }
+    , _isValidTracker{ (trackerObject != nullptr ? TRUE : FALSE) }
+#ifdef _DEBUG
+    , _sentinel{ LiveContextSentinel }
+#endif
+{
+    if (_isValidTracker == TRUE)
+        (void)_trackerObject->AddRef();
+}
+
+NativeObjectWrapperContext::~NativeObjectWrapperContext()
+{
+    DisconnectTracker();
+
+#ifdef _DEBUG
+    _sentinel = DeadContextSentinel;
+#endif
+}
+
+void* NativeObjectWrapperContext::GetRuntimeContext() const noexcept
+{
+    return _runtimeContext;
+}
+
+IReferenceTracker* NativeObjectWrapperContext::GetReferenceTracker() const noexcept
+{
+    return ((_isValidTracker == TRUE) ? _trackerObject : nullptr);
+}
+
+void NativeObjectWrapperContext::DisconnectTracker() noexcept
+{
+    // Attempt to disconnect from the tracker.
+    if (TRUE == ::InterlockedCompareExchange((LONG*)&_isValidTracker, FALSE, TRUE))
+        (void)_trackerObject->Release();
+}
diff --git a/src/coreclr/src/interop/comwrappers.hpp b/src/coreclr/src/interop/comwrappers.hpp
new file mode 100644 (file)
index 0000000..d233774
--- /dev/null
@@ -0,0 +1,249 @@
+// 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.
+
+#ifndef _INTEROP_COMWRAPPERS_H_
+#define _INTEROP_COMWRAPPERS_H_
+
+#include "platform.h"
+#include <interoplib.h>
+#include "referencetrackertypes.hpp"
+
+enum class CreateComInterfaceFlagsEx : int32_t
+{
+    None = InteropLib::Com::CreateComInterfaceFlags_None,
+    CallerDefinedIUnknown = InteropLib::Com::CreateComInterfaceFlags_CallerDefinedIUnknown,
+    TrackerSupport = InteropLib::Com::CreateComInterfaceFlags_TrackerSupport,
+
+    // Highest bit is reserved for internal usage
+    IsPegged = 1 << 31,
+
+    InternalMask = IsPegged,
+};
+
+DEFINE_ENUM_FLAG_OPERATORS(CreateComInterfaceFlagsEx);
+
+// Forward declarations
+namespace ABI
+{
+    struct ComInterfaceDispatch;
+    struct ComInterfaceEntry;
+}
+
+// Class for wrapping a managed object and projecting it in a non-managed environment
+class ManagedObjectWrapper
+{
+public:
+    Volatile<InteropLib::OBJECTHANDLE> Target;
+
+private:
+    const int32_t _runtimeDefinedCount;
+    const int32_t _userDefinedCount;
+    const ABI::ComInterfaceEntry* _runtimeDefined;
+    const ABI::ComInterfaceEntry* _userDefined;
+    ABI::ComInterfaceDispatch* _dispatches;
+
+    LONGLONG _refCount;
+    Volatile<CreateComInterfaceFlagsEx> _flags;
+
+public: // static
+    // Get the implementation for IUnknown.
+    static void GetIUnknownImpl(
+            _Out_ void** fpQueryInterface,
+            _Out_ void** fpAddRef,
+            _Out_ void** fpRelease);
+
+    // Convert the IUnknown if the instance is a ManagedObjectWrapper
+    // into a ManagedObjectWrapper, otherwise null.
+    static ManagedObjectWrapper* MapFromIUnknown(_In_ IUnknown* pUnk);
+
+    // Create a ManagedObjectWrapper instance
+    static HRESULT Create(
+        _In_ InteropLib::Com::CreateComInterfaceFlags flags,
+        _In_ InteropLib::OBJECTHANDLE objectHandle,
+        _In_ int32_t userDefinedCount,
+        _In_ ABI::ComInterfaceEntry* userDefined,
+        _Outptr_ ManagedObjectWrapper** mow);
+
+    // Destroy the instance
+    static void Destroy(_In_ ManagedObjectWrapper* wrapper);
+
+private:
+    ManagedObjectWrapper(
+        _In_ CreateComInterfaceFlagsEx flags,
+        _In_ InteropLib::OBJECTHANDLE objectHandle,
+        _In_ int32_t runtimeDefinedCount,
+        _In_ const ABI::ComInterfaceEntry* runtimeDefined,
+        _In_ int32_t userDefinedCount,
+        _In_ const ABI::ComInterfaceEntry* userDefined,
+        _In_ ABI::ComInterfaceDispatch* dispatches);
+
+    ~ManagedObjectWrapper();
+
+    // Represents a single implementation of how to release
+    // the wrapper. Supplied with a decrementing value.
+    ULONGLONG UniversalRelease(_In_ ULONGLONG dec);
+
+public:
+    void* As(_In_ REFIID riid);
+    // Attempt to set the target object handle based on an assumed current value.
+    bool TrySetObjectHandle(_In_ InteropLib::OBJECTHANDLE objectHandle, _In_ InteropLib::OBJECTHANDLE current = nullptr);
+    bool IsSet(_In_ CreateComInterfaceFlagsEx flag) const;
+    void SetFlag(_In_ CreateComInterfaceFlagsEx flag);
+    void ResetFlag(_In_ CreateComInterfaceFlagsEx flag);
+
+    // Used while validating wrapper is active.
+    ULONG IsActiveAddRef();
+
+public: // IReferenceTrackerTarget
+    ULONG AddRefFromReferenceTracker();
+    ULONG ReleaseFromReferenceTracker();
+    HRESULT Peg();
+    HRESULT Unpeg();
+
+public: // Lifetime
+    HRESULT QueryInterface(
+        /* [in] */ REFIID riid,
+        /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR * __RPC_FAR * ppvObject);
+    ULONG AddRef(void);
+    ULONG Release(void);
+};
+
+// ABI contract. This below offset is assumed in managed code.
+ABI_ASSERT(offsetof(ManagedObjectWrapper, Target) == 0);
+
+// Class for connecting a native COM object to a managed object instance
+class NativeObjectWrapperContext
+{
+#ifdef _DEBUG
+    size_t _sentinel;
+#endif
+
+    IReferenceTracker* _trackerObject;
+    void* _runtimeContext;
+    Volatile<BOOL> _isValidTracker;
+
+public: // static
+    // Convert a context pointer into a NativeObjectWrapperContext.
+    static NativeObjectWrapperContext* MapFromRuntimeContext(_In_ void* cxt);
+
+    // Create a NativeObjectWrapperContext instance
+    static HRESULT NativeObjectWrapperContext::Create(
+        _In_ IUnknown* external,
+        _In_ InteropLib::Com::CreateObjectFlags flags,
+        _In_ size_t runtimeContextSize,
+        _Outptr_ NativeObjectWrapperContext** context);
+
+    // Destroy the instance
+    static void Destroy(_In_ NativeObjectWrapperContext* wrapper);
+
+private:
+    NativeObjectWrapperContext(_In_ void* runtimeContext, _In_opt_ IReferenceTracker* trackerObject);
+    ~NativeObjectWrapperContext();
+
+public:
+    // Get the associated runtime context for this context.
+    void* GetRuntimeContext() const noexcept;
+
+    // Get the IReferenceTracker instance.
+    IReferenceTracker* GetReferenceTracker() const noexcept;
+
+    // Disconnect reference tracker instance.
+    void DisconnectTracker() noexcept;
+};
+
+// Manage native object wrappers that support IReferenceTracker.
+class TrackerObjectManager
+{
+public:
+    // Called when an IReferenceTracker instance is found.
+    static HRESULT OnIReferenceTrackerFound(_In_ IReferenceTracker* obj);
+
+    // Called after wrapper has been created.
+    static HRESULT AfterWrapperCreated(_In_ IReferenceTracker* obj);
+
+    // Called before wrapper is about to be destroyed (the same lifetime as short weak handle).
+    static HRESULT BeforeWrapperDestroyed(_In_ IReferenceTracker* obj);
+
+public:
+    // Begin the reference tracking process for external objects.
+    static HRESULT BeginReferenceTracking(InteropLibImports::RuntimeCallContext* cxt);
+
+    // End the reference tracking process for external object.
+    static HRESULT EndReferenceTracking();
+};
+
+// Class used to hold COM objects (i.e. IUnknown base class)
+// This class mimics the semantics of ATL::CComPtr<T> (https://docs.microsoft.com/cpp/atl/reference/ccomptr-class).
+template<typename T>
+struct ComHolder
+{
+    T* p;
+
+    ComHolder()
+        : p{ nullptr }
+    { }
+
+    ComHolder(_In_ const ComHolder&) = delete;
+    ComHolder& operator=(_In_ const ComHolder&) = delete;
+
+    ComHolder(_Inout_ ComHolder&& other)
+        : p{ other.Detach() }
+    { }
+
+    ComHolder& operator=(_Inout_ ComHolder&& other)
+    {
+        Attach(other.Detach());
+        return (*this);
+    }
+
+    ComHolder(_In_ T* i)
+        : p{ i }
+    {
+        _ASSERTE(p != nullptr);
+        (void)p->AddRef();
+    }
+
+    ~ComHolder()
+    {
+        Release();
+    }
+
+    T** operator&()
+    {
+        return &p;
+    }
+
+    T* operator->()
+    {
+        return p;
+    }
+
+    operator T*()
+    {
+        return p;
+    }
+
+    void Attach(_In_opt_ T* i) noexcept
+    {
+        Release();
+        p = i;
+    }
+
+    T* Detach() noexcept
+    {
+        T* tmp = p;
+        p = nullptr;
+        return tmp;
+    }
+
+    void Release() noexcept
+    {
+        if (p != nullptr)
+        {
+            (void)p->Release();
+            p = nullptr;
+        }
+    }
+};
+#endif // _INTEROP_COMWRAPPERS_H_
diff --git a/src/coreclr/src/interop/inc/interoplib.h b/src/coreclr/src/interop/inc/interoplib.h
new file mode 100644 (file)
index 0000000..df59b8b
--- /dev/null
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef _INTEROP_INC_INTEROPLIB_H_
+#define _INTEROP_INC_INTEROPLIB_H_
+
+namespace InteropLibImports
+{
+    // Forward declaration of Runtime calling context.
+    // This class is used by the consuming runtime to pass through details
+    // that may be required during a subsequent callback from the InteropLib.
+    // InteropLib never directly modifies or inspects supplied instances.
+    struct RuntimeCallContext;
+}
+
+namespace InteropLib
+{
+    using OBJECTHANDLE = void*;
+
+    namespace Com
+    {
+        // See CreateComInterfaceFlags in ComWrappers.cs
+        enum CreateComInterfaceFlags
+        {
+            CreateComInterfaceFlags_None = 0,
+            CreateComInterfaceFlags_CallerDefinedIUnknown = 1,
+            CreateComInterfaceFlags_TrackerSupport = 2,
+        };
+
+        // Create an IUnknown instance that represents the supplied managed object instance.
+        HRESULT CreateWrapperForObject(
+            _In_ OBJECTHANDLE instance,
+            _In_ INT32 vtableCount,
+            _In_ void* vtables,
+            _In_ enum CreateComInterfaceFlags flags,
+            _Outptr_ IUnknown** wrapper) noexcept;
+
+        // Destroy the supplied wrapper
+        void DestroyWrapperForObject(_In_ void* wrapper) noexcept;
+
+        // Check if a wrapper is active.
+        HRESULT IsActiveWrapper(_In_ IUnknown* wrapper) noexcept;
+
+        // Reactivate the supplied wrapper.
+        HRESULT ReactivateWrapper(_In_ IUnknown* wrapper, _In_ InteropLib::OBJECTHANDLE handle) noexcept;
+
+        struct ExternalWrapperResult
+        {
+            // The returned context memory is guaranteed to be initialized to zero.
+            void* Context;
+
+            // See https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/
+            // for details.
+            bool FromTrackerRuntime;
+        };
+
+        // See CreateObjectFlags in ComWrappers.cs
+        enum CreateObjectFlags
+        {
+            CreateObjectFlags_None = 0,
+            CreateObjectFlags_TrackerObject = 1,
+            CreateObjectFlags_UniqueInstance = 2,
+        };
+
+        // Allocate a wrapper context for an external object.
+        // The runtime supplies the external object, flags, and a memory
+        // request in order to bring the object into the runtime.
+        HRESULT CreateWrapperForExternal(
+            _In_ IUnknown* external,
+            _In_ enum CreateObjectFlags flags,
+            _In_ size_t contextSize,
+            _Out_ ExternalWrapperResult* result) noexcept;
+
+        // Destroy the supplied wrapper.
+        void DestroyWrapperForExternal(_In_ void* context) noexcept;
+
+        // Separate the supplied wrapper from the tracker runtime.
+        void SeparateWrapperFromTrackerRuntime(_In_ void* context) noexcept;
+
+        // Get internal interop IUnknown dispatch pointers.
+        void GetIUnknownImpl(
+            _Out_ void** fpQueryInterface,
+            _Out_ void** fpAddRef,
+            _Out_ void** fpRelease) noexcept;
+
+        // Begin the reference tracking process on external COM objects.
+        // This should only be called during a runtime's GC phase.
+        HRESULT BeginExternalObjectReferenceTracking(_In_ InteropLibImports::RuntimeCallContext* cxt) noexcept;
+
+        // End the reference tracking process.
+        // This should only be called during a runtime's GC phase.
+        HRESULT EndExternalObjectReferenceTracking() noexcept;
+    }
+}
+
+#endif // _INTEROP_INC_INTEROPLIB_H_
+
diff --git a/src/coreclr/src/interop/inc/interoplibimports.h b/src/coreclr/src/interop/inc/interoplibimports.h
new file mode 100644 (file)
index 0000000..3217b78
--- /dev/null
@@ -0,0 +1,78 @@
+// 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.
+
+#ifndef _INTEROP_INC_INTEROPLIBIMPORTS_H_
+#define _INTEROP_INC_INTEROPLIBIMPORTS_H_
+
+#include "interoplib.h"
+
+namespace InteropLibImports
+{
+    enum class AllocScenario
+    {
+        ManagedObjectWrapper,
+        NativeObjectWrapper,
+    };
+
+    // Allocate the given amount of memory.
+    void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) noexcept;
+
+    // Free the previously allocated memory.
+    void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept;
+
+    // Add memory pressure to the runtime's GC calculations.
+    HRESULT AddMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept;
+
+    // Remove memory pressure from the runtime's GC calculations.
+    HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept;
+
+    enum class GcRequest
+    {
+        Default,
+        FullBlocking // This is an expensive GC request, akin to a Gen2/"stop the world" GC.
+    };
+
+    // Request a GC from the runtime.
+    HRESULT RequestGarbageCollectionForExternal(_In_ GcRequest req) noexcept;
+
+    // Wait for the runtime's finalizer to clean up objects.
+    HRESULT WaitForRuntimeFinalizerForExternal() noexcept;
+
+    // Release objects associated with the current thread.
+    HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept;
+
+    // Delete Object instance handle.
+    void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept;
+
+    // Get the current global pegging state.
+    bool GetGlobalPeggingState() noexcept;
+
+    // Set the current global pegging state.
+    void SetGlobalPeggingState(_In_ bool state) noexcept;
+
+    // Get next External Object Context from the Runtime calling context.
+    // S_OK - Context is valid.
+    // S_FALSE - Iterator has reached end and context out parameter is set to NULL.
+    HRESULT IteratorNext(
+        _In_ RuntimeCallContext* runtimeContext,
+        _Outptr_result_maybenull_ void** extObjContext) noexcept;
+
+    // Tell the runtime a reference path between the External Object Context and
+    // OBJECTHANDLE was found.
+    HRESULT FoundReferencePath(
+        _In_ RuntimeCallContext* runtimeContext,
+        _In_ void* extObjContext,
+        _In_ InteropLib::OBJECTHANDLE handle) noexcept;
+
+    // Get or create an IReferenceTrackerTarget instance for the supplied
+    // external object.
+    HRESULT GetOrCreateTrackerTargetForExternal(
+        _In_ IUnknown* externalComObject,
+        _In_ InteropLib::Com::CreateObjectFlags externalObjectFlags,
+        _In_ InteropLib::Com::CreateComInterfaceFlags trackerTargetFlags,
+        _Outptr_ void** trackerTarget) noexcept;
+}
+
+#endif // _INTEROP_INC_INTEROPLIBIMPORTS_H_
+
diff --git a/src/coreclr/src/interop/interoplib.cpp b/src/coreclr/src/interop/interoplib.cpp
new file mode 100644 (file)
index 0000000..8283024
--- /dev/null
@@ -0,0 +1,161 @@
+// 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.
+
+#include "platform.h"
+#include <interoplib.h>
+#include <interoplibimports.h>
+
+#ifdef FEATURE_COMWRAPPERS
+#include "comwrappers.hpp"
+#endif // FEATURE_COMWRAPPERS
+
+using OBJECTHANDLE = InteropLib::OBJECTHANDLE;
+using RuntimeCallContext = InteropLibImports::RuntimeCallContext;
+
+namespace InteropLib
+{
+#ifdef FEATURE_COMWRAPPERS
+    // Exposed COM related API
+    namespace Com
+    {
+        HRESULT CreateWrapperForObject(
+            _In_ OBJECTHANDLE instance,
+            _In_ INT32 vtableCount,
+            _In_ void* vtablesRaw,
+            _In_ enum CreateComInterfaceFlags flags,
+            _Outptr_ IUnknown** wrapper) noexcept
+        {
+            _ASSERTE(instance != nullptr && wrapper != nullptr);
+
+            // Validate the supplied vtable data is valid with a
+            // reasonable count.
+            if ((vtablesRaw == nullptr && vtableCount != 0) || vtableCount < 0)
+                return E_INVALIDARG;
+
+            HRESULT hr;
+
+            // Convert input to appropriate types.
+            auto vtables = static_cast<ABI::ComInterfaceEntry*>(vtablesRaw);
+
+            ManagedObjectWrapper* mow;
+            RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow));
+
+            *wrapper = static_cast<IUnknown*>(mow->As(IID_IUnknown));
+            return S_OK;
+        }
+
+        void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept
+        {
+            ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(static_cast<IUnknown*>(wrapperMaybe));
+
+            // A caller should not be destroying a wrapper without knowing if the wrapper is valid.
+            _ASSERTE(wrapper != nullptr);
+
+            ManagedObjectWrapper::Destroy(wrapper);
+        }
+
+        HRESULT IsActiveWrapper(_In_ IUnknown* wrapperMaybe) noexcept
+        {
+            ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe);
+            if (wrapper == nullptr)
+                return E_INVALIDARG;
+
+            ULONG count = wrapper->IsActiveAddRef();
+            if (count == 1 || wrapper->Target == nullptr)
+            {
+                // The wrapper isn't active.
+                (void)wrapper->Release();
+                return S_FALSE;
+            }
+
+            return S_OK;
+        }
+
+        HRESULT ReactivateWrapper(_In_ IUnknown* wrapperMaybe, _In_ OBJECTHANDLE handle) noexcept
+        {
+            ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe);
+            if (wrapper == nullptr || handle == nullptr)
+                return E_INVALIDARG;
+
+            // Take an AddRef() as an indication of ownership.
+            (void)wrapper->AddRef();
+
+            // If setting this object handle fails, then the race
+            // was lost and we will cleanup the handle.
+            if (!wrapper->TrySetObjectHandle(handle))
+                InteropLibImports::DeleteObjectInstanceHandle(handle);
+
+            return S_OK;
+        }
+
+        HRESULT CreateWrapperForExternal(
+            _In_ IUnknown* external,
+            _In_ enum CreateObjectFlags flags,
+            _In_ size_t contextSize,
+            _Out_ ExternalWrapperResult* result) noexcept
+        {
+            _ASSERTE(external != nullptr && result != nullptr);
+
+            HRESULT hr;
+
+            NativeObjectWrapperContext* wrapperContext;
+            RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, flags, contextSize, &wrapperContext));
+
+            result->Context = wrapperContext->GetRuntimeContext();
+            result->FromTrackerRuntime = (wrapperContext->GetReferenceTracker() != nullptr);
+            return S_OK;
+        }
+
+        void DestroyWrapperForExternal(_In_ void* contextMaybe) noexcept
+        {
+            NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);
+
+            // A caller should not be destroying a context without knowing if the context is valid.
+            _ASSERTE(context != nullptr);
+
+            // Check if the tracker object manager should be informed prior to being destroyed.
+            IReferenceTracker* trackerMaybe = context->GetReferenceTracker();
+            if (trackerMaybe != nullptr)
+            {
+                // We only call this during a GC so ignore the failure as
+                // there is no way we can handle it at this point.
+                HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(trackerMaybe);
+                _ASSERTE(SUCCEEDED(hr));
+                (void)hr;
+            }
+
+            NativeObjectWrapperContext::Destroy(context);
+        }
+
+        void SeparateWrapperFromTrackerRuntime(_In_ void* contextMaybe) noexcept
+        {
+            NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);
+
+            // A caller should not be separating a context without knowing if the context is valid.
+            _ASSERTE(context != nullptr);
+
+            context->DisconnectTracker();
+        }
+
+        void GetIUnknownImpl(
+            _Out_ void** fpQueryInterface,
+            _Out_ void** fpAddRef,
+            _Out_ void** fpRelease) noexcept
+        {
+            ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease);
+        }
+
+        HRESULT BeginExternalObjectReferenceTracking(_In_ RuntimeCallContext* cxt) noexcept
+        {
+            return TrackerObjectManager::BeginReferenceTracking(cxt);
+        }
+
+        HRESULT EndExternalObjectReferenceTracking() noexcept
+        {
+            return TrackerObjectManager::EndReferenceTracking();
+        }
+    }
+
+#endif // FEATURE_COMWRAPPERS
+}
diff --git a/src/coreclr/src/interop/platform.h b/src/coreclr/src/interop/platform.h
new file mode 100644 (file)
index 0000000..b21ec99
--- /dev/null
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef _INTEROP_PLATFORM_H_
+#define _INTEROP_PLATFORM_H_
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef _ASSERTE
+#define _ASSERTE(x) assert((x))
+#endif
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <objidl.h> // COM interfaces
+
+// Common macro for working in COM
+#define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return hr; } }
+#define RETURN_VOID_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { _ASSERTE(false && #exp); return; } }
+
+#endif // _WIN32
+
+#define ABI_ASSERT(abi_definition) static_assert((abi_definition), "ABI is being invalidated.")
+
+// Runtime headers
+#include <volatile.h>
+
+#endif // _INTEROP_PLATFORM_H_
\ No newline at end of file
diff --git a/src/coreclr/src/interop/referencetrackertypes.hpp b/src/coreclr/src/interop/referencetrackertypes.hpp
new file mode 100644 (file)
index 0000000..b9802a4
--- /dev/null
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef _INTEROP_REFERENCETRACKERTYPES_H_
+#define _INTEROP_REFERENCETRACKERTYPES_H_
+
+#include <unknwn.h>
+
+// Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/
+
+class DECLSPEC_UUID("64bd43f8-bfee-4ec4-b7eb-2935158dae21") IReferenceTrackerTarget : public IUnknown
+{
+public:
+    STDMETHOD_(ULONG, AddRefFromReferenceTracker)() = 0;
+    STDMETHOD_(ULONG, ReleaseFromReferenceTracker)() = 0;
+    STDMETHOD(Peg)() = 0;
+    STDMETHOD(Unpeg)() = 0;
+};
+
+class DECLSPEC_UUID("29a71c6a-3c42-4416-a39d-e2825a07a773") IReferenceTrackerHost : public IUnknown
+{
+public:
+    STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags) = 0;
+    STDMETHOD(ReleaseDisconnectedReferenceSources)() = 0;
+    STDMETHOD(NotifyEndOfReferenceTrackingOnThread)() = 0;
+    STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) = 0;
+    STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated) = 0;
+    STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated) = 0;
+};
+
+class DECLSPEC_UUID("3cf184b4-7ccb-4dda-8455-7e6ce99a3298") IReferenceTrackerManager : public IUnknown
+{
+public:
+    STDMETHOD(ReferenceTrackingStarted)() = 0;
+    STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) = 0;
+    STDMETHOD(ReferenceTrackingCompleted)() = 0;
+    STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost *pCLRServices) = 0;
+};
+
+class DECLSPEC_UUID("04b3486c-4687-4229-8d14-505ab584dd88") IFindReferenceTargetsCallback : public IUnknown
+{
+public:
+    STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) = 0;
+};
+
+class DECLSPEC_UUID("11d3b13a-180e-4789-a8be-7712882893e6") IReferenceTracker : public IUnknown
+{
+public:
+    STDMETHOD(ConnectFromTrackerSource)() = 0;
+    STDMETHOD(DisconnectFromTrackerSource)() = 0;
+    STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback *pCallback) = 0;
+    STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager **ppTrackerManager) = 0;
+    STDMETHOD(AddRefFromTrackerSource)() = 0;
+    STDMETHOD(ReleaseFromTrackerSource)() = 0;
+    STDMETHOD(PegFromTrackerSource)() = 0;
+};
+
+#endif // _INTEROP_REFERENCETRACKERTYPES_H_
diff --git a/src/coreclr/src/interop/trackerobjectmanager.cpp b/src/coreclr/src/interop/trackerobjectmanager.cpp
new file mode 100644 (file)
index 0000000..7d1cb15
--- /dev/null
@@ -0,0 +1,372 @@
+// 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.
+
+#include "comwrappers.hpp"
+#include <interoplibimports.h>
+
+using OBJECTHANDLE = InteropLib::OBJECTHANDLE;
+using RuntimeCallContext = InteropLibImports::RuntimeCallContext;
+
+namespace
+{
+    const IID IID_IReferenceTrackerHost = __uuidof(IReferenceTrackerHost);
+    const IID IID_IReferenceTrackerTarget = __uuidof(IReferenceTrackerTarget);
+    const IID IID_IReferenceTracker = __uuidof(IReferenceTracker);
+    const IID IID_IReferenceTrackerManager = __uuidof(IReferenceTrackerManager);
+    const IID IID_IFindReferenceTargetsCallback = __uuidof(IFindReferenceTargetsCallback);
+
+    // In order to minimize the impact of a constructor running on module load,
+    // the HostServices class should have no instance fields.
+    class HostServices : public IReferenceTrackerHost
+    {
+    public: // IReferenceTrackerHost
+        STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags);
+        STDMETHOD(ReleaseDisconnectedReferenceSources)();
+        STDMETHOD(NotifyEndOfReferenceTrackingOnThread)();
+        STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference);
+        STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated);
+        STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated);
+
+    public: // IUnknown
+        // Lifetime maintained by stack - we don't care about ref counts
+        STDMETHOD_(ULONG, AddRef)() { return 1; }
+        STDMETHOD_(ULONG, Release)() { return 1; }
+
+        STDMETHOD(QueryInterface)(
+            /* [in] */ REFIID riid,
+            /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+        {
+            if (ppvObject == nullptr)
+                return E_POINTER;
+
+            if (IsEqualIID(riid, IID_IReferenceTrackerHost))
+            {
+                *ppvObject = static_cast<IReferenceTrackerHost*>(this);
+            }
+            else if (IsEqualIID(riid, IID_IUnknown))
+            {
+                *ppvObject = static_cast<IUnknown*>(this);
+            }
+            else
+            {
+                *ppvObject = nullptr;
+                return E_NOINTERFACE;
+            }
+
+            (void)AddRef();
+            return S_OK;
+        }
+    };
+
+    // Global instance of host services.
+    HostServices g_HostServicesInstance;
+
+    // Defined in windows.ui.xaml.hosting.referencetracker.h.
+    enum XAML_REFERENCETRACKER_DISCONNECT
+    {
+        // Indicates the disconnect is during a suspend and a GC can be trigger.
+        XAML_REFERENCETRACKER_DISCONNECT_SUSPEND = 0x00000001
+    };
+
+    STDMETHODIMP HostServices::DisconnectUnusedReferenceSources(_In_ DWORD flags)
+    {
+        InteropLibImports::GcRequest type = InteropLibImports::GcRequest::Default;
+
+        // Request a "stop the world" GC when a suspend is occurring.
+        if (flags & XAML_REFERENCETRACKER_DISCONNECT_SUSPEND)
+            type = InteropLibImports::GcRequest::FullBlocking;
+
+        return InteropLibImports::RequestGarbageCollectionForExternal(type);
+    }
+
+    STDMETHODIMP HostServices::ReleaseDisconnectedReferenceSources()
+    {
+        return InteropLibImports::WaitForRuntimeFinalizerForExternal();
+    }
+
+    STDMETHODIMP HostServices::NotifyEndOfReferenceTrackingOnThread()
+    {
+        return InteropLibImports::ReleaseExternalObjectsFromCurrentThread();
+    }
+
+    // Creates a proxy object (managed object wrapper) that points to the given IUnknown.
+    // The proxy represents the following:
+    //   1. Has a managed reference pointing to the external object
+    //      and therefore forms a cycle that can be resolved by GC.
+    //   2. Forwards data binding requests.
+    //
+    // For example:
+    //
+    // Grid <---- NoCW             Grid <-------- NoCW
+    // | ^                         |              ^
+    // | |             Becomes     |              |
+    // v |                         v              |
+    // Rectangle                  Rectangle ----->Proxy
+    //
+    // Arguments
+    //   obj        - An IUnknown* where a NoCW points to (Grid, in this case)
+    //                    Notes:
+    //                    1. We can either create a new NoCW or get back an old one from the cache.
+    //                    2. This obj could be a regular tracker runtime object for data binding.
+    //  ppNewReference  - The IReferenceTrackerTarget* for the proxy created
+    //                    The tracker runtime will call IReferenceTrackerTarget to establish a reference.
+    //
+    STDMETHODIMP HostServices::GetTrackerTarget(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference)
+    {
+        if (obj == nullptr || ppNewReference == nullptr)
+            return E_INVALIDARG;
+
+        HRESULT hr;
+
+        // QI for IUnknown to get the identity unknown
+        ComHolder<IUnknown> identity;
+        RETURN_IF_FAILED(obj->QueryInterface(&identity));
+
+        // Get or create an existing implementation for this external.
+        ComHolder<IUnknown> target;
+        RETURN_IF_FAILED(InteropLibImports::GetOrCreateTrackerTargetForExternal(
+            identity,
+            InteropLib::Com::CreateObjectFlags_TrackerObject,
+            InteropLib::Com::CreateComInterfaceFlags_TrackerSupport,
+            (void**)&target));
+
+        return target->QueryInterface(IID_IReferenceTrackerTarget, (void**)ppNewReference);
+    }
+
+    STDMETHODIMP HostServices::AddMemoryPressure(_In_ UINT64 bytesAllocated)
+    {
+        return InteropLibImports::AddMemoryPressureForExternal(bytesAllocated);
+    }
+
+    STDMETHODIMP HostServices::RemoveMemoryPressure(_In_ UINT64 bytesAllocated)
+    {
+        return InteropLibImports::RemoveMemoryPressureForExternal(bytesAllocated);
+    }
+
+    VolatilePtr<IReferenceTrackerManager> s_TrackerManager; // The one and only Tracker Manager instance
+    Volatile<BOOL> s_HasTrackingStarted = FALSE;
+
+    // Indicates if walking the external objects is needed.
+    // (i.e. Have any IReferenceTracker instances been found?)
+    bool ShouldWalkExternalObjects()
+    {
+        return (s_TrackerManager != nullptr);
+    }
+
+    // Callback implementation of IFindReferenceTargetsCallback
+    class FindDependentWrappersCallback : public IFindReferenceTargetsCallback
+    {
+        NativeObjectWrapperContext* _nowCxt;
+        RuntimeCallContext* _runtimeCallCxt;
+
+    public:
+        FindDependentWrappersCallback(_In_ NativeObjectWrapperContext* nowCxt, _In_ RuntimeCallContext* runtimeCallCxt)
+            : _nowCxt{ nowCxt }
+            , _runtimeCallCxt{ runtimeCallCxt }
+        {
+            _ASSERTE(_nowCxt != nullptr && runtimeCallCxt != nullptr);
+        }
+
+        STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target)
+        {
+            HRESULT hr;
+
+            if (target == nullptr)
+                return E_POINTER;
+
+            ManagedObjectWrapper* mow = ManagedObjectWrapper::MapFromIUnknown(target);
+
+            // Not a target we implemented.
+            if (mow == nullptr)
+                return S_OK;
+
+            // Notify the runtime a reference path was found.
+            RETURN_IF_FAILED(InteropLibImports::FoundReferencePath(
+                _runtimeCallCxt,
+                _nowCxt->GetRuntimeContext(),
+                mow->Target));
+
+            return S_OK;
+        }
+
+        // Lifetime maintained by stack - we don't care about ref counts
+        STDMETHOD_(ULONG, AddRef)() { return 1; }
+        STDMETHOD_(ULONG, Release)() { return 1; }
+
+        STDMETHOD(QueryInterface)(
+            /* [in] */ REFIID riid,
+            /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+        {
+            if (ppvObject == nullptr)
+                return E_POINTER;
+
+            if (IsEqualIID(riid, IID_IFindReferenceTargetsCallback))
+            {
+                *ppvObject = static_cast<IFindReferenceTargetsCallback*>(this);
+            }
+            else if (IsEqualIID(riid, IID_IUnknown))
+            {
+                *ppvObject = static_cast<IUnknown*>(this);
+            }
+            else
+            {
+                *ppvObject = nullptr;
+                return E_NOINTERFACE;
+            }
+
+            (void)AddRef();
+            return S_OK;
+        }
+    };
+
+    HRESULT WalkExternalTrackerObjects(_In_ RuntimeCallContext* cxt)
+    {
+        _ASSERTE(cxt != nullptr);
+
+        BOOL walkFailed = FALSE;
+        HRESULT hr;
+
+        void* extObjContext = nullptr;
+        while (S_OK == (hr = InteropLibImports::IteratorNext(cxt, &extObjContext)))
+        {
+            _ASSERTE(extObjContext != nullptr);
+
+            NativeObjectWrapperContext* nowc = NativeObjectWrapperContext::MapFromRuntimeContext(extObjContext);
+
+            // Check if the object is a tracker object.
+            IReferenceTracker* trackerMaybe = nowc->GetReferenceTracker();
+            if (trackerMaybe == nullptr)
+                continue;
+
+            // Ask the tracker instance to find all reference targets.
+            FindDependentWrappersCallback cb{ nowc, cxt };
+            hr = trackerMaybe->FindTrackerTargets(&cb);
+            if (FAILED(hr))
+                break;
+        }
+
+        if (FAILED(hr))
+        {
+            // Remember the fact that we've failed and stop walking
+            walkFailed = TRUE;
+            InteropLibImports::SetGlobalPeggingState(true);
+        }
+
+        _ASSERTE(s_TrackerManager != nullptr);
+        (void)s_TrackerManager->FindTrackerTargetsCompleted(walkFailed);
+
+        return hr;
+    }
+}
+
+HRESULT TrackerObjectManager::OnIReferenceTrackerFound(_In_ IReferenceTracker* obj)
+{
+    _ASSERTE(obj != nullptr);
+    if (s_TrackerManager != nullptr)
+        return S_OK;
+
+    // Retrieve IReferenceTrackerManager
+    HRESULT hr;
+    ComHolder<IReferenceTrackerManager> trackerManager;
+    RETURN_IF_FAILED(obj->GetReferenceTrackerManager(&trackerManager));
+
+    ComHolder<IReferenceTrackerHost> hostServices;
+    RETURN_IF_FAILED(g_HostServicesInstance.QueryInterface(IID_IReferenceTrackerHost, (void**)&hostServices));
+
+    // Attempt to set the tracker instance.
+    if (::InterlockedCompareExchangePointer((void**)&s_TrackerManager, trackerManager.p, nullptr) == nullptr)
+    {
+        (void)trackerManager.Detach(); // Ownership has been transfered
+        RETURN_IF_FAILED(s_TrackerManager->SetReferenceTrackerHost(hostServices));
+    }
+
+    return S_OK;
+}
+
+HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ IReferenceTracker* obj)
+{
+    _ASSERTE(obj != nullptr);
+
+    HRESULT hr;
+
+    // Notify tracker runtime that we've created a new wrapper for this object.
+    // To avoid surprises, we should notify them before we fire the first AddRefFromTrackerSource.
+    RETURN_IF_FAILED(obj->ConnectFromTrackerSource());
+
+    // Send out AddRefFromTrackerSource callbacks to notify tracker runtime we've done AddRef()
+    // for certain interfaces. We should do this *after* we made a AddRef() because we should never
+    // be in a state where report refs > actual refs
+    RETURN_IF_FAILED(obj->AddRefFromTrackerSource());
+
+    return S_OK;
+}
+
+HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ IReferenceTracker* obj)
+{
+    _ASSERTE(obj != nullptr);
+
+    HRESULT hr;
+
+    // Notify tracker runtime that we are about to destroy a wrapper
+    // (same timing as short weak handle) for this object.
+    // They need this information to disconnect weak refs and stop firing events,
+    // so that they can avoid resurrecting the object.
+    RETURN_IF_FAILED(obj->DisconnectFromTrackerSource());
+
+    return S_OK;
+}
+
+HRESULT TrackerObjectManager::BeginReferenceTracking(_In_ RuntimeCallContext* cxt)
+{
+    _ASSERTE(cxt != nullptr);
+
+    if (!ShouldWalkExternalObjects())
+        return S_FALSE;
+
+    HRESULT hr;
+
+    _ASSERTE(s_HasTrackingStarted == FALSE);
+    _ASSERTE(InteropLibImports::GetGlobalPeggingState());
+
+    s_HasTrackingStarted = TRUE;
+
+    // From this point, the tracker runtime decides whether a target
+    // should be pegged or not as the global pegging flag is now off.
+    InteropLibImports::SetGlobalPeggingState(false);
+
+    // Let the tracker runtime know we are about to walk external objects so that
+    // they can lock their reference cache. Note that the tracker runtime doesn't need to
+    // unpeg all external objects at this point and they can do the pegging/unpegging.
+    // in FindTrackerTargetsCompleted.
+    _ASSERTE(s_TrackerManager != nullptr);
+    RETURN_IF_FAILED(s_TrackerManager->ReferenceTrackingStarted());
+
+    // Time to walk the external objects
+    RETURN_IF_FAILED(WalkExternalTrackerObjects(cxt));
+
+    return S_OK;
+}
+
+HRESULT TrackerObjectManager::EndReferenceTracking()
+{
+    if (s_HasTrackingStarted != TRUE
+        || !ShouldWalkExternalObjects())
+        return S_FALSE;
+
+    HRESULT hr;
+
+    // Let the tracker runtime know the external object walk is done and they need to:
+    // 1. Unpeg all managed object wrappers (mow) if the (mow) needs to be unpegged
+    //       (i.e. when the (mow) is only reachable by other external tracker objects).
+    // 2. Peg all mows if the mow needs to be pegged (i.e. when the above condition is not true)
+    // 3. Unlock reference cache when they are done.
+    _ASSERTE(s_TrackerManager != nullptr);
+    hr = s_TrackerManager->ReferenceTrackingCompleted();
+    _ASSERTE(SUCCEEDED(hr));
+
+    InteropLibImports::SetGlobalPeggingState(true);
+    s_HasTrackingStarted = FALSE;
+
+    return hr;
+}
index 43b9bc3..30471d4 100644 (file)
@@ -3,6 +3,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 # Needed due to the cmunged files being in the binary folders, the set(CMAKE_INCLUDE_CURRENT_DIR ON) is not enough
 include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
 include_directories(${ARCH_SOURCES_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../interop/inc)
 
 add_definitions(-DUNICODE)
 add_definitions(-D_UNICODE)
@@ -593,6 +594,7 @@ list(APPEND VM_SOURCES_WKS
     dispparammarshaler.cpp
     dwreport.cpp
     eventreporter.cpp
+    interoplibinterface.cpp
     mngstdinterfaces.cpp
     notifyexternals.cpp
     olecontexthelpers.cpp
@@ -621,6 +623,7 @@ list(APPEND VM_HEADERS_WKS
     dispparammarshaler.h
     dwreport.h
     eventreporter.h
+    interoplibinterface.h
     mngstdinterfaces.h
     notifyexternals.h
     olecontexthelpers.h
index 2790349..23b31b6 100644 (file)
 #include "runtimecallablewrapper.h"
 #include "notifyexternals.h"
 #include "mngstdinterfaces.h"
-#include "rcwwalker.h"
+#include "interoplibinterface.h"
 #endif // FEATURE_COMINTEROP
 
 #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
index 5a20f27..1ae20b1 100644 (file)
@@ -921,6 +921,7 @@ FCFuncStart(gRuntimeHelpers)
     FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack)
     FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack)
     FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject)
+    QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory)
 FCFuncEnd()
 
 FCFuncStart(gContextSynchronizationFuncs)
@@ -992,6 +993,14 @@ FCFuncEnd()
 
 #endif // FEATURE_COMINTEROP
 
+#ifdef FEATURE_COMWRAPPERS
+FCFuncStart(gComWrappersFuncs)
+    QCFuncElement("GetIUnknownImplInternal", ComWrappersNative::GetIUnknownImpl)
+    QCFuncElement("GetOrCreateComInterfaceForObjectInternal", ComWrappersNative::GetOrCreateComInterfaceForObject)
+    QCFuncElement("GetOrCreateObjectForComInstanceInternal", ComWrappersNative::GetOrCreateObjectForComInstance)
+FCFuncEnd()
+#endif // FEATURE_COMWRAPPERS
+
 FCFuncStart(gMngdRefCustomMarshalerFuncs)
     FCFuncElement("CreateMarshaler", MngdRefCustomMarshaler::CreateMarshaler)
     FCFuncElement("ConvertContentsToNative", MngdRefCustomMarshaler::ConvertContentsToNative)
@@ -1209,6 +1218,9 @@ FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs)
 FCClassElement("Buffer", "System", gBufferFuncs)
 FCClassElement("CLRConfig", "System", gClrConfig)
 FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers)
+#ifdef FEATURE_COMINTEROP
+FCClassElement("ComWrappers", "System.Runtime.InteropServices", gComWrappersFuncs)
+#endif // FEATURE_COMINTEROP
 FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs)
 FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs)
 FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument)
index fe0fc30..8352bb6 100644 (file)
@@ -214,17 +214,7 @@ void GCToEEInterface::GcStartWork (int condemned, int max_gen)
 #endif
 
 #ifdef FEATURE_COMINTEROP
-    //
-    // Let GC detect managed/native cycles with input from jupiter
-    // Jupiter will
-    // 1. Report reference from RCW to CCW based on native reference in Jupiter
-    // 2. Identify the subset of CCWs that needs to be rooted
-    //
-    // We'll build the references from RCW to CCW using
-    // 1. Preallocated arrays
-    // 2. Dependent handles
-    //
-    RCWWalker::OnGCStarted(condemned);
+    Interop::OnGCStarted(condemned);
 #endif // FEATURE_COMINTEROP
 
     if (condemned == max_gen)
@@ -243,10 +233,7 @@ void GCToEEInterface::GcDone(int condemned)
     CONTRACTL_END;
 
 #ifdef FEATURE_COMINTEROP
-    //
-    // Tell Jupiter GC has finished
-    //
-    RCWWalker::OnGCFinished(condemned);
+    Interop::OnGCFinished(condemned);
 #endif // FEATURE_COMINTEROP
 }
 
index 85f6a69..5d6a58d 100644 (file)
@@ -10,7 +10,7 @@
 
 #ifdef FEATURE_COMINTEROP
 #include "runtimecallablewrapper.h"
-#include "rcwwalker.h"
+#include "interoplibinterface.h"
 #include "comcallablewrapper.h"
 #endif // FEATURE_COMINTEROP
 
index e04fd58..697fed5 100644 (file)
@@ -10,7 +10,7 @@
 
 #ifdef FEATURE_COMINTEROP
 #include "runtimecallablewrapper.h"
-#include "rcwwalker.h"
+#include "interoplibinterface.h"
 #include "comcallablewrapper.h"
 #endif // FEATURE_COMINTEROP
 
diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp
new file mode 100644 (file)
index 0000000..70e5f14
--- /dev/null
@@ -0,0 +1,1299 @@
+// 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.
+
+// Runtime headers
+#include "common.h"
+#include "rcwrefcache.h"
+#include "rcwwalker.h"
+#include "olecontexthelpers.h"
+#include "finalizerthread.h"
+
+// Interop library header
+#include <interoplibimports.h>
+
+#include "interoplibinterface.h"
+
+using CreateObjectFlags = InteropLib::Com::CreateObjectFlags;
+using CreateComInterfaceFlags = InteropLib::Com::CreateComInterfaceFlags;
+
+namespace
+{
+    // This class is used to track the external object within the runtime.
+    struct ExternalObjectContext
+    {
+        static const DWORD InvalidSyncBlockIndex;
+
+        void* Identity;
+        void* ThreadContext;
+        DWORD SyncBlockIndex;
+
+        enum
+        {
+            Flags_None = 0,
+            Flags_Collected = 1,
+            Flags_ReferenceTracker = 2,
+            Flags_InCache = 4,
+        };
+        DWORD Flags;
+
+        static void Construct(
+            _Out_ ExternalObjectContext* cxt,
+            _In_ IUnknown* identity,
+            _In_opt_ void* threadContext,
+            _In_ DWORD syncBlockIndex,
+            _In_ DWORD flags)
+        {
+            CONTRACTL
+            {
+                NOTHROW;
+                GC_NOTRIGGER;
+                MODE_ANY;
+                PRECONDITION(cxt != NULL);
+                PRECONDITION(threadContext != NULL);
+                PRECONDITION(syncBlockIndex != InvalidSyncBlockIndex);
+            }
+            CONTRACTL_END;
+
+            cxt->Identity = (void*)identity;
+            cxt->ThreadContext = threadContext;
+            cxt->SyncBlockIndex = syncBlockIndex;
+            cxt->Flags = flags;
+        }
+
+        bool IsSet(_In_ DWORD f) const
+        {
+            return ((Flags & f) == f);
+        }
+
+        bool IsActive() const
+        {
+            return !IsSet(Flags_Collected)
+                && (SyncBlockIndex != InvalidSyncBlockIndex);
+        }
+
+        void MarkCollected()
+        {
+            _ASSERTE(GCHeapUtilities::IsGCInProgress());
+            SyncBlockIndex = InvalidSyncBlockIndex;
+            Flags |= Flags_Collected;
+        }
+
+        OBJECTREF GetObjectRef()
+        {
+            CONTRACTL
+            {
+                NOTHROW;
+                GC_NOTRIGGER;
+                MODE_COOPERATIVE;
+            }
+            CONTRACTL_END;
+
+            _ASSERTE(IsActive());
+            return ObjectToOBJECTREF(g_pSyncTable[SyncBlockIndex].m_Object);
+        }
+    };
+
+    const DWORD ExternalObjectContext::InvalidSyncBlockIndex = 0; // See syncblk.h
+
+    static_assert((sizeof(ExternalObjectContext) % sizeof(void*)) == 0, "Keep context pointer size aligned");
+
+    // Holder for a External Wrapper Result
+    struct ExternalWrapperResultHolder
+    {
+        InteropLib::Com::ExternalWrapperResult Result;
+        ExternalWrapperResultHolder()
+            : Result{}
+        { }
+        ~ExternalWrapperResultHolder()
+        {
+            if (Result.Context != NULL)
+                InteropLib::Com::DestroyWrapperForExternal(Result.Context);
+        }
+        InteropLib::Com::ExternalWrapperResult* operator&()
+        {
+            return &Result;
+        }
+        ExternalObjectContext* GetContext()
+        {
+            return static_cast<ExternalObjectContext*>(Result.Context);
+        }
+        ExternalObjectContext* DetachContext()
+        {
+            ExternalObjectContext* t = GetContext();
+            Result.Context = NULL;
+            return t;
+        }
+    };
+
+    using ExtObjCxtRefCache = RCWRefCache;
+
+    class ExtObjCxtCache
+    {
+        static Volatile<ExtObjCxtCache*> g_Instance;
+
+    public: // static
+        static ExtObjCxtCache* GetInstanceNoThrow() noexcept
+        {
+            CONTRACT(ExtObjCxtCache*)
+            {
+                NOTHROW;
+                GC_NOTRIGGER;
+                POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+            }
+            CONTRACT_END;
+
+            RETURN g_Instance;
+        }
+
+        static ExtObjCxtCache* GetInstance()
+        {
+            CONTRACT(ExtObjCxtCache*)
+            {
+                THROWS;
+                GC_NOTRIGGER;
+                POSTCONDITION(RETVAL != NULL);
+            }
+            CONTRACT_END;
+
+            if (g_Instance.Load() == NULL)
+            {
+                ExtObjCxtCache* instMaybe = new ExtObjCxtCache();
+
+                // Attempt to set the global instance.
+                if (NULL != FastInterlockCompareExchangePointer(&g_Instance, instMaybe, NULL))
+                    delete instMaybe;
+            }
+
+            RETURN g_Instance;
+        }
+
+    public: // Inner class definitions
+        class Traits : public DefaultSHashTraits<ExternalObjectContext *>
+        {
+        public:
+            using key_t = void*;
+            static const key_t GetKey(_In_ element_t e) { LIMITED_METHOD_CONTRACT; return (key_t)e->Identity; }
+            static count_t Hash(_In_ key_t key) { LIMITED_METHOD_CONTRACT; return (count_t)key; }
+            static bool Equals(_In_ key_t lhs, _In_ key_t rhs) { LIMITED_METHOD_CONTRACT; return (lhs == rhs); }
+        };
+
+        // Alias some useful types
+        using Element = SHash<Traits>::element_t;
+        using Iterator = SHash<Traits>::Iterator;
+
+        class LockHolder : public CrstHolder
+        {
+        public:
+            LockHolder(_In_ ExtObjCxtCache *cache)
+                : CrstHolder(&cache->_lock)
+            {
+                // This cache must be locked in Cooperative mode
+                // since releases of wrappers can occur during a GC.
+                CONTRACTL
+                {
+                    NOTHROW;
+                    GC_NOTRIGGER;
+                    MODE_COOPERATIVE;
+                }
+                CONTRACTL_END;
+            }
+        };
+
+    private:
+        friend struct InteropLibImports::RuntimeCallContext;
+        SHash<Traits> _hashMap;
+        Crst _lock;
+        ExtObjCxtRefCache* _refCache;
+
+        ExtObjCxtCache()
+            : _lock(CrstExternalObjectContextCache, CRST_UNSAFE_COOPGC)
+            , _refCache(GetAppDomain()->GetRCWRefCache())
+        { }
+        ~ExtObjCxtCache() = default;
+
+    public:
+#if _DEBUG
+        bool IsLockHeld()
+        {
+            WRAPPER_NO_CONTRACT;
+            return (_lock.OwnedByCurrentThread() != FALSE);
+        }
+#endif // _DEBUG
+
+        // Get the associated reference cache with this external object cache.
+        ExtObjCxtRefCache* GetRefCache()
+        {
+            WRAPPER_NO_CONTRACT;
+            return _refCache;
+        }
+
+        // Create a managed IEnumerable instance for this collection.
+        // The collection should respect the supplied arguments.
+        //        withFlags - If Flag_None, then ignore. Otherwise objects must have these flags.
+        //    threadContext - The object must be associated with the supplied thread context.
+        //
+        // [TODO] Performance improvement should be made here to provide a custom IEnumerable
+        // instead of a managed array.
+        OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext)
+        {
+            CONTRACT(OBJECTREF)
+            {
+                THROWS;
+                GC_TRIGGERS;
+                MODE_COOPERATIVE;
+                PRECONDITION(!IsLockHeld());
+                POSTCONDITION(RETVAL != NULL);
+            }
+            CONTRACT_END;
+
+            DWORD objCount;
+            DWORD objCountMax;
+
+            struct
+            {
+                PTRARRAYREF arrRef;
+                PTRARRAYREF arrRefTmp;
+            } gc;
+            ::ZeroMemory(&gc, sizeof(gc));
+            GCPROTECT_BEGIN(gc);
+
+            {
+                LockHolder lock(this);
+                objCountMax = _hashMap.GetCount();
+            }
+
+            // Allocate the max number of objects needed.
+            gc.arrRef = (PTRARRAYREF)AllocateObjectArray(objCountMax, g_pObjectClass);
+
+            // Populate the array
+            {
+                LockHolder lock(this);
+                Iterator curr = _hashMap.Begin();
+                Iterator end = _hashMap.End();
+
+                ExternalObjectContext* inst;
+                for (objCount = 0; curr != end && objCount < objCountMax; objCount++, curr++)
+                {
+                    inst = *curr;
+
+                    // Only add objects that are in the correct thread
+                    // context and have the appropriate flags set.
+                    if (inst->ThreadContext == threadContext
+                        && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet(withFlags)))
+                    {
+                        // Separate the wrapper from the tracker runtime prior to
+                        // passing this onto the caller. This call is okay to make
+                        // even if the instance isn't from the tracker runtime.
+                        InteropLib::Com::SeparateWrapperFromTrackerRuntime(inst);
+                        gc.arrRef->SetAt(objCount, inst->GetObjectRef());
+                        STRESS_LOG1(LF_INTEROP, LL_INFO100, "Add EOC to Enumerable: 0x%p\n", inst);
+                    }
+                }
+            }
+
+            // Make the array the correct size
+            if (objCount < objCountMax)
+            {
+                gc.arrRefTmp = (PTRARRAYREF)AllocateObjectArray(objCount, g_pObjectClass);
+
+                SIZE_T elementSize = gc.arrRef->GetComponentSize();
+
+                void *src = gc.arrRef->GetDataPtr();
+                void *dest = gc.arrRefTmp->GetDataPtr();
+
+                _ASSERTE(sizeof(Object*) == elementSize && "Assumption invalidated in memmoveGCRefs() usage");
+                memmoveGCRefs(dest, src, objCount * elementSize);
+                gc.arrRef = gc.arrRefTmp;
+            }
+
+            GCPROTECT_END();
+
+            RETURN gc.arrRef;
+        }
+
+        ExternalObjectContext* Find(_In_ IUnknown* instance)
+        {
+            CONTRACT(ExternalObjectContext*)
+            {
+                NOTHROW;
+                GC_NOTRIGGER;
+                MODE_COOPERATIVE;
+                PRECONDITION(IsLockHeld());
+                PRECONDITION(instance != NULL);
+                POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+            }
+            CONTRACT_END;
+
+            // Forbid the GC from messing with the hash table.
+            GCX_FORBID();
+
+            RETURN _hashMap.Lookup(instance);
+        }
+
+        ExternalObjectContext* Add(_In_ ExternalObjectContext* cxt)
+        {
+            CONTRACT(ExternalObjectContext*)
+            {
+                THROWS;
+                GC_NOTRIGGER;
+                MODE_COOPERATIVE;
+                PRECONDITION(IsLockHeld());
+                PRECONDITION(!Traits::IsNull(cxt) && !Traits::IsDeleted(cxt));
+                PRECONDITION(cxt->Identity != NULL);
+                PRECONDITION(Find(static_cast<IUnknown*>(cxt->Identity)) == NULL);
+                POSTCONDITION(RETVAL == cxt);
+            }
+            CONTRACT_END;
+
+            _hashMap.Add(cxt);
+            RETURN cxt;
+        }
+
+        ExternalObjectContext* FindOrAdd(_In_ IUnknown* key, _In_ ExternalObjectContext* newCxt)
+        {
+            CONTRACT(ExternalObjectContext*)
+            {
+                THROWS;
+                GC_NOTRIGGER;
+                MODE_COOPERATIVE;
+                PRECONDITION(IsLockHeld());
+                PRECONDITION(key != NULL);
+                PRECONDITION(!Traits::IsNull(newCxt) && !Traits::IsDeleted(newCxt));
+                PRECONDITION(key == newCxt->Identity);
+                POSTCONDITION(CheckPointer(RETVAL));
+            }
+            CONTRACT_END;
+
+            // Forbid the GC from messing with the hash table.
+            GCX_FORBID();
+
+            ExternalObjectContext* cxt = Find(key);
+            if (cxt == NULL)
+                cxt = Add(newCxt);
+
+            RETURN cxt;
+        }
+
+        void Remove(_In_ ExternalObjectContext* cxt)
+        {
+            CONTRACTL
+            {
+                NOTHROW;
+                GC_NOTRIGGER;
+                MODE_ANY;
+                PRECONDITION(!Traits::IsNull(cxt) && !Traits::IsDeleted(cxt));
+                PRECONDITION(cxt->Identity != NULL);
+
+                // The GC thread doesn't have to take the lock
+                // since all other threads access in cooperative mode
+                PRECONDITION(
+                    (IsLockHeld() && GetThread()->PreemptiveGCDisabled())
+                    || Debug_IsLockedViaThreadSuspension());
+            }
+            CONTRACTL_END;
+
+            _hashMap.Remove(cxt->Identity);
+        }
+    };
+
+    // Global instance
+    Volatile<ExtObjCxtCache*> ExtObjCxtCache::g_Instance;
+
+    // Defined handle types for the specific object uses.
+    const HandleType InstanceHandleType{ HNDTYPE_STRONG };
+
+    void* CallComputeVTables(
+        _In_ OBJECTREF* implPROTECTED,
+        _In_ OBJECTREF* instancePROTECTED,
+        _In_ INT32 flags,
+        _Out_ DWORD* vtableCount)
+    {
+        CONTRACTL
+        {
+            THROWS;
+            MODE_COOPERATIVE;
+            PRECONDITION(implPROTECTED != NULL);
+            PRECONDITION(instancePROTECTED != NULL);
+            PRECONDITION(vtableCount != NULL);
+        }
+        CONTRACTL_END;
+
+        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);
+        CALL_MANAGED_METHOD(vtables, void*, args);
+
+        return vtables;
+    }
+
+    OBJECTREF CallGetObject(
+        _In_ OBJECTREF* implPROTECTED,
+        _In_ IUnknown* externalComObject,
+        _In_ INT32 flags)
+    {
+        CONTRACTL
+        {
+            THROWS;
+            MODE_COOPERATIVE;
+            PRECONDITION(implPROTECTED != NULL);
+            PRECONDITION(externalComObject != NULL);
+        }
+        CONTRACTL_END;
+
+        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);
+        CALL_MANAGED_METHOD(retObjRef, OBJECTREF, args);
+
+        return retObjRef;
+    }
+
+    void CallReleaseObjects(
+        _In_ OBJECTREF* implPROTECTED,
+        _In_ OBJECTREF* objsEnumPROTECTED)
+    {
+        CONTRACTL
+        {
+            THROWS;
+            MODE_COOPERATIVE;
+            PRECONDITION(implPROTECTED != NULL);
+            PRECONDITION(objsEnumPROTECTED != NULL);
+        }
+        CONTRACTL_END;
+
+        PREPARE_NONVIRTUAL_CALLSITE(METHOD__COMWRAPPERS__RELEASE_OBJECTS);
+        DECLARE_ARGHOLDER_ARRAY(args, 2);
+        args[ARGNUM_0]  = OBJECTREF_TO_ARGHOLDER(*implPROTECTED);
+        args[ARGNUM_1]  = OBJECTREF_TO_ARGHOLDER(*objsEnumPROTECTED);
+        CALL_MANAGED_METHOD_NORET(args);
+    }
+
+    void* GetOrCreateComInterfaceForObjectInternal(
+        _In_opt_ OBJECTREF impl,
+        _In_ OBJECTREF instance,
+        _In_ CreateComInterfaceFlags flags)
+    {
+        CONTRACT(void*)
+        {
+            THROWS;
+            MODE_COOPERATIVE;
+            PRECONDITION(instance != NULL);
+            POSTCONDITION(CheckPointer(RETVAL));
+        }
+        CONTRACT_END;
+
+        HRESULT hr;
+
+        SafeComHolder<IUnknown> newWrapper;
+        void* wrapperRaw = NULL;
+
+        struct
+        {
+            OBJECTREF implRef;
+            OBJECTREF instRef;
+        } gc;
+        ::ZeroMemory(&gc, sizeof(gc));
+        GCPROTECT_BEGIN(gc);
+
+        gc.implRef = impl;
+        gc.instRef = instance;
+
+        // Check the object's SyncBlock for a managed object wrapper.
+        SyncBlock* syncBlock = gc.instRef->GetSyncBlock();
+        InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo();
+        _ASSERTE(syncBlock->IsPrecious());
+
+        // Query the associated InteropSyncBlockInfo for an existing managed object wrapper.
+        if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw))
+        {
+            // Compute VTables for the new existing COM object using the supplied COM Wrappers implementation.
+            //
+            // N.B. Calling to compute the associated VTables is perhaps early since no lock
+            // 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);
+
+            // Re-query the associated InteropSyncBlockInfo for an existing managed object wrapper.
+            if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw))
+            {
+                OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType);
+
+                // Call the InteropLib and create the associated managed object wrapper.
+                hr = InteropLib::Com::CreateWrapperForObject(
+                    instHandle,
+                    vtableCount,
+                    vtables,
+                    flags,
+                    &newWrapper);
+                if (FAILED(hr))
+                {
+                    DestroyHandleCommon(instHandle, InstanceHandleType);
+                    COMPlusThrowHR(hr);
+                }
+                _ASSERTE(!newWrapper.IsNull());
+
+                // Try setting the newly created managed object wrapper on the InteropSyncBlockInfo.
+                if (!interopInfo->TrySetManagedObjectComWrapper(newWrapper))
+                {
+                    // The new wrapper couldn't be set which means a wrapper already exists.
+                    newWrapper.Release();
+
+                    // If the managed object wrapper couldn't be set, then
+                    // it should be possible to get the current one.
+                    if (!interopInfo->TryGetManagedObjectComWrapper(&wrapperRaw))
+                    {
+                        UNREACHABLE();
+                    }
+                }
+            }
+        }
+
+        // Determine what to return.
+        if (!newWrapper.IsNull())
+        {
+            // A new managed object wrapper was created, remove the object from the holder.
+            // No AddRef() here since the wrapper should be created with a reference.
+            wrapperRaw = newWrapper.Extract();
+            STRESS_LOG1(LF_INTEROP, LL_INFO100, "Created MOW: 0x%p\n", wrapperRaw);
+        }
+        else
+        {
+            _ASSERTE(wrapperRaw != NULL);
+
+            // It is possible the supplied wrapper is no longer valid. If so, reactivate the
+            // wrapper using the protected OBJECTREF.
+            IUnknown* wrapper = static_cast<IUnknown*>(wrapperRaw);
+            hr = InteropLib::Com::IsActiveWrapper(wrapper);
+            if (hr == S_FALSE)
+            {
+                STRESS_LOG1(LF_INTEROP, LL_INFO100, "Reactivating MOW: 0x%p\n", wrapperRaw);
+                OBJECTHANDLE h = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType);
+                hr = InteropLib::Com::ReactivateWrapper(wrapper, static_cast<InteropLib::OBJECTHANDLE>(h));
+            }
+
+            if (FAILED(hr))
+                COMPlusThrowHR(hr);
+        }
+
+        GCPROTECT_END();
+
+        RETURN wrapperRaw;
+    }
+
+    OBJECTREF GetOrCreateObjectForComInstanceInternal(
+        _In_opt_ OBJECTREF impl,
+        _In_ IUnknown* identity,
+        _In_ CreateObjectFlags flags,
+        _In_opt_ OBJECTREF wrapperMaybe)
+    {
+        CONTRACT(OBJECTREF)
+        {
+            THROWS;
+            MODE_COOPERATIVE;
+            PRECONDITION(identity != NULL);
+            POSTCONDITION(RETVAL != NULL);
+        }
+        CONTRACT_END;
+
+        HRESULT hr;
+        ExternalObjectContext* extObjCxt = NULL;
+
+        struct
+        {
+            OBJECTREF implRef;
+            OBJECTREF wrapperMaybeRef;
+            OBJECTREF objRef;
+        } gc;
+        ::ZeroMemory(&gc, sizeof(gc));
+        GCPROTECT_BEGIN(gc);
+
+        gc.implRef = impl;
+        gc.wrapperMaybeRef = wrapperMaybe;
+
+        ExtObjCxtCache* cache = ExtObjCxtCache::GetInstance();
+
+        // Check if the user requested a unique instance.
+        bool uniqueInstance = !!(flags & CreateObjectFlags::CreateObjectFlags_UniqueInstance);
+        if (!uniqueInstance)
+        {
+            // Query the external object cache
+            ExtObjCxtCache::LockHolder lock(cache);
+            extObjCxt = cache->Find(identity);
+        }
+
+        if (extObjCxt != NULL)
+        {
+            gc.objRef = extObjCxt->GetObjectRef();
+        }
+        else
+        {
+            // Create context instance for the possibly new external object.
+            ExternalWrapperResultHolder resultHolder;
+            hr = InteropLib::Com::CreateWrapperForExternal(
+                identity,
+                flags,
+                sizeof(ExternalObjectContext),
+                &resultHolder);
+            if (FAILED(hr))
+                COMPlusThrowHR(hr);
+
+            // The user could have supplied a wrapper so assign that now.
+            gc.objRef = gc.wrapperMaybeRef;
+
+            // If the wrapper hasn't been set yet, call the implementation to create one.
+            if (gc.objRef == NULL)
+            {
+                gc.objRef = CallGetObject(&gc.implRef, identity, flags);
+                if (gc.objRef == NULL)
+                    COMPlusThrow(kArgumentNullException);
+            }
+
+            // Construct the new context with the object details.
+            DWORD flags = (resultHolder.Result.FromTrackerRuntime
+                            ? ExternalObjectContext::Flags_ReferenceTracker
+                            : ExternalObjectContext::Flags_None) |
+                          (uniqueInstance
+                            ? ExternalObjectContext::Flags_None
+                            : ExternalObjectContext::Flags_InCache);
+            ExternalObjectContext::Construct(
+                resultHolder.GetContext(),
+                identity,
+                GetCurrentCtxCookie(),
+                gc.objRef->GetSyncBlockIndex(),
+                flags);
+
+            if (uniqueInstance)
+            {
+                extObjCxt = resultHolder.GetContext();
+            }
+            else
+            {
+                // Attempt to insert the new context into the cache.
+                ExtObjCxtCache::LockHolder lock(cache);
+                extObjCxt = cache->FindOrAdd(identity, resultHolder.GetContext());
+            }
+
+            // If the returned context matches the new context it means the
+            // new context was inserted or a unique instance was requested.
+            if (extObjCxt == resultHolder.GetContext())
+            {
+                // Update the object's SyncBlock with a handle to the context for runtime cleanup.
+                SyncBlock* syncBlock = gc.objRef->GetSyncBlock();
+                InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo();
+                _ASSERTE(syncBlock->IsPrecious());
+
+                // Since the caller has the option of providing a wrapper, it is
+                // possible the supplied wrapper already has an associated external
+                // object and an object can only be associated with one external object.
+                if (!interopInfo->TrySetExternalComObjectContext((void**)extObjCxt))
+                {
+                    // Failed to set the context; one must already exist.
+                    // Remove from the cache above as well.
+                    ExtObjCxtCache::LockHolder lock(cache);
+                    cache->Remove(resultHolder.GetContext());
+
+                    COMPlusThrow(kNotSupportedException);
+                }
+
+                // Detach from the holder to avoid cleanup.
+                (void)resultHolder.DetachContext();
+                STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created EOC (Unique Instance: %d): 0x%p\n", (int)uniqueInstance, extObjCxt);
+            }
+
+            _ASSERTE(extObjCxt->IsActive());
+        }
+
+        GCPROTECT_END();
+
+        RETURN gc.objRef;
+    }
+}
+
+namespace InteropLibImports
+{
+    void* MemAlloc(_In_ size_t sizeInBytes, _In_ AllocScenario scenario) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_ANY;
+            PRECONDITION(sizeInBytes != 0);
+        }
+        CONTRACTL_END;
+
+        return ::malloc(sizeInBytes);
+    }
+
+    void MemFree(_In_ void* mem, _In_ AllocScenario scenario) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_ANY;
+            PRECONDITION(mem != NULL);
+        }
+        CONTRACTL_END;
+
+        ::free(mem);
+    }
+
+    HRESULT AddMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            GCInterface::NewAddMemoryPressure(memoryInBytes);
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    HRESULT RemoveMemoryPressureForExternal(_In_ UINT64 memoryInBytes) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            GCInterface::NewRemoveMemoryPressure(memoryInBytes);
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    HRESULT RequestGarbageCollectionForExternal(_In_ GcRequest req) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            GCX_COOP_THREAD_EXISTS(GET_THREAD());
+            if (req == GcRequest::FullBlocking)
+            {
+                GCHeapUtilities::GetGCHeap()->GarbageCollect(2, true, collection_blocking | collection_optimized);
+            }
+            else
+            {
+                _ASSERTE(req == GcRequest::Default);
+                GCHeapUtilities::GetGCHeap()->GarbageCollect();
+            }
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    HRESULT WaitForRuntimeFinalizerForExternal() noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            FinalizerThread::FinalizerThreadWait();
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    HRESULT ReleaseExternalObjectsFromCurrentThread() noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            // Switch to cooperative mode so the cache can be queried.
+            GCX_COOP();
+
+            struct
+            {
+                OBJECTREF implRef;
+                OBJECTREF objsEnumRef;
+            } gc;
+            ::ZeroMemory(&gc, sizeof(gc));
+            GCPROTECT_BEGIN(gc);
+
+            gc.implRef = NULL; // Use the globally registered implementation.
+
+            // Pass the objects along to get released.
+            ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+            gc.objsEnumRef = cache->CreateManagedEnumerable(
+                ExternalObjectContext::Flags_ReferenceTracker,
+                GetCurrentCtxCookie());
+
+            CallReleaseObjects(&gc.implRef, &gc.objsEnumRef);
+
+            GCPROTECT_END();
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    void DeleteObjectInstanceHandle(_In_ InteropLib::OBJECTHANDLE handle) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_ANY;
+            PRECONDITION(handle != NULL);
+        }
+        CONTRACTL_END;
+
+        DestroyHandleCommon(static_cast<::OBJECTHANDLE>(handle), InstanceHandleType);
+    }
+
+    bool GetGlobalPeggingState() noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_ANY;
+        }
+        CONTRACTL_END;
+
+        return (RCWWalker::s_bIsGlobalPeggingOn != FALSE);
+    }
+
+    void SetGlobalPeggingState(_In_ bool state) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_ANY;
+        }
+        CONTRACTL_END;
+
+        BOOL newState = state ? TRUE : FALSE;
+        VolatileStore(&RCWWalker::s_bIsGlobalPeggingOn, newState);
+    }
+
+    HRESULT GetOrCreateTrackerTargetForExternal(
+        _In_ IUnknown* externalComObject,
+        _In_ CreateObjectFlags externalObjectFlags,
+        _In_ CreateComInterfaceFlags trackerTargetFlags,
+        _Outptr_ void** trackerTarget) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            MODE_PREEMPTIVE;
+            PRECONDITION(externalComObject != NULL);
+            PRECONDITION(trackerTarget != NULL);
+        }
+        CONTRACTL_END;
+
+        HRESULT hr = S_OK;
+        BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+        {
+            // Switch to Cooperative mode since object references
+            // are being manipulated.
+            GCX_COOP();
+
+            struct
+            {
+                OBJECTREF implRef;
+                OBJECTREF wrapperMaybeRef;
+                OBJECTREF objRef;
+            } gc;
+            ::ZeroMemory(&gc, sizeof(gc));
+            GCPROTECT_BEGIN(gc);
+
+            gc.implRef = NULL; // Use the globally registered implementation.
+            gc.wrapperMaybeRef = NULL; // No supplied wrapper here.
+
+            // Get wrapper for external object
+            gc.objRef = GetOrCreateObjectForComInstanceInternal(
+                gc.implRef,
+                externalComObject,
+                externalObjectFlags,
+                gc.wrapperMaybeRef);
+
+            // Get wrapper for managed object
+            *trackerTarget = GetOrCreateComInterfaceForObjectInternal(
+                gc.implRef,
+                gc.objRef,
+                trackerTargetFlags);
+
+            STRESS_LOG2(LF_INTEROP, LL_INFO100, "Created Target for External: 0x%p => 0x%p\n", OBJECTREFToObject(gc.objRef), *trackerTarget);
+            GCPROTECT_END();
+        }
+        END_EXTERNAL_ENTRYPOINT;
+
+        return hr;
+    }
+
+    struct RuntimeCallContext
+    {
+        // Iterators for all known external objects.
+        ExtObjCxtCache::Iterator Curr;
+        ExtObjCxtCache::Iterator End;
+
+        // Pointer to cache used to create object references.
+        ExtObjCxtRefCache* RefCache;
+
+        RuntimeCallContext(_In_ ExtObjCxtCache* cache)
+            : Curr{ cache->_hashMap.Begin() }
+            , End{ cache->_hashMap.End() }
+            , RefCache{ cache->GetRefCache() }
+        { }
+    };
+
+    HRESULT IteratorNext(
+        _In_ RuntimeCallContext* runtimeContext,
+        _Outptr_result_maybenull_ void** extObjContext) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_COOPERATIVE;
+            PRECONDITION(runtimeContext != NULL);
+            PRECONDITION(extObjContext != NULL);
+
+            // Should only be called during a GC suspension
+            PRECONDITION(Debug_IsLockedViaThreadSuspension());
+        }
+        CONTRACTL_END;
+
+        if (runtimeContext->Curr == runtimeContext->End)
+        {
+            *extObjContext = NULL;
+            return S_FALSE;
+        }
+
+        ExtObjCxtCache::Element e = *runtimeContext->Curr++;
+        *extObjContext = e;
+        return S_OK;
+    }
+
+    HRESULT FoundReferencePath(
+        _In_ RuntimeCallContext* runtimeContext,
+        _In_ void* extObjContextRaw,
+        _In_ InteropLib::OBJECTHANDLE handle) noexcept
+    {
+        CONTRACTL
+        {
+            NOTHROW;
+            GC_NOTRIGGER;
+            MODE_COOPERATIVE;
+            PRECONDITION(runtimeContext != NULL);
+            PRECONDITION(extObjContextRaw != NULL);
+            PRECONDITION(handle != NULL);
+
+            // Should only be called during a GC suspension
+            PRECONDITION(Debug_IsLockedViaThreadSuspension());
+        }
+        CONTRACTL_END;
+
+        // Get the external object's managed wrapper
+        ExternalObjectContext* extObjContext = static_cast<ExternalObjectContext*>(extObjContextRaw);
+        OBJECTREF source = extObjContext->GetObjectRef();
+
+        // Get the target of the external object's reference.
+        ::OBJECTHANDLE objectHandle = static_cast<::OBJECTHANDLE>(handle);
+        OBJECTREF target = ObjectFromHandle(objectHandle);
+
+        // If these point at the same object don't create a reference.
+        if (source->PassiveGetSyncBlock() == target->PassiveGetSyncBlock())
+            return S_FALSE;
+
+        STRESS_LOG2(LF_INTEROP, LL_INFO1000, "Found reference path: 0x%p => 0x%p\n",
+            OBJECTREFToObject(source),
+            OBJECTREFToObject(target));
+        return runtimeContext->RefCache->AddReferenceFromObjectToObject(source, target);
+    }
+}
+
+#ifdef FEATURE_COMWRAPPERS
+
+void* QCALLTYPE ComWrappersNative::GetOrCreateComInterfaceForObject(
+    _In_ QCall::ObjectHandleOnStack comWrappersImpl,
+    _In_ QCall::ObjectHandleOnStack instance,
+    _In_ INT32 flags)
+{
+    QCALL_CONTRACT;
+
+    void* wrapper = NULL;
+
+    BEGIN_QCALL;
+
+    // Switch to Cooperative mode since object references
+    // are being manipulated.
+    {
+        GCX_COOP();
+        wrapper = GetOrCreateComInterfaceForObjectInternal(
+            ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
+            ObjectToOBJECTREF(*instance.m_ppObject),
+            (CreateComInterfaceFlags)flags);
+    }
+
+    END_QCALL;
+
+    _ASSERTE(wrapper != NULL);
+    return wrapper;
+}
+
+void QCALLTYPE ComWrappersNative::GetOrCreateObjectForComInstance(
+    _In_ QCall::ObjectHandleOnStack comWrappersImpl,
+    _In_ void* ext,
+    _In_ INT32 flags,
+    _In_ QCall::ObjectHandleOnStack wrapperMaybe,
+    _Inout_ QCall::ObjectHandleOnStack retValue)
+{
+    QCALL_CONTRACT;
+
+    _ASSERTE(ext != NULL);
+
+    BEGIN_QCALL;
+
+    HRESULT hr;
+    IUnknown* externalComObject = reinterpret_cast<IUnknown*>(ext);
+
+    // Determine the true identity of the object
+    SafeComHolder<IUnknown> identity;
+    hr = externalComObject->QueryInterface(IID_IUnknown, &identity);
+    _ASSERTE(hr == S_OK);
+
+    // Switch to Cooperative mode since object references
+    // are being manipulated.
+    {
+        GCX_COOP();
+        OBJECTREF newObj = GetOrCreateObjectForComInstanceInternal(
+            ObjectToOBJECTREF(*comWrappersImpl.m_ppObject),
+            identity,
+            (CreateObjectFlags)flags,
+            ObjectToOBJECTREF(*wrapperMaybe.m_ppObject));
+
+        // Set the return value
+        retValue.Set(newObj);
+    }
+
+    END_QCALL;
+}
+
+void QCALLTYPE ComWrappersNative::GetIUnknownImpl(
+        _Out_ void** fpQueryInterface,
+        _Out_ void** fpAddRef,
+        _Out_ void** fpRelease)
+{
+    QCALL_CONTRACT;
+
+    _ASSERTE(fpQueryInterface != NULL);
+    _ASSERTE(fpAddRef != NULL);
+    _ASSERTE(fpRelease != NULL);
+
+    BEGIN_QCALL;
+
+    InteropLib::Com::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease);
+
+    END_QCALL;
+}
+
+void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        MODE_ANY;
+        PRECONDITION(wrapper != NULL);
+    }
+    CONTRACTL_END;
+
+    STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying MOW: 0x%p\n", wrapper);
+    InteropLib::Com::DestroyWrapperForObject(wrapper);
+}
+
+void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        MODE_ANY;
+        PRECONDITION(contextRaw != NULL);
+    }
+    CONTRACTL_END;
+
+#ifdef _DEBUG
+    ExternalObjectContext* context = static_cast<ExternalObjectContext*>(contextRaw);
+    _ASSERTE(!context->IsActive());
+#endif
+
+    STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying EOC: 0x%p\n", contextRaw);
+    InteropLib::Com::DestroyWrapperForExternal(contextRaw);
+}
+
+void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* contextRaw)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        PRECONDITION(contextRaw != NULL);
+        PRECONDITION(GCHeapUtilities::IsGCInProgress());
+    }
+    CONTRACTL_END;
+
+    ExternalObjectContext* context = static_cast<ExternalObjectContext*>(contextRaw);
+    _ASSERTE(context->IsActive());
+    context->MarkCollected();
+
+    bool inCache = context->IsSet(ExternalObjectContext::Flags_InCache);
+    STRESS_LOG2(LF_INTEROP, LL_INFO100, "Mark Collected EOC (In Cache: %d): 0x%p\n", (int)inCache, contextRaw);
+
+    // Verify the caller didn't ignore the cache during creation.
+    if (inCache)
+    {
+        ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+        cache->Remove(context);
+    }
+}
+
+#endif // FEATURE_COMWRAPPERS
+
+void Interop::OnGCStarted(_In_ int nCondemnedGeneration)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+    //
+    // Let GC detect managed/native cycles with input from jupiter
+    // Jupiter will
+    // 1. Report reference from RCW to CCW based on native reference in Jupiter
+    // 2. Identify the subset of CCWs that needs to be rooted
+    //
+    // We'll build the references from RCW to CCW using
+    // 1. Preallocated arrays
+    // 2. Dependent handles
+    //
+    RCWWalker::OnGCStarted(nCondemnedGeneration);
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_COMWRAPPERS
+    //
+    // Note that we could get nested GCStart/GCEnd calls, such as :
+    // GCStart for Gen 2 background GC
+    //    GCStart for Gen 0/1 foregorund GC
+    //    GCEnd   for Gen 0/1 foreground GC
+    //    ....
+    // GCEnd for Gen 2 background GC
+    //
+    // The nCondemnedGeneration >= 2 check takes care of this nesting problem
+    //
+    // See Interop::OnGCFinished()
+    if (nCondemnedGeneration >= 2)
+    {
+        // If no cache exists, then there is nothing to do here.
+        ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+        if (cache != NULL)
+        {
+            STRESS_LOG0(LF_INTEROP, LL_INFO10000, "Begin Reference Tracking\n");
+            ExtObjCxtRefCache* refCache = cache->GetRefCache();
+
+            // Reset the ref cache
+            refCache->ResetDependentHandles();
+
+            // Create a call context for the InteropLib.
+            InteropLibImports::RuntimeCallContext cxt(cache);
+            (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt);
+
+            // Shrink cache and clear unused handles.
+            refCache->ShrinkDependentHandles();
+        }
+    }
+#endif // FEATURE_COMWRAPPERS
+}
+
+void Interop::OnGCFinished(_In_ int nCondemnedGeneration)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+    }
+    CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+    //
+    // Tell Jupiter GC has finished
+    //
+    RCWWalker::OnGCFinished(nCondemnedGeneration);
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_COMWRAPPERS
+    //
+    // Note that we could get nested GCStart/GCEnd calls, such as :
+    // GCStart for Gen 2 background GC
+    //    GCStart for Gen 0/1 foregorund GC
+    //    GCEnd   for Gen 0/1 foreground GC
+    //    ....
+    // GCEnd for Gen 2 background GC
+    //
+    // The nCondemnedGeneration >= 2 check takes care of this nesting problem
+    //
+    // See Interop::OnGCStarted()
+    if (nCondemnedGeneration >= 2)
+    {
+        ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+        if (cache != NULL)
+        {
+            (void)InteropLib::Com::EndExternalObjectReferenceTracking();
+            STRESS_LOG0(LF_INTEROP, LL_INFO10000, "End Reference Tracking\n");
+        }
+    }
+#endif // FEATURE_COMWRAPPERS
+}
diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h
new file mode 100644 (file)
index 0000000..2ed54cf
--- /dev/null
@@ -0,0 +1,48 @@
+// 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.
+
+//
+// Interface between the VM and Interop library.
+//
+
+#ifdef FEATURE_COMWRAPPERS
+
+// Native calls for the managed ComWrappers API
+class ComWrappersNative
+{
+public: // Native QCalls for the abstract ComWrappers managed type.
+    static void QCALLTYPE GetIUnknownImpl(
+        _Out_ void** fpQueryInterface,
+        _Out_ void** fpAddRef,
+        _Out_ void** fpRelease);
+
+    static void* QCALLTYPE GetOrCreateComInterfaceForObject(
+        _In_ QCall::ObjectHandleOnStack comWrappersImpl,
+        _In_ QCall::ObjectHandleOnStack instance,
+        _In_ INT32 flags);
+
+    static void QCALLTYPE GetOrCreateObjectForComInstance(
+        _In_ QCall::ObjectHandleOnStack comWrappersImpl,
+        _In_ void* externalComObject,
+        _In_ INT32 flags,
+        _In_ QCall::ObjectHandleOnStack wrapperMaybe,
+        _Inout_ QCall::ObjectHandleOnStack retValue);
+
+public: // Lifetime management for COM Wrappers
+    static void DestroyManagedObjectComWrapper(_In_ void* wrapper);
+    static void DestroyExternalComObjectContext(_In_ void* context);
+    static void MarkExternalComObjectContextCollected(_In_ void* context);
+};
+
+#endif // FEATURE_COMWRAPPERS
+
+class Interop
+{
+public:
+    // Notify when GC started
+    static void OnGCStarted(_In_ int nCondemnedGeneration);
+
+    // Notify when GC finished
+    static void OnGCFinished(_In_ int nCondemnedGeneration);
+};
index 0ec23b6..fc87cf9 100644 (file)
@@ -25,6 +25,7 @@
 #include "siginfo.hpp"
 #include "eemessagebox.h"
 #include "finalizerthread.h"
+#include "interoplibinterface.h"
 
 #ifdef FEATURE_COMINTEROP
 #include "cominterfacemarshaler.h"
@@ -1936,6 +1937,12 @@ void MinorCleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo)
     RCW* pRCW = pInteropInfo->GetRawRCW();
     if (pRCW)
         pRCW->MinorCleanup();
+
+#ifdef FEATURE_COMWRAPPERS
+    void* eoc;
+    if (pInteropInfo->TryGetExternalComObjectContext(&eoc))
+        ComWrappersNative::MarkExternalComObjectContextCollected(eoc);
+#endif // FEATURE_COMWRAPPERS
 }
 
 void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo)
@@ -1976,6 +1983,22 @@ void CleanupSyncBlockComData(InteropSyncBlockInfo* pInteropInfo)
         pInteropInfo->SetCCW(NULL);
         pCCW->Cleanup();
     }
+
+#ifdef FEATURE_COMWRAPPERS
+    void* mocw;
+    if (pInteropInfo->TryGetManagedObjectComWrapper(&mocw))
+    {
+        (void)pInteropInfo->TrySetManagedObjectComWrapper(NULL, mocw);
+        ComWrappersNative::DestroyManagedObjectComWrapper(mocw);
+    }
+
+    void* eoc;
+    if (pInteropInfo->TryGetExternalComObjectContext(&eoc))
+    {
+        (void)pInteropInfo->TrySetExternalComObjectContext(NULL, eoc);
+        ComWrappersNative::DestroyExternalComObjectContext(eoc);
+    }
+#endif // FEATURE_COMWRAPPERS
 }
 
 void ReleaseRCWsInCachesNoThrow(LPVOID pCtxCookie)
index 8699224..e783046 100644 (file)
@@ -196,6 +196,9 @@ 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(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v))
 #endif // FEATURE_COMINTEROP
 DEFINE_METASIG(SM(Int_RetVoid, i, v))
 DEFINE_METASIG(SM(Int_Int_RetVoid, i i, v))
index 40a1e14..6cdd5d9 100644 (file)
@@ -67,6 +67,7 @@
 #include "variant.h"
 #include "oavariant.h"
 #include "mngstdinterfaces.h"
+#include "interoplibinterface.h"
 #endif // FEATURE_COMINTEROP
 
 #include "stubhelpers.h"
index 3ce6149..12ec6d8 100644 (file)
@@ -452,12 +452,19 @@ DEFINE_METHOD(ICUSTOM_MARSHALER,    MARSHAL_NATIVE_TO_MANAGED,MarshalNativeToMan
 DEFINE_METHOD(ICUSTOM_MARSHALER,    MARSHAL_MANAGED_TO_NATIVE,MarshalManagedToNative,   IM_Obj_RetIntPtr)
 DEFINE_METHOD(ICUSTOM_MARSHALER,    CLEANUP_NATIVE_DATA,    CleanUpNativeData,          IM_IntPtr_RetVoid)
 DEFINE_METHOD(ICUSTOM_MARSHALER,    CLEANUP_MANAGED_DATA,   CleanUpManagedData,         IM_Obj_RetVoid)
-DEFINE_METHOD(ICUSTOM_MARSHALER,    GET_NATIVE_DATA_SIZE,   GetNativeDataSize,         IM_RetInt)
+DEFINE_METHOD(ICUSTOM_MARSHALER,    GET_NATIVE_DATA_SIZE,   GetNativeDataSize,          IM_RetInt)
 
 #ifdef FEATURE_COMINTEROP
 DEFINE_CLASS(ICUSTOM_QUERYINTERFACE,      Interop,          ICustomQueryInterface)
-DEFINE_METHOD(ICUSTOM_QUERYINTERFACE,     GET_INTERFACE,    GetInterface,                IM_RefGuid_OutIntPtr_RetCustomQueryInterfaceResult)
+DEFINE_METHOD(ICUSTOM_QUERYINTERFACE,     GET_INTERFACE,    GetInterface,               IM_RefGuid_OutIntPtr_RetCustomQueryInterfaceResult)
 DEFINE_CLASS(CUSTOMQUERYINTERFACERESULT,  Interop,          CustomQueryInterfaceResult)
+
+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_METHOD(COMWRAPPERS,                RELEASE_OBJECTS,  CallReleaseObjects,         SM_ComWrappers_IEnumerable_RetVoid)
 #endif //FEATURE_COMINTEROP
 
 DEFINE_CLASS(SERIALIZATION_INFO,        Serialization,      SerializationInfo)
index 652f182..c22a84e 100644 (file)
@@ -209,23 +209,40 @@ HRESULT RCWRefCache::AddReferenceFromRCWToCCW(RCW *pRCW, ComCallWrapper *pCCW)
     CONTRACTL_END;
 
     // Try adding reference using dependent handles
-    return AddReferenceUsingDependentHandle(pRCW, pCCW);
+    return AddReferenceUsingDependentHandle(pRCW->GetExposedObject(), pCCW->GetObjectRef());
 }
 
 //
-// Add RCW -> CCW reference using dependent handle
+// Add a reference from obj1 to obj2
+//
+HRESULT RCWRefCache::AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2)
+{
+    CONTRACTL
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_COOPERATIVE;
+        PRECONDITION(obj1 != NULL);
+        PRECONDITION(obj2 != NULL);
+    }
+    CONTRACTL_END;
+
+    // Try adding reference using dependent handles
+    return AddReferenceUsingDependentHandle(obj1, obj2);
+}
+
+//
+// Add obj1 -> obj2 reference using dependent handle
 // May fail if OOM
 //
-HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper *pCCW)
+HRESULT RCWRefCache::AddReferenceUsingDependentHandle(OBJECTREF obj1, OBJECTREF obj2)
 {
     CONTRACTL
     {
         NOTHROW;
         GC_NOTRIGGER;
         MODE_COOPERATIVE;
-        PRECONDITION(CheckPointer(pRCW));
-        PRECONDITION(CheckPointer(pCCW));
-        PRECONDITION(CheckPointer(OBJECTREFToObject(pCCW->GetObjectRef())));
+        PRECONDITION(CheckPointer(OBJECTREFToObject(obj2)));
     }
     CONTRACTL_END;
 
@@ -242,7 +259,7 @@ HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper
         // No, we need to create a new handle
         EX_TRY
         {
-            OBJECTHANDLE depHnd = m_pAppDomain->CreateDependentHandle(pRCW->GetExposedObject(), pCCW->GetObjectRef());
+            OBJECTHANDLE depHnd = m_pAppDomain->CreateDependentHandle(obj1, obj2);
             m_depHndList.Push(depHnd);
 
             STRESS_LOG2(
@@ -267,8 +284,8 @@ HRESULT RCWRefCache::AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper
         OBJECTHANDLE depHnd = (OBJECTHANDLE) m_depHndList[m_dwDepHndListFreeIndex];
 
         IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
-        mgr->StoreObjectInHandle(depHnd, OBJECTREFToObject(pRCW->GetExposedObject()));
-        mgr->SetDependentHandleSecondary(depHnd, OBJECTREFToObject(pCCW->GetObjectRef()));
+        mgr->StoreObjectInHandle(depHnd, OBJECTREFToObject(obj1));
+        mgr->SetDependentHandleSecondary(depHnd, OBJECTREFToObject(obj2));
 
         STRESS_LOG3(
             LF_INTEROP, LL_INFO1000,
index 4566b08..4e9aa20 100644 (file)
@@ -32,6 +32,11 @@ public :
     HRESULT AddReferenceFromRCWToCCW(RCW *pRCW, ComCallWrapper *pCCW);
 
     //
+    // Add a reference from obj1 to obj2
+    //
+    HRESULT AddReferenceFromObjectToObject(OBJECTREF obj1, OBJECTREF obj2);
+
+    //
     // Enumerate all Jupiter RCWs in the RCW cache and do the callback
     // I'm using template here so there is no perf penality
     //
@@ -84,10 +89,10 @@ public :
 
 private :
     //
-    // Add RCW -> CCW reference using dependent handle
+    // Add obj1 -> obj2 reference using dependent handle
     // May fail if OOM
     //
-    HRESULT AddReferenceUsingDependentHandle(RCW *pRCW, ComCallWrapper *pCCW);
+    HRESULT AddReferenceUsingDependentHandle(OBJECTREF obj1, OBJECTREF obj2);
 
 private :
     AppDomain      *m_pAppDomain;                   // Domain
index 0a532d8..f625725 100644 (file)
@@ -33,12 +33,13 @@ class RCWWalker
 {
     friend struct _DacGlobals;
 
-private :
+private:
     static VolatilePtr<IJupiterGCManager>  s_pGCManager;            // The one and only GCManager instance
     static BOOL                     s_bGCStarted;                   // Has GC started?
+
+public:
     SVAL_DECL(BOOL,                 s_bIsGlobalPeggingOn);           // Do we need to peg every CCW?
 
-public :
 #ifndef DACCESS_COMPILE
     static void OnJupiterRCWCreated(RCW *pRCW, IJupiterObject *pJupiterObject);
     static void AfterJupiterRCWCreated(RCW *pRCW);
index e740579..2b4cad2 100644 (file)
@@ -1721,6 +1721,28 @@ FCIMPL1(IMDInternalImport*, RuntimeTypeHandle::GetMetadataImport, ReflectClassBa
 }
 FCIMPLEND
 
+PVOID QCALLTYPE RuntimeTypeHandle::AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size)
+{
+    QCALL_CONTRACT;
+
+    void *allocatedMemory = nullptr;
+
+    BEGIN_QCALL;
+
+    TypeHandle typeHandle = type.AsTypeHandle();
+    _ASSERTE(!typeHandle.IsNull());
+
+    // Get the loader allocator for the associated type.
+    // Allocating using the type's associated loader allocator means
+    // that the memory will be freed when the type is unloaded.
+    PTR_LoaderAllocator loaderAllocator = typeHandle.GetMethodTable()->GetLoaderAllocator();
+    LoaderHeap* loaderHeap = loaderAllocator->GetHighFrequencyHeap();
+    allocatedMemory = loaderHeap->AllocMem(S_SIZE_T(size));
+
+    END_QCALL;
+
+    return allocatedMemory;
+}
 
 //***********************************************************************************
 //***********************************************************************************
index 36bc4ad..7787e01 100644 (file)
@@ -261,6 +261,9 @@ public:
 
     static
     FCDECL1(IMDInternalImport*, GetMetadataImport, ReflectClassBaseObject * pModuleUNSAFE);
+
+    static
+    PVOID QCALLTYPE AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size);
 };
 
 class RuntimeMethodHandle {
index f30b056..e0364df 100644 (file)
@@ -789,6 +789,50 @@ public:
     // instead.
     TADDR               m_pRCW;
 #endif
+
+public:
+    bool TryGetManagedObjectComWrapper(_Out_ void** mocw)
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        *mocw = m_managedObjectComWrapper;
+        return (*mocw != NULL);
+    }
+
+#ifndef DACCESS_COMPILE
+    bool TrySetManagedObjectComWrapper(_In_ void* mocw, _In_ void* curr = NULL)
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        return (FastInterlockCompareExchangePointer(
+                        &m_managedObjectComWrapper,
+                        mocw,
+                        curr) == curr);
+    }
+#endif // !DACCESS_COMPILE
+
+    bool TryGetExternalComObjectContext(_Out_ void** eoc)
+    {
+        LIMITED_METHOD_DAC_CONTRACT;
+        *eoc = m_externalComObjectContext;
+        return (*eoc != NULL);
+    }
+
+#ifndef DACCESS_COMPILE
+    bool TrySetExternalComObjectContext(_In_ void* eoc, _In_ void* curr = NULL)
+    {
+        LIMITED_METHOD_CONTRACT;
+
+        return (FastInterlockCompareExchangePointer(
+                        &m_externalComObjectContext,
+                        eoc,
+                        curr) == curr);
+    }
+#endif // !DACCESS_COMPILE
+
+private:
+    // See InteropLib API for usage.
+    void* m_managedObjectComWrapper;
+    void* m_externalComObjectContext;
 #endif // FEATURE_COMINTEROP
 
 };
index 3e534c8..d8f23d3 100644 (file)
@@ -81,6 +81,7 @@ if(CLR_CMAKE_TARGET_WIN32)
     add_subdirectory(COM/NativeClients/Licensing)
     add_subdirectory(COM/NativeClients/DefaultInterfaces)
     add_subdirectory(COM/NativeClients/Dispatch)
+    add_subdirectory(COM/ComWrappers/MockReferenceTrackerRuntime)
     add_subdirectory(WinRT/NativeComponent)
 
     # IJW isn't supported on ARM64
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/ComWrappersTests.csproj
new file mode 100644 (file)
index 0000000..e82960a
--- /dev/null
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <!-- Test unsupported outside of windows -->
+    <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+    <DisableProjectBuild Condition="'$(TargetsWindows)' != 'true'">true</DisableProjectBuild>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <Import Project="$([MSBuild]::GetPathOfFileAbove(Interop.settings.targets))" />
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="MockReferenceTrackerRuntime/CMakeLists.txt" />
+  </ItemGroup>
+</Project>
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fb232b9
--- /dev/null
@@ -0,0 +1,10 @@
+project (MockReferenceTrackerRuntime)
+include_directories( ${INC_PLATFORM_DIR} )
+set(SOURCES ReferenceTrackerRuntime.cpp)
+
+# add the shared library
+add_library (MockReferenceTrackerRuntime SHARED ${SOURCES})
+target_link_libraries(MockReferenceTrackerRuntime ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS MockReferenceTrackerRuntime DESTINATION bin)
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/MockReferenceTrackerRuntime/ReferenceTrackerRuntime.cpp
new file mode 100644 (file)
index 0000000..f0a0f30
--- /dev/null
@@ -0,0 +1,339 @@
+// 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.
+
+#include <xplatform.h>
+#include <ComHelpers.h>
+#include <unordered_map>
+#include <list>
+
+namespace API
+{
+    // Documentation found at https://docs.microsoft.com/windows/win32/api/windows.ui.xaml.hosting.referencetracker/
+    class DECLSPEC_UUID("64bd43f8-bfee-4ec4-b7eb-2935158dae21") IReferenceTrackerTarget : public IUnknown
+    {
+    public:
+        STDMETHOD_(ULONG, AddRefFromReferenceTracker)() = 0;
+        STDMETHOD_(ULONG, ReleaseFromReferenceTracker)() = 0;
+        STDMETHOD(Peg)() = 0;
+        STDMETHOD(Unpeg)() = 0;
+    };
+
+    class DECLSPEC_UUID("29a71c6a-3c42-4416-a39d-e2825a07a773") IReferenceTrackerHost : public IUnknown
+    {
+    public:
+        STDMETHOD(DisconnectUnusedReferenceSources)(_In_ DWORD dwFlags) = 0;
+        STDMETHOD(ReleaseDisconnectedReferenceSources)() = 0;
+        STDMETHOD(NotifyEndOfReferenceTrackingOnThread)() = 0;
+        STDMETHOD(GetTrackerTarget)(_In_ IUnknown* obj, _Outptr_ IReferenceTrackerTarget** ppNewReference) = 0;
+        STDMETHOD(AddMemoryPressure)(_In_ UINT64 bytesAllocated) = 0;
+        STDMETHOD(RemoveMemoryPressure)(_In_ UINT64 bytesAllocated) = 0;
+    };
+
+    class DECLSPEC_UUID("3cf184b4-7ccb-4dda-8455-7e6ce99a3298") IReferenceTrackerManager : public IUnknown
+    {
+    public:
+        STDMETHOD(ReferenceTrackingStarted)() = 0;
+        STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed) = 0;
+        STDMETHOD(ReferenceTrackingCompleted)() = 0;
+        STDMETHOD(SetReferenceTrackerHost)(_In_ IReferenceTrackerHost *pCLRServices) = 0;
+    };
+
+    class DECLSPEC_UUID("04b3486c-4687-4229-8d14-505ab584dd88") IFindReferenceTargetsCallback : public IUnknown
+    {
+    public:
+        STDMETHOD(FoundTrackerTarget)(_In_ IReferenceTrackerTarget* target) = 0;
+    };
+
+    class DECLSPEC_UUID("11d3b13a-180e-4789-a8be-7712882893e6") IReferenceTracker : public IUnknown
+    {
+    public:
+        STDMETHOD(ConnectFromTrackerSource)() = 0;
+        STDMETHOD(DisconnectFromTrackerSource)() = 0;
+        STDMETHOD(FindTrackerTargets)(_In_ IFindReferenceTargetsCallback *pCallback) = 0;
+        STDMETHOD(GetReferenceTrackerManager)(_Outptr_ IReferenceTrackerManager **ppTrackerManager) = 0;
+        STDMETHOD(AddRefFromTrackerSource)() = 0;
+        STDMETHOD(ReleaseFromTrackerSource)() = 0;
+        STDMETHOD(PegFromTrackerSource)() = 0;
+    };
+}
+
+namespace
+{
+    // Testing types
+    struct DECLSPEC_UUID("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09") ITest : public IUnknown
+    {
+        STDMETHOD(SetValue)(int i) = 0;
+    };
+
+    struct DECLSPEC_UUID("42951130-245C-485E-B60B-4ED4254256F8") ITrackerObject : public IUnknown
+    {
+        STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id) = 0;
+        STDMETHOD(DropObjectRef)(_In_ int id) = 0;
+    };
+
+    struct TrackerObject : public ITrackerObject, public API::IReferenceTracker, public UnknownImpl
+    {
+        const size_t _id;
+        std::atomic<int> _trackerSourceCount;
+        bool _connected;
+        std::atomic<int> _elementId;
+        std::unordered_map<int, ComSmartPtr<IUnknown>> _elements;
+
+        TrackerObject(size_t id) : _id{ id }, _trackerSourceCount{ 0 }, _connected{ false }, _elementId{ 1 }
+        { }
+
+        HRESULT ToggleTargets(_In_ bool shouldPeg)
+        {
+            HRESULT hr;
+
+            auto curr = std::begin(_elements);
+            while (curr != std::end(_elements))
+            {
+                ComSmartPtr<API::IReferenceTrackerTarget> mowMaybe;
+                if (S_OK == curr->second->QueryInterface(&mowMaybe))
+                {
+                    if (shouldPeg)
+                    {
+                        RETURN_IF_FAILED(mowMaybe->Peg());
+                    }
+                    else
+                    {
+                        RETURN_IF_FAILED(mowMaybe->Unpeg());
+                    }
+                }
+                ++curr;
+            }
+
+            return S_OK;
+        }
+
+        STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id)
+        {
+            assert(c != nullptr && id != nullptr);
+
+            try
+            {
+                *id = _elementId;
+                if (!_elements.insert(std::make_pair(*id, ComSmartPtr<IUnknown>{ c })).second)
+                    return S_FALSE;
+
+                _elementId++;
+            }
+            catch (const std::bad_alloc&)
+            {
+                return E_OUTOFMEMORY;
+            }
+
+            ComSmartPtr<API::IReferenceTrackerTarget> mowMaybe;
+            if (S_OK == c->QueryInterface(&mowMaybe))
+                (void)mowMaybe->AddRefFromReferenceTracker();
+
+            return S_OK;
+        }
+
+        STDMETHOD(DropObjectRef)(_In_ int id)
+        {
+            auto iter = _elements.find(id);
+            if (iter == std::end(_elements))
+                return S_FALSE;
+
+            ComSmartPtr<API::IReferenceTrackerTarget> mowMaybe;
+            if (S_OK == iter->second->QueryInterface(&mowMaybe))
+            {
+                (void)mowMaybe->ReleaseFromReferenceTracker();
+            }
+
+            _elements.erase(iter);
+
+            return S_OK;
+        }
+
+        STDMETHOD(ConnectFromTrackerSource)();
+        STDMETHOD(DisconnectFromTrackerSource)();
+        STDMETHOD(FindTrackerTargets)(_In_ API::IFindReferenceTargetsCallback* pCallback);
+        STDMETHOD(GetReferenceTrackerManager)(_Outptr_ API::IReferenceTrackerManager** ppTrackerManager);
+        STDMETHOD(AddRefFromTrackerSource)();
+        STDMETHOD(ReleaseFromTrackerSource)();
+        STDMETHOD(PegFromTrackerSource)();
+
+        STDMETHOD(QueryInterface)(
+            /* [in] */ REFIID riid,
+            /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+        {
+            return DoQueryInterface(riid, ppvObject, static_cast<IReferenceTracker*>(this), static_cast<ITrackerObject*>(this));
+        }
+
+        DEFINE_REF_COUNTING()
+    };
+
+    std::atomic<size_t> CurrentObjectId{};
+
+    class TrackerRuntimeManagerImpl : public API::IReferenceTrackerManager
+    {
+        ComSmartPtr<API::IReferenceTrackerHost> _runtimeServices;
+        std::list<ComSmartPtr<TrackerObject>> _objects;
+
+    public:
+        void RecordObject(_In_ TrackerObject* obj)
+        {
+            _objects.push_back(ComSmartPtr<TrackerObject>{ obj });
+
+            if (_runtimeServices != nullptr)
+                _runtimeServices->AddMemoryPressure(sizeof(TrackerObject));
+        }
+
+        void ReleaseObjects()
+        {
+            size_t count = _objects.size();
+            _objects.clear();
+            if (_runtimeServices != nullptr)
+                _runtimeServices->RemoveMemoryPressure(sizeof(TrackerObject) * count);
+        }
+
+        HRESULT NotifyEndOfReferenceTrackingOnThread()
+        {
+            if (_runtimeServices != nullptr)
+                return _runtimeServices->NotifyEndOfReferenceTrackingOnThread();
+
+            return S_OK;
+        }
+
+    public: // IReferenceTrackerManager
+        STDMETHOD(ReferenceTrackingStarted)()
+        {
+            // Unpeg all instances
+            for (auto& i : _objects)
+                i->ToggleTargets(/* should peg */ false);
+
+            return S_OK;
+        }
+
+        STDMETHOD(FindTrackerTargetsCompleted)(_In_ BOOL bWalkFailed)
+        {
+            // Verify and ensure all connected types are pegged
+            for (auto& i : _objects)
+                i->ToggleTargets(/* should peg */ true);
+
+            return S_OK;
+        }
+
+        STDMETHOD(ReferenceTrackingCompleted)()
+        {
+            return S_OK;
+        }
+
+        STDMETHOD(SetReferenceTrackerHost)(_In_ API::IReferenceTrackerHost* pHostServices)
+        {
+            assert(pHostServices != nullptr);
+            return pHostServices->QueryInterface(&_runtimeServices);
+        }
+
+        // Lifetime maintained by stack - we don't care about ref counts
+        STDMETHOD_(ULONG, AddRef)() { return 1; }
+        STDMETHOD_(ULONG, Release)() { return 1; }
+
+        STDMETHOD(QueryInterface)(
+            /* [in] */ REFIID riid,
+            /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
+        {
+            if (ppvObject == nullptr)
+                return E_POINTER;
+
+            if (IsEqualIID(riid, __uuidof(API::IReferenceTrackerManager)))
+            {
+                *ppvObject = static_cast<API::IReferenceTrackerManager*>(this);
+            }
+            else if (IsEqualIID(riid, IID_IUnknown))
+            {
+                *ppvObject = static_cast<IUnknown*>(this);
+            }
+            else
+            {
+                *ppvObject = nullptr;
+                return E_NOINTERFACE;
+            }
+
+            AddRef();
+            return S_OK;
+        }
+    };
+
+    TrackerRuntimeManagerImpl TrackerRuntimeManager;
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::ConnectFromTrackerSource()
+    {
+        _connected = true;
+        return S_OK;
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::DisconnectFromTrackerSource()
+    {
+        _connected = false;
+        return S_OK;
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::FindTrackerTargets(_In_ API::IFindReferenceTargetsCallback* pCallback)
+    {
+        assert(pCallback != nullptr);
+
+        ComSmartPtr<API::IReferenceTrackerTarget> mowMaybe;
+        for (auto& e : _elements)
+        {
+            if (S_OK == e.second->QueryInterface(&mowMaybe))
+            {
+                (void)pCallback->FoundTrackerTarget(mowMaybe.p);
+                mowMaybe.Release();
+            }
+        }
+
+        return S_OK;
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::GetReferenceTrackerManager(_Outptr_ API::IReferenceTrackerManager** ppTrackerManager)
+    {
+        assert(ppTrackerManager != nullptr);
+        return TrackerRuntimeManager.QueryInterface(__uuidof(API::IReferenceTrackerManager), (void**)ppTrackerManager);
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::AddRefFromTrackerSource()
+    {
+        assert(0 <= _trackerSourceCount);
+        ++_trackerSourceCount;
+        return S_OK;
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::ReleaseFromTrackerSource()
+    {
+        assert(0 < _trackerSourceCount);
+        --_trackerSourceCount;
+        return S_OK;
+    }
+
+    HRESULT STDMETHODCALLTYPE TrackerObject::PegFromTrackerSource()
+    {
+        /* Not used by runtime */
+        return E_NOTIMPL;
+    }
+}
+
+// Create external object
+extern "C" DLL_EXPORT ITrackerObject* STDMETHODCALLTYPE CreateTrackerObject()
+{
+    auto obj = new TrackerObject{ CurrentObjectId++ };
+
+    TrackerRuntimeManager.RecordObject(obj);
+
+    return obj;
+}
+
+// Release the reference on all internally held tracker objects
+extern "C" DLL_EXPORT void STDMETHODCALLTYPE ReleaseAllTrackerObjects()
+{
+    TrackerRuntimeManager.ReleaseObjects();
+}
+
+extern "C" DLL_EXPORT int STDMETHODCALLTYPE Trigger_NotifyEndOfReferenceTrackingOnThread()
+{
+    return TrackerRuntimeManager.NotifyEndOfReferenceTrackingOnThread();
+}
diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/Program.cs
new file mode 100644 (file)
index 0000000..e85f87a
--- /dev/null
@@ -0,0 +1,517 @@
+// 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
+{
+    using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Runtime.CompilerServices;
+    using System.Runtime.InteropServices;
+
+    using TestLibrary;
+
+    class Program
+    {
+        //
+        // Managed object with native wrapper definition.
+        //
+        [Guid("447BB9ED-DA48-4ABC-8963-5BB5C3E0AA09")]
+        interface ITest
+        {
+            void SetValue(int i);
+        }
+
+        class Test : ITest
+        {
+            public static int InstanceCount = 0;
+
+            private int value = -1;
+            public Test() { InstanceCount++; }
+            ~Test() { InstanceCount--; }
+
+            public void SetValue(int i) => this.value = i;
+            public int GetValue() => this.value;
+        }
+
+        public struct IUnknownVtbl
+        {
+            public IntPtr QueryInterface;
+            public IntPtr AddRef;
+            public IntPtr Release;
+        }
+
+        public struct ITestVtbl
+        {
+            public IUnknownVtbl IUnknownImpl;
+            public IntPtr SetValue;
+
+            public delegate int _SetValue(IntPtr thisPtr, int i);
+            public static _SetValue pSetValue = new _SetValue(SetValueInternal);
+
+            public static int SetValueInternal(IntPtr dispatchPtr, int i)
+            {
+                unsafe
+                {
+                    try
+                    {
+                        ComWrappers.ComInterfaceDispatch.GetInstance<ITest>((ComWrappers.ComInterfaceDispatch*)dispatchPtr).SetValue(i);
+                    }
+                    catch (Exception e)
+                    {
+                        return e.HResult;
+                    }
+                }
+                return 0; // S_OK;
+            }
+        }
+
+        //
+        // Native interface defintion with managed wrapper for tracker object
+        //
+        struct MockReferenceTrackerRuntime
+        {
+            [DllImport(nameof(MockReferenceTrackerRuntime))]
+            extern public static IntPtr CreateTrackerObject();
+
+            [DllImport(nameof(MockReferenceTrackerRuntime))]
+            extern public static void ReleaseAllTrackerObjects();
+
+            [DllImport(nameof(MockReferenceTrackerRuntime))]
+            extern public static int Trigger_NotifyEndOfReferenceTrackingOnThread();
+        }
+
+        [Guid("42951130-245C-485E-B60B-4ED4254256F8")]
+        public interface ITrackerObject
+        {
+            int AddObjectRef(IntPtr obj);
+            void DropObjectRef(int id);
+        };
+
+        public struct VtblPtr
+        {
+            public IntPtr Vtbl;
+        }
+
+        public class ITrackerObjectWrapper : ITrackerObject
+        {
+            private struct ITrackerObjectWrapperVtbl
+            {
+                public IntPtr QueryInterface;
+                public _AddRef AddRef;
+                public _Release Release;
+                public _AddObjectRef AddObjectRef;
+                public _DropObjectRef DropObjectRef;
+            }
+
+            private delegate int _AddRef(IntPtr This);
+            private delegate int _Release(IntPtr This);
+            private delegate int _AddObjectRef(IntPtr This, IntPtr obj, out int id);
+            private delegate int _DropObjectRef(IntPtr This, int id);
+
+            private readonly IntPtr instance;
+            private readonly ITrackerObjectWrapperVtbl vtable;
+
+            public ITrackerObjectWrapper(IntPtr instance)
+            {
+                var inst = Marshal.PtrToStructure<VtblPtr>(instance);
+                this.vtable = Marshal.PtrToStructure<ITrackerObjectWrapperVtbl>(inst.Vtbl);
+                this.instance = instance;
+            }
+
+            ~ITrackerObjectWrapper()
+            {
+                if (this.instance != IntPtr.Zero)
+                {
+                    this.vtable.Release(this.instance);
+                }
+            }
+
+            public int AddObjectRef(IntPtr obj)
+            {
+                int id;
+                int hr = this.vtable.AddObjectRef(this.instance, obj, out id);
+                if (hr != 0)
+                {
+                    throw new COMException($"{nameof(AddObjectRef)}", hr);
+                }
+
+                return id;
+            }
+
+            public void DropObjectRef(int id)
+            {
+                int hr = this.vtable.DropObjectRef(this.instance, id);
+                if (hr != 0)
+                {
+                    throw new COMException($"{nameof(DropObjectRef)}", hr);
+                }
+            }
+        }
+
+        class TestComWrappers : ComWrappers
+        {
+            public static readonly TestComWrappers Global = new TestComWrappers();
+
+            protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
+            {
+                Assert.IsTrue(obj is Test);
+
+                IntPtr fpQueryInteface = default;
+                IntPtr fpAddRef = default;
+                IntPtr fpRelease = default;
+                ComWrappers.GetIUnknownImpl(out fpQueryInteface, out fpAddRef, out fpRelease);
+
+                var vtbl = new ITestVtbl()
+                {
+                    IUnknownImpl = new IUnknownVtbl()
+                    {
+                        QueryInterface = fpQueryInteface,
+                        AddRef = fpAddRef,
+                        Release = fpRelease
+                    },
+                    SetValue = Marshal.GetFunctionPointerForDelegate(ITestVtbl.pSetValue)
+                };
+                var vtblRaw = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ITestVtbl), sizeof(ITestVtbl));
+                Marshal.StructureToPtr(vtbl, vtblRaw, false);
+
+                var entryRaw = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(ITestVtbl), sizeof(ComInterfaceEntry));
+                entryRaw->IID = typeof(ITest).GUID;
+                entryRaw->Vtable = vtblRaw;
+
+                count = 1;
+                return entryRaw;
+            }
+
+            protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flag)
+            {
+                var iid = typeof(ITrackerObject).GUID;
+                IntPtr iTestComObject;
+                int hr = Marshal.QueryInterface(externalComObject, ref iid, out iTestComObject);
+                Assert.AreEqual(hr, 0);
+
+                return new ITrackerObjectWrapper(iTestComObject);
+            }
+
+            public const int ReleaseObjectsCallAck = unchecked((int)-1);
+
+            protected override void ReleaseObjects(IEnumerable objects)
+            {
+                throw new Exception() { HResult = ReleaseObjectsCallAck };
+            }
+
+            public static void ValidateIUnknownImpls()
+            {
+                Console.WriteLine($"Running {nameof(ValidateIUnknownImpls)}...");
+
+                ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease);
+
+                Assert.AreNotEqual(fpQueryInteface, IntPtr.Zero);
+                Assert.AreNotEqual(fpAddRef, IntPtr.Zero);
+                Assert.AreNotEqual(fpRelease, IntPtr.Zero);
+            }
+        }
+
+        static void ValidateComInterfaceCreation()
+        {
+            Console.WriteLine($"Running {nameof(ValidateComInterfaceCreation)}...");
+
+            var testObj = new Test();
+
+            var wrappers = new TestComWrappers();
+
+            // Allocate a wrapper for the object
+            IntPtr comWrapper = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport);
+            Assert.AreNotEqual(comWrapper, IntPtr.Zero);
+
+            // Get a wrapper for an object and verify it is the same one.
+            IntPtr comWrapperMaybe = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport);
+            Assert.AreEqual(comWrapper, comWrapperMaybe);
+
+            // Release the wrapper
+            int count = Marshal.Release(comWrapper);
+            Assert.AreEqual(count, 1);
+            count = Marshal.Release(comWrapperMaybe);
+            Assert.AreEqual(count, 0);
+
+            // Create a new wrapper
+            IntPtr comWrapperNew = wrappers.GetOrCreateComInterfaceForObject(testObj, CreateComInterfaceFlags.TrackerSupport);
+
+            // Once a wrapper is created for a managed object it is always present
+            Assert.AreEqual(comWrapperNew, comWrapper);
+
+            // Release the new wrapper
+            count = Marshal.Release(comWrapperNew);
+            Assert.AreEqual(count, 0);
+        }
+
+        static void ValidateCreateObjectCachingScenario()
+        {
+            Console.WriteLine($"Running {nameof(ValidateCreateObjectCachingScenario)}...");
+
+            var cw = new TestComWrappers();
+
+            // Get an object from a tracker runtime.
+            IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+
+            var trackerObj1 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject);
+            var trackerObj2 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject);
+            Assert.AreEqual(trackerObj1, trackerObj2);
+
+            // Ownership has been transferred to the wrapper.
+            Marshal.Release(trackerObjRaw);
+
+            var trackerObj3 = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance);
+            Assert.AreNotEqual(trackerObj1, trackerObj3);
+        }
+
+        static void ValidatePrecreatedExternalWrapper()
+        {
+            Console.WriteLine($"Running {nameof(ValidatePrecreatedExternalWrapper)}...");
+
+            var cw = new TestComWrappers();
+
+            // Get an object from a tracker runtime.
+            IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+
+            // Manually create a wrapper
+            var iid = typeof(ITrackerObject).GUID;
+            IntPtr iTestComObject;
+            int hr = Marshal.QueryInterface(trackerObjRaw, ref iid, out iTestComObject);
+            Assert.AreEqual(hr, 0);
+            var nativeWrapper = new ITrackerObjectWrapper(iTestComObject);
+
+            // Register wrapper, but supply the wrapper.
+            var nativeWrapper2 = (ITrackerObjectWrapper)cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject, nativeWrapper);
+            Assert.AreEqual(nativeWrapper, nativeWrapper2);
+
+            // Ownership has been transferred to the wrapper.
+            Marshal.Release(trackerObjRaw);
+
+            // Validate reuse of a wrapper fails.
+            IntPtr trackerObjRaw2 = MockReferenceTrackerRuntime.CreateTrackerObject();
+            Assert.Throws<NotSupportedException>(
+                () =>
+                {
+                    cw.GetOrRegisterObjectForComInstance(trackerObjRaw2, CreateObjectFlags.None, nativeWrapper2);
+                });
+            Marshal.Release(trackerObjRaw2);
+
+            // Validate passing null wrapper fails.
+            Assert.Throws<ArgumentNullException>(
+                () =>
+                {
+                    cw.GetOrRegisterObjectForComInstance(trackerObjRaw, CreateObjectFlags.None, null);
+                });
+        }
+
+        static void ValidateIUnknownImpls()
+            => TestComWrappers.ValidateIUnknownImpls();
+
+        class BadComWrappers : ComWrappers
+        {
+            public enum FailureMode
+            {
+                ReturnInvalid,
+                ThrowException,
+            }
+
+            public const int ExceptionErrorCode = 0x27;
+
+            public FailureMode ComputeVtablesMode { get; set; }
+            public FailureMode CreateObjectMode { get; set; }
+
+            protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
+            {
+                switch (ComputeVtablesMode)
+                {
+                    case FailureMode.ReturnInvalid:
+                    {
+                        count = -1;
+                        return null;
+                    }
+                    case FailureMode.ThrowException:
+                        throw new Exception() { HResult = ExceptionErrorCode };
+                    default:
+                        Assert.Fail("Invalid failure mode");
+                        throw new Exception("UNREACHABLE");
+                }
+            }
+
+            protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags)
+            {
+                switch (CreateObjectMode)
+                {
+                    case FailureMode.ReturnInvalid:
+                        return null;
+                    case FailureMode.ThrowException:
+                        throw new Exception() { HResult = ExceptionErrorCode };
+                    default:
+                        Assert.Fail("Invalid failure mode");
+                        throw new Exception("UNREACHABLE");
+                }
+            }
+
+            protected override void ReleaseObjects(IEnumerable objects)
+            {
+                throw new NotSupportedException();
+            }
+        }
+
+        static void ValidateBadComWrapperImpl()
+        {
+            Console.WriteLine($"Running {nameof(ValidateBadComWrapperImpl)}...");
+
+            var wrapper = new BadComWrappers();
+
+            Assert.Throws<ArgumentException>(
+                () =>
+                {
+                    wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ReturnInvalid;
+                    wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None);
+                });
+
+            try
+            {
+                wrapper.ComputeVtablesMode = BadComWrappers.FailureMode.ThrowException;
+                wrapper.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.None);
+            }
+            catch (Exception e)
+            {
+                Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult);
+            }
+
+            IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+
+            Assert.Throws<ArgumentNullException>(
+                () =>
+                {
+                    wrapper.CreateObjectMode = BadComWrappers.FailureMode.ReturnInvalid;
+                    wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None);
+                });
+
+            try
+            {
+                wrapper.CreateObjectMode = BadComWrappers.FailureMode.ThrowException;
+                wrapper.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.None);
+            }
+            catch (Exception e)
+            {
+                Assert.AreEqual(BadComWrappers.ExceptionErrorCode, e.HResult);
+            }
+
+            Marshal.Release(trackerObjRaw);
+        }
+
+        static void ValidateRuntimeTrackerScenario()
+        {
+            Console.WriteLine($"Running {nameof(ValidateRuntimeTrackerScenario)}...");
+
+            var cw = new TestComWrappers();
+
+            // Get an object from a tracker runtime.
+            IntPtr trackerObjRaw = MockReferenceTrackerRuntime.CreateTrackerObject();
+
+            // Create a managed wrapper for the native object.
+            var trackerObj = (ITrackerObjectWrapper)cw.GetOrCreateObjectForComInstance(trackerObjRaw, CreateObjectFlags.TrackerObject);
+
+            // Ownership has been transferred to the wrapper.
+            Marshal.Release(trackerObjRaw);
+
+            var testWrapperIds = new List<int>();
+            for (int i = 0; i < 1000; ++i)
+            {
+                // Create a native wrapper for the managed object.
+                IntPtr testWrapper = cw.GetOrCreateComInterfaceForObject(new Test(), CreateComInterfaceFlags.TrackerSupport);
+
+                // Pass the managed object to the native object.
+                int id = trackerObj.AddObjectRef(testWrapper);
+
+                // Retain the managed object wrapper ptr.
+                testWrapperIds.Add(id);
+            }
+
+            Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount);
+
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+
+            Assert.IsTrue(testWrapperIds.Count <= Test.InstanceCount);
+
+            // Remove the managed object ref from the native object.
+            foreach (int id in testWrapperIds)
+            {
+                trackerObj.DropObjectRef(id);
+            }
+
+            testWrapperIds.Clear();
+
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+            GC.Collect();
+        }
+
+        static void ValidateGlobalInstanceScenarios()
+        {
+            Console.WriteLine($"Running {nameof(ValidateGlobalInstanceScenarios)}...");
+            Console.WriteLine($"Validate RegisterAsGlobalInstance()...");
+
+            var wrappers1 = TestComWrappers.Global;
+            wrappers1.RegisterAsGlobalInstance();
+
+            Assert.Throws<InvalidOperationException>(
+                () =>
+                {
+                    wrappers1.RegisterAsGlobalInstance();
+                }, "Should not be able to re-register for global ComWrappers");
+
+            var wrappers2 = new TestComWrappers();
+            Assert.Throws<InvalidOperationException>(
+                () =>
+                {
+                    wrappers2.RegisterAsGlobalInstance();
+                }, "Should not be able to reset for global ComWrappers");
+
+            Console.WriteLine($"Validate NotifyEndOfReferenceTrackingOnThread()...");
+
+            int hr;
+            var cw = TestComWrappers.Global;
+
+            // Trigger the thread lifetime end API and verify the callback occurs.
+            hr = MockReferenceTrackerRuntime.Trigger_NotifyEndOfReferenceTrackingOnThread();
+            Assert.AreEqual(TestComWrappers.ReleaseObjectsCallAck, hr);
+        }
+
+        static int Main(string[] doNotUse)
+        {
+            try
+            {
+                ValidateComInterfaceCreation();
+                ValidateCreateObjectCachingScenario();
+                ValidatePrecreatedExternalWrapper();
+                ValidateIUnknownImpls();
+                ValidateBadComWrapperImpl();
+                ValidateRuntimeTrackerScenario();
+
+                // Perform all global impacting test scenarios last to
+                // avoid polluting non-global tests.
+                ValidateGlobalInstanceScenarios();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
+
index 2ff3f88..3e0fccc 100644 (file)
@@ -68,61 +68,9 @@ private:
     }
 };
 
-template<typename T>
-struct ComSmartPtr
-{
-    ComSmartPtr()
-        : p{}
-    { }
-
-    ComSmartPtr(_In_ const ComSmartPtr &) = delete;
-    ComSmartPtr(_Inout_ ComSmartPtr &&) = delete;
-
-    ComSmartPtr& operator=(_In_ const ComSmartPtr &) = delete;
-    ComSmartPtr& operator=(_Inout_ ComSmartPtr &&) = delete;
-
-    ~ComSmartPtr()
-    {
-        if (p != nullptr)
-            p->Release();
-    }
-
-    operator T*()
-    {
-        return p;
-    }
-
-    T** operator&()
-    {
-        return &p;
-    }
-
-    T* operator->()
-    {
-        return p;
-    }
-
-    void Attach(_In_opt_ T *t)
-    {
-        if (p != nullptr)
-            p->Release();
-
-        p = t;
-    }
-
-    T *Detach()
-    {
-        T *tmp = p;
-        p = nullptr;
-        return tmp;
-    }
-
-    T *p;
-};
+#include <ComHelpers.h>
 
 #ifndef COM_CLIENT
-    #include <ComHelpers.h>
-
     #define DEF_FUNC(n) virtual COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE n
 
     #include "NumericTesting.h"
index f6a35d3..c90ff7a 100644 (file)
@@ -331,3 +331,76 @@ public: // IUnknown
 
     DEFINE_REF_COUNTING();
 };
+
+template<typename T>
+struct ComSmartPtr
+{
+    T* p;
+
+    ComSmartPtr()
+        : p{ nullptr }
+    { }
+
+    ComSmartPtr(_In_ T* t)
+        : p{ t }
+    {
+        if (p != nullptr)
+            (void)p->AddRef();
+    }
+
+    ComSmartPtr(_In_ const ComSmartPtr&) = delete;
+
+    ComSmartPtr(_Inout_ ComSmartPtr&& other)
+        : p{ other.Detach() }
+    { }
+
+    ~ComSmartPtr()
+    {
+        Release();
+    }
+
+    ComSmartPtr& operator=(_In_ const ComSmartPtr&) = delete;
+
+    ComSmartPtr& operator=(_Inout_ ComSmartPtr&& other)
+    {
+        Attach(other.Detach());
+        return (*this);
+    }
+
+    operator T*()
+    {
+        return p;
+    }
+
+    T** operator&()
+    {
+        return &p;
+    }
+
+    T* operator->()
+    {
+        return p;
+    }
+
+    void Attach(_In_opt_ T* t) noexcept
+    {
+        Release();
+        p = t;
+    }
+
+    T* Detach() noexcept
+    {
+        T* tmp = p;
+        p = nullptr;
+        return tmp;
+    }
+
+    void Release() noexcept
+    {
+        if (p != nullptr)
+        {
+            (void)p->Release();
+            p = nullptr;
+        }
+    }
+};
index 40b92b7..faf6504 100644 (file)
@@ -5,57 +5,49 @@
 using System;
 using System.Runtime.CompilerServices;
 
-namespace GCD
+class GCD
 {
-    /// <summary>
-    /// Summary description for Class1.
-    /// </summary>
-    class GCD
+    private int _val = -2;
+    private int _exitcode = -1;
+    public GCD() {}
+    public int GetExitCode(){ return _exitcode;}
+    public void g ()
     {
-        private int _val = -2;
-        private int _exitcode = -1;
-        public GCD() {}
-        public int GetExitCode(){ return _exitcode;}
-        public void g ()
-        {
-            throw new System.Exception("TryCode test");
-        }
-        public void TryCode0 (object obj)
-        {
-            _val = (int)obj;
-            g();
-        }
-        public void CleanupCode0 (object obj, bool excpThrown)
+        throw new System.Exception("TryCode test");
+    }
+    public void TryCode0 (object obj)
+    {
+        _val = (int)obj;
+        g();
+    }
+    public void CleanupCode0 (object obj, bool excpThrown)
+    {
+        if(excpThrown && ((int)obj == _val))
         {
-            if(excpThrown && ((int)obj == _val))
-            {
-                _exitcode = 100;
-            }
+            _exitcode = 100;
         }
     }
+}
 
-
-    class GCDTest 
+class ExecuteCodeWithGuaranteedCleanupTest
+{
+    public static void Run()
     {
-        /// <summary>
-        /// The main entry point for the application.
-        /// </summary>
-            static int Main(string[] args)
-            {
-                GCD gcd = new GCD();
-                RuntimeHelpers.TryCode t = new RuntimeHelpers.TryCode(gcd.TryCode0);
-                RuntimeHelpers.CleanupCode c = new RuntimeHelpers.CleanupCode(gcd.CleanupCode0);
-                int val = 21;
-                try
-                {
-                    RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(t, c, val);
-                }
-                catch (Exception Ex)
-                {
+        GCD gcd = new GCD();
+        RuntimeHelpers.TryCode t = new RuntimeHelpers.TryCode(gcd.TryCode0);
+        RuntimeHelpers.CleanupCode c = new RuntimeHelpers.CleanupCode(gcd.CleanupCode0);
+        int val = 21;
+        try
+        {
+            RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(t, c, val);
+        }
+        catch (Exception Ex)
+        {
 
-                }
+        }
 
-                return gcd.GetExitCode();
-            }
+        int res = gcd.GetExitCode();
+        if (res != 100)
+            throw new Exception($"{nameof(ExecuteCodeWithGuaranteedCleanupTest)} failed. Result: {res}");
     }
 }
diff --git a/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs b/src/coreclr/tests/src/baseservices/compilerservices/RuntimeHelpers/RuntimeHelpersTests.cs
new file mode 100644 (file)
index 0000000..5b667a7
--- /dev/null
@@ -0,0 +1,23 @@
+// 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.
+//
+using System;
+
+class RuntimeHelpersTests
+{
+    static int Main(string[] args)
+    {
+        try
+        {
+            ExecuteCodeWithGuaranteedCleanupTest.Run();
+        }
+        catch (Exception e)
+        {
+            Console.WriteLine(e);
+            return 101;
+        }
+
+        return 100;
+    }
+}
@@ -5,9 +5,9 @@
     <CLRTestPriority>1</CLRTestPriority>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="ExecuteCodeWithGuaranteedCleanup.cs" />
+    <Compile Include="*.cs" />
   </ItemGroup>
   <ItemGroup>
-    <NoWarn Include="42016,42020,42025,42024" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
   </ItemGroup>
 </Project>
index d3e1a96..0639dc7 100644 (file)
   <data name="InvalidOperation_MultipleComUnRegFunctions" xml:space="preserve">
     <value>Type '{0}' has more than one COM unregistration function.</value>
   </data>
+  <data name="InvalidOperation_ResetGlobalComWrappersInstance" xml:space="preserve">
+    <value>Attempt to update previously set global instance.</value>
+  </data>
   <data name="Utf8String_CallbackProvidedMalformedData" xml:space="preserve">
     <value>The callback populated its buffer with ill-formed UTF-8 data. Callbacks are required to populate the buffer only with well-formed UTF-8 data.</value>
   </data>
index 62e69dd..3ee3d97 100644 (file)
   <ItemGroup Condition="'$(FeatureAsyncCausalityTracer)' != 'true'">
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\AsyncCausalityTracer.Noop.cs" />
   </ItemGroup>
+  <ItemGroup Condition="'$(FeatureComWrappers)' != 'true'">
+    <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComWrappers.PlatformNotSupported.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(FeatureCominterop)' != 'true'">
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.NoCom.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComEventsHelpers.NoCom.cs" />
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.PlatformNotSupported.cs
new file mode 100644 (file)
index 0000000..77e8b35
--- /dev/null
@@ -0,0 +1,76 @@
+// 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.
+
+using System;
+using System.Collections;
+
+namespace System.Runtime.InteropServices
+{
+    [Flags]
+    public enum CreateComInterfaceFlags
+    {
+        None = 0,
+        CallerDefinedIUnknown = 1,
+        TrackerSupport = 2,
+    }
+
+    [Flags]
+    public enum CreateObjectFlags
+    {
+        None = 0,
+        TrackerObject = 1,
+        UniqueInstance = 2,
+    }
+
+    [CLSCompliant(false)]
+    public abstract class ComWrappers
+    {
+        public struct ComInterfaceEntry
+        {
+            public Guid IID;
+            public IntPtr Vtable;
+        }
+
+        public struct ComInterfaceDispatch
+        {
+            public IntPtr Vtable;
+
+            public static unsafe T GetInstance<T>(ComInterfaceDispatch* dispatchPtr) where T : class
+            {
+                throw new PlatformNotSupportedException();
+            }
+        }
+
+        public IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
+
+        public object GetOrCreateObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        protected abstract object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags);
+
+        public object GetOrRegisterObjectForComInstance(IntPtr externalComObject, CreateObjectFlags flags, object wrapper)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        protected abstract void ReleaseObjects(IEnumerable objects);
+
+        public void RegisterAsGlobalInstance()
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        protected static void GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease)
+        {
+            throw new PlatformNotSupportedException();
+        }
+    }
+}
\ No newline at end of file
index aee1cf8..92ddefd 100644 (file)
@@ -983,6 +983,42 @@ namespace System.Runtime.InteropServices
         public VariantWrapper(object? obj) { }
         public object? WrappedObject { get { throw null; } }
     }
+    [System.FlagsAttribute]
+    public enum CreateComInterfaceFlags
+    {
+        None = 0,
+        CallerDefinedIUnknown = 1,
+        TrackerSupport = 2,
+    }
+    [System.FlagsAttribute]
+    public enum CreateObjectFlags
+    {
+        None = 0,
+        TrackerObject = 1,
+        UniqueInstance = 2,
+    }
+    [System.CLSCompliantAttribute(false)]
+    public abstract class ComWrappers
+    {
+        public struct ComInterfaceEntry
+        {
+            public System.Guid IID;
+            public System.IntPtr Vtable;
+        }
+        public struct ComInterfaceDispatch
+        {
+            public System.IntPtr Vtable;
+            public static unsafe T GetInstance<T>(ComInterfaceDispatch* dispatchPtr) where T : class { throw null; }
+        }
+        public System.IntPtr GetOrCreateComInterfaceForObject(object instance, CreateComInterfaceFlags flags) { throw null; }
+        protected unsafe abstract ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count);
+        public object GetOrCreateObjectForComInstance(System.IntPtr externalComObject, CreateObjectFlags flags) { throw null; }
+        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() { }
+        protected static void GetIUnknownImpl(out System.IntPtr fpQueryInterface, out System.IntPtr fpAddRef, out System.IntPtr fpRelease) { throw null; }
+    }
 }
 namespace System.Runtime.InteropServices.ComTypes
 {
index 467d544..0f0362a 100644 (file)
@@ -9025,6 +9025,7 @@ namespace System.Runtime.CompilerServices
     public static partial class RuntimeHelpers
     {
         public static int OffsetToStringData { get { throw null; } }
+        public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) { throw null; }
         public static void EnsureSufficientExecutionStack() { }
         public static new bool Equals(object? o1, object? o2) { throw null; }
         public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServices.RuntimeHelpers.TryCode code, System.Runtime.CompilerServices.RuntimeHelpers.CleanupCode backoutCode, object? userData) { }
index bd4054d..dea93fa 100644 (file)
@@ -264,6 +264,22 @@ namespace System.Runtime.CompilerServices.Tests
             Assert.Throws<ArgumentOutOfRangeException>(() => { int [] array = RuntimeHelpers.GetSubArray(a, range); });
         }
 
+        [Fact]
+        [SkipOnMono("Not presently implemented on Mono")]
+        public static void AllocateTypeAssociatedMemoryInvalidArguments()
+        {
+            Assert.Throws<ArgumentException>(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(null, 10); });
+            Assert.Throws<ArgumentOutOfRangeException>(() => { RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(RuntimeHelpersTests), -1); });
+        }
+
+        [Fact]
+        [SkipOnMono("Not presently implemented on Mono")]
+        public static void AllocateTypeAssociatedMemoryValidArguments()
+        {
+            IntPtr memory = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(RuntimeHelpersTests), 32);
+            Assert.NotEqual(memory, IntPtr.Zero);
+        }
+
         [StructLayoutAttribute(LayoutKind.Sequential)]
         private struct StructWithoutReferences
         {
index 79a6a69..e054eee 100644 (file)
@@ -99,6 +99,11 @@ namespace System.Runtime.CompilerServices
                        RunModuleConstructor (module.Value);
                }
 
+               public static IntPtr AllocateTypeAssociatedMemory (Type type, int size)
+               {
+                       throw new PlatformNotSupportedException ();
+               }
+
                [Intrinsic]
                public static bool IsReferenceOrContainsReferences<T> () => IsReferenceOrContainsReferences<T> ();