From 16b7db27c98dce5b4d15894db13acd629b8fea62 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 7 May 2020 19:20:23 -0700 Subject: [PATCH] Add support for ComWrappers-based RCWs to the special native WeakReference support. (#35819) * Add support for ComWrappers-based RCWs to the special native WeakReference support. Rename the WinRT weak reference handle to "native COM weak reference" handle. * Fix line endings. * Fix line endings (try 2). * Revert change to prebuilt idl * Try updating prebuilt again. * Remove accidental duplicate of the test. * PR feedback. * PR Feedback. * React to global ComWrappers changes. * Add back WinRT enum member to prebuilt idl. * Add back old enum member to idl for backcompat. * Change definition of enum member to explicitly assign the same value. * Code cleanup and go down a non-allocating route when possible. * Switch to preemptive mode for the QI call. * Fix contracts * Add a check before calling GetComWeakReference so we only call it when the object has interop info attached. * Apply early check to WeakReference as well. * Make sure we only make one call to PassiveGetSyncBlock instead of 2. * PR Feedback. * ComWrappersNative::GetIdentityForObject can trigger GC since we transition to and from pre-emptive when calling into the external QI. --- .../System/Runtime/InteropServices/ComWrappers.cs | 9 +- src/coreclr/src/debug/daccess/daccess.cpp | 2 +- src/coreclr/src/debug/daccess/dacdbiimpl.cpp | 8 +- src/coreclr/src/debug/daccess/request.cpp | 4 +- src/coreclr/src/dlls/mscorrc/mscorrc.rc | 2 +- src/coreclr/src/dlls/mscorrc/resource.h | 2 +- src/coreclr/src/gc/gchandletable.cpp | 2 +- src/coreclr/src/gc/gcinterface.h | 8 +- src/coreclr/src/gc/handletable.cpp | 6 +- src/coreclr/src/gc/objecthandle.cpp | 16 +- src/coreclr/src/inc/cordebug.idl | 3 +- src/coreclr/src/pal/prebuilt/inc/cordebug.h | 3 +- src/coreclr/src/vm/appdomain.hpp | 4 +- src/coreclr/src/vm/ecalllist.h | 1 + src/coreclr/src/vm/gchandleutilities.h | 10 +- src/coreclr/src/vm/interoplibinterface.cpp | 115 ++++++++++++- src/coreclr/src/vm/interoplibinterface.h | 22 +++ src/coreclr/src/vm/marshalnative.cpp | 2 +- src/coreclr/src/vm/runtimehandles.cpp | 2 +- src/coreclr/src/vm/weakreferencenative.cpp | 186 ++++++++++++--------- src/coreclr/tests/src/Interop/CMakeLists.txt | 1 + .../COM/ComWrappers/WeakReference/CMakeLists.txt | 10 ++ .../WeakReference/WeakReferenceNative.cpp | 124 ++++++++++++++ .../ComWrappers/WeakReference/WeakReferenceTest.cs | 137 +++++++++++++++ .../WeakReference/WeakReferenceTest.csproj | 24 +++ src/coreclr/tests/src/Interop/common/ComHelpers.h | 6 + 26 files changed, 587 insertions(+), 122 deletions(-) create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/CMakeLists.txt create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceNative.cpp create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.cs create mode 100644 src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.csproj 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 index 323e60a..8c37701 100644 --- 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 @@ -331,8 +331,15 @@ namespace System.Runtime.InteropServices { throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalComWrappersInstance); } + + SetGlobalInstanceRegisteredForTrackerSupport(); } + + [DllImport(RuntimeHelpers.QCall)] + [SuppressGCTransition] + private static extern void SetGlobalInstanceRegisteredForTrackerSupport(); + /// /// Register a instance to be used as the global instance for marshalling in the runtime. /// @@ -390,4 +397,4 @@ namespace System.Runtime.InteropServices return (int)customQueryInterface.GetInterface(ref iid, out ppObject); } } -} \ No newline at end of file +} diff --git a/src/coreclr/src/debug/daccess/daccess.cpp b/src/coreclr/src/debug/daccess/daccess.cpp index a05d4f09d..4441145 100644 --- a/src/coreclr/src/debug/daccess/daccess.cpp +++ b/src/coreclr/src/debug/daccess/daccess.cpp @@ -8252,7 +8252,7 @@ void CALLBACK DacHandleWalker::EnumCallbackSOS(PTR_UNCHECKED_OBJECTREF handle, u if (param->Type == HNDTYPE_DEPENDENT) data.Secondary = GetDependentHandleSecondary(handle.GetAddr()).GetAddr(); #ifdef FEATURE_COMINTEROP - else if (param->Type == HNDTYPE_WEAK_WINRT) + else if (param->Type == HNDTYPE_WEAK_NATIVE_COM) data.Secondary = HndGetHandleExtraInfo(handle.GetAddr()); #endif // FEATURE_COMINTEROP else diff --git a/src/coreclr/src/debug/daccess/dacdbiimpl.cpp b/src/coreclr/src/debug/daccess/dacdbiimpl.cpp index 905698a..e315265 100644 --- a/src/coreclr/src/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/src/debug/daccess/dacdbiimpl.cpp @@ -7491,8 +7491,8 @@ UINT32 DacRefWalker::GetHandleWalkerMask() if ((mHandleMask & CorHandleWeakRefCount) || (mHandleMask & CorHandleStrongRefCount)) result |= (1 << HNDTYPE_REFCOUNTED); - if (mHandleMask & CorHandleWeakWinRT) - result |= (1 << HNDTYPE_WEAK_WINRT); + if (mHandleMask & CorHandleWeakNativeCom) + result |= (1 << HNDTYPE_WEAK_NATIVE_COM); #endif // FEATURE_COMINTEROP if (mHandleMask & CorHandleStrongDependent) @@ -7667,8 +7667,8 @@ void CALLBACK DacHandleWalker::EnumCallbackDac(PTR_UNCHECKED_OBJECTREF handle, u data.i64ExtraData = refCnt; break; - case HNDTYPE_WEAK_WINRT: - data.dwType = (DWORD)CorHandleWeakWinRT; + case HNDTYPE_WEAK_NATIVE_COM: + data.dwType = (DWORD)CorHandleWeakNativeCom; break; #endif diff --git a/src/coreclr/src/debug/daccess/request.cpp b/src/coreclr/src/debug/daccess/request.cpp index 2be663e..79880ff 100644 --- a/src/coreclr/src/debug/daccess/request.cpp +++ b/src/coreclr/src/debug/daccess/request.cpp @@ -3253,7 +3253,7 @@ HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum) unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT, HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, #ifdef FEATURE_COMINTEROP - HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_WINRT + HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_NATIVE_COM #endif }; @@ -3291,7 +3291,7 @@ HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppH unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT, HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, #ifdef FEATURE_COMINTEROP - HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_WINRT + HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_NATIVE_COM #endif }; diff --git a/src/coreclr/src/dlls/mscorrc/mscorrc.rc b/src/coreclr/src/dlls/mscorrc/mscorrc.rc index 8a7410e..4573e20 100644 --- a/src/coreclr/src/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/src/dlls/mscorrc/mscorrc.rc @@ -344,7 +344,7 @@ BEGIN IDS_EE_WINRT_NOT_FACTORY_FOR_TYPE "Windows Runtime factory '%1' is not a factory for Windows Runtime type '%2'." IDS_EE_WINRT_INVALID_FACTORY_FOR_TYPE "Windows Runtime type '%1' has a invalid Windows Runtime factory" IDS_EE_CANNOTCAST_NOMARSHAL "The Windows Runtime Object can only be used in the threading context where it was created, because it implements INoMarshal or has MarshalingBehaviorAttribute(MarshalingType.None) set." - IDS_EE_WINRT_WEAKREF_BAD_TYPE "The object resolved by a native IWeakReference has an incompatible type for its managed WeakReference instance.\r\nExpected WeakReference target type: '%1'\r\nNative IWeakReference returned type: '%2'" + IDS_EE_NATIVE_COM_WEAKREF_BAD_TYPE "The object resolved by a native IWeakReference has an incompatible type for its managed WeakReference instance.\r\nExpected WeakReference target type: '%1'\r\nNative IWeakReference returned type: '%2'" #endif // FEATURE_COMINTEROP IDS_EE_INTEROP_CODE_SIZE_COMMENT "Code size" diff --git a/src/coreclr/src/dlls/mscorrc/resource.h b/src/coreclr/src/dlls/mscorrc/resource.h index 342c057..4011efb 100644 --- a/src/coreclr/src/dlls/mscorrc/resource.h +++ b/src/coreclr/src/dlls/mscorrc/resource.h @@ -600,7 +600,7 @@ #ifdef FEATURE_COMINTEROP -#define IDS_EE_WINRT_WEAKREF_BAD_TYPE 0x262e +#define IDS_EE_NATIVE_COM_WEAKREF_BAD_TYPE 0x262e #endif // FEATURE_COMINTEROP #define IDS_EE_BADMARSHAL_TYPE_ANSIBSTR 0x262f diff --git a/src/coreclr/src/gc/gchandletable.cpp b/src/coreclr/src/gc/gchandletable.cpp index b7c4bf2..d3f93b4 100644 --- a/src/coreclr/src/gc/gchandletable.cpp +++ b/src/coreclr/src/gc/gchandletable.cpp @@ -168,7 +168,7 @@ Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE h HandleType GCHandleManager::HandleFetchType(OBJECTHANDLE handle) { uint32_t type = ::HandleFetchType(handle); - assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_WINRT); + assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM); return static_cast(type); } diff --git a/src/coreclr/src/gc/gcinterface.h b/src/coreclr/src/gc/gcinterface.h index 122106c..e9bef6a 100644 --- a/src/coreclr/src/gc/gcinterface.h +++ b/src/coreclr/src/gc/gcinterface.h @@ -422,18 +422,18 @@ typedef enum HNDTYPE_SIZEDREF = 8, /* - * WINRT WEAK HANDLES + * NATIVE WEAK HANDLES * - * WinRT weak reference handles hold two different types of weak handles to any + * Native weak reference handles hold two different types of weak handles to any * RCW with an underlying COM object that implements IWeakReferenceSource. The * object reference itself is a short weak handle to the RCW. In addition an * IWeakReference* to the underlying COM object is stored, allowing the handle * to create a new RCW if the existing RCW is collected. This ensures that any - * code holding onto a WinRT weak reference can always access an RCW to the + * code holding onto a native weak reference can always access an RCW to the * underlying COM object as long as it has not been released by all of its strong * references. */ - HNDTYPE_WEAK_WINRT = 9 + HNDTYPE_WEAK_NATIVE_COM = 9 } HandleType; typedef enum diff --git a/src/coreclr/src/gc/handletable.cpp b/src/coreclr/src/gc/handletable.cpp index 898e843..1b56c74 100644 --- a/src/coreclr/src/gc/handletable.cpp +++ b/src/coreclr/src/gc/handletable.cpp @@ -409,10 +409,10 @@ void HndDestroyHandleOfUnknownType(HHANDLETABLE hTable, OBJECTHANDLE handle) _ASSERTE(handle); #ifdef FEATURE_COMINTEROP - // If we're being asked to destroy a WinRT weak handle, that will cause a leak + // If we're being asked to destroy a native COM weak handle, that will cause a leak // of the IWeakReference* that it holds in its extra data. Instead of using this - // API use DestroyWinRTWeakHandle instead. - _ASSERTE(HandleFetchType(handle) != HNDTYPE_WEAK_WINRT); + // API use DestroyNativeComWeakHandle instead. + _ASSERTE(HandleFetchType(handle) != HNDTYPE_WEAK_NATIVE_COM); #endif // FEATURE_COMINTEROP // fetch the type and then free normally diff --git a/src/coreclr/src/gc/objecthandle.cpp b/src/coreclr/src/gc/objecthandle.cpp index 4a4f201..e6a5160 100644 --- a/src/coreclr/src/gc/objecthandle.cpp +++ b/src/coreclr/src/gc/objecthandle.cpp @@ -426,7 +426,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt case HNDTYPE_WEAK_SHORT: case HNDTYPE_WEAK_LONG: #ifdef FEATURE_COMINTEROP - case HNDTYPE_WEAK_WINRT: + case HNDTYPE_WEAK_NATIVE_COM: #endif // FEATURE_COMINTEROP rootFlags |= kEtwGCRootFlagsWeakRef; break; @@ -520,7 +520,7 @@ static const uint32_t s_rgTypeFlags[] = HNDF_EXTRAINFO, // HNDTYPE_DEPENDENT HNDF_NORMAL, // HNDTYPE_ASYNCPINNED HNDF_EXTRAINFO, // HNDTYPE_SIZEDREF - HNDF_EXTRAINFO, // HNDTYPE_WEAK_WINRT + HNDF_EXTRAINFO, // HNDTYPE_WEAK_NATIVE_COM }; int getNumberOfSlots() @@ -1380,7 +1380,7 @@ void Ref_CheckAlive(uint32_t condemned, uint32_t maxgen, uintptr_t lp1) { HNDTYPE_WEAK_SHORT #ifdef FEATURE_COMINTEROP - , HNDTYPE_WEAK_WINRT + , HNDTYPE_WEAK_NATIVE_COM #endif // FEATURE_COMINTEROP }; uint32_t flags = (((ScanContext*) lp1)->concurrent) ? HNDGCF_ASYNC : HNDGCF_NORMAL; @@ -1439,7 +1439,7 @@ void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Re HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK #ifdef FEATURE_COMINTEROP - HNDTYPE_WEAK_WINRT, + HNDTYPE_WEAK_NATIVE_COM, #endif // FEATURE_COMINTEROP HNDTYPE_SIZEDREF, }; @@ -1485,7 +1485,7 @@ void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_sca HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK #ifdef FEATURE_COMINTEROP - HNDTYPE_WEAK_WINRT, + HNDTYPE_WEAK_NATIVE_COM, #endif // FEATURE_COMINTEROP HNDTYPE_PINNED, // HNDTYPE_VARIABLE, @@ -1631,7 +1631,7 @@ void Ref_AgeHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK #ifdef FEATURE_COMINTEROP - HNDTYPE_WEAK_WINRT, + HNDTYPE_WEAK_NATIVE_COM, #endif // FEATURE_COMINTEROP HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, @@ -1674,7 +1674,7 @@ void Ref_RejuvenateHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK #ifdef FEATURE_COMINTEROP - HNDTYPE_WEAK_WINRT, + HNDTYPE_WEAK_NATIVE_COM, #endif // FEATURE_COMINTEROP HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, @@ -1716,7 +1716,7 @@ void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc) HNDTYPE_REFCOUNTED, #endif // FEATURE_COMINTEROP || FEATURE_REDHAWK #ifdef FEATURE_COMINTEROP - HNDTYPE_WEAK_WINRT, + HNDTYPE_WEAK_NATIVE_COM, #endif // FEATURE_COMINTEROP HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF, diff --git a/src/coreclr/src/inc/cordebug.idl b/src/coreclr/src/inc/cordebug.idl index 87e1458..be74536 100644 --- a/src/coreclr/src/inc/cordebug.idl +++ b/src/coreclr/src/inc/cordebug.idl @@ -2567,7 +2567,8 @@ typedef enum CorGCReferenceType CorHandleStrongDependent = 1<<6, CorHandleStrongAsyncPinned = 1<<7, CorHandleStrongSizedByref = 1<<8, - CorHandleWeakWinRT = 1<<9, + CorHandleWeakNativeCom = 1<<9, + CorHandleWeakWinRT = CorHandleWeakNativeCom, CorReferenceStack = 0x80000001, CorReferenceFinalizer = 80000002, diff --git a/src/coreclr/src/pal/prebuilt/inc/cordebug.h b/src/coreclr/src/pal/prebuilt/inc/cordebug.h index 96dc9c0..69c8975 100644 --- a/src/coreclr/src/pal/prebuilt/inc/cordebug.h +++ b/src/coreclr/src/pal/prebuilt/inc/cordebug.h @@ -6376,7 +6376,8 @@ enum CorGCReferenceType CorHandleStrongDependent = ( 1 << 6 ) , CorHandleStrongAsyncPinned = ( 1 << 7 ) , CorHandleStrongSizedByref = ( 1 << 8 ) , - CorHandleWeakWinRT = ( 1 << 9 ) , + CorHandleWeakNativeCom = ( 1 << 9 ) , + CorHandleWeakWinRT = CorHandleWeakNativeCom, CorReferenceStack = 0x80000001, CorReferenceFinalizer = 80000002, CorHandleStrongOnly = 0x1e3, diff --git a/src/coreclr/src/vm/appdomain.hpp b/src/coreclr/src/vm/appdomain.hpp index 0ca41f0..f017b42 100644 --- a/src/coreclr/src/vm/appdomain.hpp +++ b/src/coreclr/src/vm/appdomain.hpp @@ -1110,10 +1110,10 @@ public: return ::CreateRefcountedHandle(m_handleStore, object); } - OBJECTHANDLE CreateWinRTWeakHandle(OBJECTREF object, IWeakReference* pWinRTWeakReference) + OBJECTHANDLE CreateNativeComWeakHandle(OBJECTREF object, IWeakReference* pComWeakReference) { WRAPPER_NO_CONTRACT; - return ::CreateWinRTWeakHandle(m_handleStore, object, pWinRTWeakReference); + return ::CreateNativeComWeakHandle(m_handleStore, object, pComWeakReference); } #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 87aa7b6..155ae2c 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -986,6 +986,7 @@ FCFuncStart(gComWrappersFuncs) QCFuncElement("TryGetOrCreateComInterfaceForObjectInternal", ComWrappersNative::TryGetOrCreateComInterfaceForObject) QCFuncElement("TryGetOrCreateObjectForComInstanceInternal", ComWrappersNative::TryGetOrCreateObjectForComInstance) QCFuncElement("SetGlobalInstanceRegisteredForMarshalling", GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMarshalling) + QCFuncElement("SetGlobalInstanceRegisteredForTrackerSupport", GlobalComWrappersForTrackerSupport::SetGlobalInstanceRegisteredForTrackerSupport) FCFuncEnd() #endif // FEATURE_COMWRAPPERS diff --git a/src/coreclr/src/vm/gchandleutilities.h b/src/coreclr/src/vm/gchandleutilities.h index b875644..d90a2f0 100644 --- a/src/coreclr/src/vm/gchandleutilities.h +++ b/src/coreclr/src/vm/gchandleutilities.h @@ -201,9 +201,9 @@ inline OBJECTHANDLE CreateGlobalRefcountedHandle(OBJECTREF object) // Special handle creation convenience functions #ifdef FEATURE_COMINTEROP -inline OBJECTHANDLE CreateWinRTWeakHandle(IGCHandleStore* store, OBJECTREF object, IWeakReference* pWinRTWeakReference) +inline OBJECTHANDLE CreateNativeComWeakHandle(IGCHandleStore* store, OBJECTREF object, IWeakReference* pComWeakReference) { - OBJECTHANDLE hnd = store->CreateHandleWithExtraInfo(OBJECTREFToObject(object), HNDTYPE_WEAK_WINRT, (void*)pWinRTWeakReference); + OBJECTHANDLE hnd = store->CreateHandleWithExtraInfo(OBJECTREFToObject(object), HNDTYPE_WEAK_NATIVE_COM, (void*)pComWeakReference); if (!hnd) { COMPlusThrowOM(); @@ -363,7 +363,7 @@ inline void DestroyTypedHandle(OBJECTHANDLE handle) } #ifdef FEATURE_COMINTEROP -inline void DestroyWinRTWeakHandle(OBJECTHANDLE handle) +inline void DestroyNativeComWeakHandle(OBJECTHANDLE handle) { CONTRACTL { @@ -375,7 +375,7 @@ inline void DestroyWinRTWeakHandle(OBJECTHANDLE handle) CONTRACTL_END; // Release the WinRT weak reference if we have one. We're assuming that this will not reenter the - // runtime, since if we are pointing at a managed object, we should not be using HNDTYPE_WEAK_WINRT + // runtime, since if we are pointing at a managed object, we should not be using HNDTYPE_WEAK_NATIVE_COM // but rather HNDTYPE_WEAK_SHORT or HNDTYPE_WEAK_LONG. void* pExtraInfo = GCHandleUtilities::GetGCHandleManager()->GetExtraInfoFromHandle(handle); IWeakReference* pWinRTWeakReference = reinterpret_cast(pExtraInfo); @@ -385,7 +385,7 @@ inline void DestroyWinRTWeakHandle(OBJECTHANDLE handle) } DiagHandleDestroyed(handle); - GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfType(handle, HNDTYPE_WEAK_WINRT); + GCHandleUtilities::GetGCHandleManager()->DestroyHandleOfType(handle, HNDTYPE_WEAK_NATIVE_COM); } #endif diff --git a/src/coreclr/src/vm/interoplibinterface.cpp b/src/coreclr/src/vm/interoplibinterface.cpp index 934e0bb..3eaf5ba 100644 --- a/src/coreclr/src/vm/interoplibinterface.cpp +++ b/src/coreclr/src/vm/interoplibinterface.cpp @@ -401,7 +401,8 @@ namespace Volatile ExtObjCxtCache::g_Instance; // Indicator for if a ComWrappers implementation is globally registered - bool g_IsGlobalComWrappersRegistered; + bool g_IsGlobalComWrappersRegisteredForMarshalling; + bool g_IsGlobalComWrappersRegisteredForTrackerSupport; // Defined handle types for the specific object uses. const HandleType InstanceHandleType{ HNDTYPE_STRONG }; @@ -1387,15 +1388,15 @@ void QCALLTYPE GlobalComWrappersForMarshalling::SetGlobalInstanceRegisteredForMa // QCALL contracts are not used here because the managed declaration // uses the SuppressGCTransition attribute - _ASSERTE(!g_IsGlobalComWrappersRegistered); - g_IsGlobalComWrappersRegistered = true; + _ASSERTE(!g_IsGlobalComWrappersRegisteredForMarshalling); + g_IsGlobalComWrappersRegisteredForMarshalling = true; } bool GlobalComWrappersForMarshalling::TryGetOrCreateComInterfaceForObject( _In_ OBJECTREF instance, _Outptr_ void** wrapperRaw) { - if (!g_IsGlobalComWrappersRegistered) + if (!g_IsGlobalComWrappersRegisteredForMarshalling) return false; // Switch to Cooperative mode since object references @@ -1420,7 +1421,7 @@ bool GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance( _In_ INT32 objFromComIPFlags, _Out_ OBJECTREF* objRef) { - if (!g_IsGlobalComWrappersRegistered) + if (!g_IsGlobalComWrappersRegisteredForMarshalling) return false; // Determine the true identity of the object @@ -1452,6 +1453,110 @@ bool GlobalComWrappersForMarshalling::TryGetOrCreateObjectForComInstance( } } +void QCALLTYPE GlobalComWrappersForTrackerSupport::SetGlobalInstanceRegisteredForTrackerSupport() +{ + // QCALL contracts are not used here because the managed declaration + // uses the SuppressGCTransition attribute + + _ASSERTE(!g_IsGlobalComWrappersRegisteredForTrackerSupport); + g_IsGlobalComWrappersRegisteredForTrackerSupport = true; +} + +bool GlobalComWrappersForTrackerSupport::TryGetOrCreateComInterfaceForObject( + _In_ OBJECTREF instance, + _Outptr_ void** wrapperRaw) +{ + CONTRACTL + { + THROWS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + if (!g_IsGlobalComWrappersRegisteredForTrackerSupport) + return false; + + // Passing NULL as the ComWrappers implementation indicates using the globally registered instance + return TryGetOrCreateComInterfaceForObjectInternal( + NULL, + instance, + CreateComInterfaceFlags::CreateComInterfaceFlags_TrackerSupport, + ComWrappersScenario::TrackerSupportGlobalInstance, + wrapperRaw); +} + +bool GlobalComWrappersForTrackerSupport::TryGetOrCreateObjectForComInstance( + _In_ IUnknown* externalComObject, + _Out_ OBJECTREF* objRef) +{ + CONTRACTL + { + THROWS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + if (!g_IsGlobalComWrappersRegisteredForTrackerSupport) + return false; + + // Determine the true identity of the object + SafeComHolder identity; + { + GCX_PREEMP(); + + HRESULT hr = externalComObject->QueryInterface(IID_IUnknown, &identity); + _ASSERTE(hr == S_OK); + } + + // Passing NULL as the ComWrappers implementation indicates using the globally registered instance + return TryGetOrCreateObjectForComInstanceInternal( + NULL /*comWrappersImpl*/, + identity, + CreateObjectFlags::CreateObjectFlags_TrackerObject, + ComWrappersScenario::TrackerSupportGlobalInstance, + NULL /*wrapperMaybe*/, + objRef); +} + +IUnknown* ComWrappersNative::GetIdentityForObject(_In_ OBJECTREF* objectPROTECTED, _In_ REFIID riid) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(objectPROTECTED)); + } + CONTRACTL_END; + + ASSERT_PROTECTED(objectPROTECTED); + + SyncBlock* syncBlock = (*objectPROTECTED)->PassiveGetSyncBlock(); + if (syncBlock == nullptr) + { + return nullptr; + } + + InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfoNoCreate(); + if (interopInfo == nullptr) + { + return nullptr; + } + + void* context; + if (interopInfo->TryGetExternalComObjectContext(&context)) + { + IUnknown* identity = reinterpret_cast(reinterpret_cast(context)->Identity); + GCX_PREEMP(); + IUnknown* result; + if (SUCCEEDED(identity->QueryInterface(riid, (void**)&result))) + { + return result; + } + } + return nullptr; +} + #endif // FEATURE_COMWRAPPERS void Interop::OnGCStarted(_In_ int nCondemnedGeneration) diff --git a/src/coreclr/src/vm/interoplibinterface.h b/src/coreclr/src/vm/interoplibinterface.h index 6a5884a..3e5abda 100644 --- a/src/coreclr/src/vm/interoplibinterface.h +++ b/src/coreclr/src/vm/interoplibinterface.h @@ -37,6 +37,9 @@ public: // Lifetime management for COM Wrappers public: // COM activation static void MarkWrapperAsComActivated(_In_ IUnknown* wrapperMaybe); + +public: // Unwrapping support + static IUnknown* GetIdentityForObject(_In_ OBJECTREF* objectPROTECTED, _In_ REFIID riid); }; class GlobalComWrappersForMarshalling @@ -58,6 +61,25 @@ public: // Functions operating on a registered global instance for marshalling _Out_ OBJECTREF* objRef); }; + +class GlobalComWrappersForTrackerSupport +{ +public: + // Native QCall for the ComWrappers managed type to indicate a global instance + // is registered for tracker support. This should be set if the private static member + // representing the global instance for tracker support on ComWrappers is non-null. + static void QCALLTYPE SetGlobalInstanceRegisteredForTrackerSupport(); + +public: // Functions operating on a registered global instance for tracker support + static bool TryGetOrCreateComInterfaceForObject( + _In_ OBJECTREF instance, + _Outptr_ void** wrapperRaw); + + static bool TryGetOrCreateObjectForComInstance( + _In_ IUnknown* externalComObject, + _Out_ OBJECTREF* objRef); +}; + #endif // FEATURE_COMWRAPPERS class Interop diff --git a/src/coreclr/src/vm/marshalnative.cpp b/src/coreclr/src/vm/marshalnative.cpp index 2e06801..75c1e51 100644 --- a/src/coreclr/src/vm/marshalnative.cpp +++ b/src/coreclr/src/vm/marshalnative.cpp @@ -558,7 +558,7 @@ FCIMPL2(LPVOID, MarshalNative::GCHandleInternalAlloc, Object *obj, int type) OBJECTREF objRef(obj); - assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_WINRT); + assert(type >= HNDTYPE_WEAK_SHORT && type <= HNDTYPE_WEAK_NATIVE_COM); if (CORProfilerTrackGC()) { diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index a16bc1a..82c793b 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -1110,7 +1110,7 @@ PVOID QCALLTYPE RuntimeTypeHandle::GetGCHandle(QCall::TypeHandle pTypeHandle, IN GCX_COOP(); TypeHandle th = pTypeHandle.AsTypeHandle(); - assert(handleType >= HNDTYPE_WEAK_SHORT && handleType <= HNDTYPE_WEAK_WINRT); + assert(handleType >= HNDTYPE_WEAK_SHORT && handleType <= HNDTYPE_WEAK_NATIVE_COM); objHandle = AppDomain::GetCurrentDomain()->CreateTypedHandle(NULL, static_cast(handleType)); th.GetLoaderAllocator()->RegisterHandleForCleanup(objHandle); diff --git a/src/coreclr/src/vm/weakreferencenative.cpp b/src/coreclr/src/vm/weakreferencenative.cpp index b1dc825..2cdb83c 100644 --- a/src/coreclr/src/vm/weakreferencenative.cpp +++ b/src/coreclr/src/vm/weakreferencenative.cpp @@ -16,6 +16,7 @@ #include "typestring.h" #include "typeparse.h" #include "threadsuspend.h" +#include "interoplibinterface.h" //************************************************************************ @@ -34,7 +35,7 @@ const LPVOID specialWeakReferenceHandles[3] = { 0, 0, 0 }; // // A WeakReference instance can hold one of three types of handles - short or long weak handles, -// or a WinRT weak reference handle. The WinRT weak reference handle has the extra capability +// or a native COM weak reference handle. The native COM weak reference handle has the extra capability // of recreating an RCW for a COM object which is still alive even though the previous RCW had // been collected. In order to differentiate this type of handle from the standard weak handles, // the bottom bit is stolen. @@ -50,23 +51,23 @@ const LPVOID specialWeakReferenceHandles[3] = { 0, 0, 0 }; // The following functions are to set, test, and unset that bit before the handle is used. // -// Determine if an object handle is a WinRT weak reference handle -bool IsWinRTWeakReferenceHandle(OBJECTHANDLE handle) +// Determine if an object handle is a native COM weak reference handle +bool IsNativeComWeakReferenceHandle(OBJECTHANDLE handle) { STATIC_CONTRACT_LEAF; return (reinterpret_cast(handle) & 0x1) != 0x0; } -// Mark an object handle as being a WinRT weak reference handle -OBJECTHANDLE SetWinRTWeakReferenceHandle(OBJECTHANDLE handle) +// Mark an object handle as being a native COM weak reference handle +OBJECTHANDLE SetNativeComWeakReferenceHandle(OBJECTHANDLE handle) { STATIC_CONTRACT_LEAF; - _ASSERTE(!IsWinRTWeakReferenceHandle(handle)); + _ASSERTE(!IsNativeComWeakReferenceHandle(handle)); return reinterpret_cast(reinterpret_cast(handle) | 0x1); } -// Get the object handle value even if the object is a WinRT weak reference +// Get the object handle value even if the object is a native COM weak reference OBJECTHANDLE GetHandleValue(OBJECTHANDLE handle) { STATIC_CONTRACT_LEAF; @@ -102,17 +103,17 @@ private: #ifdef FEATURE_COMINTEROP -// Get a WinRT weak reference for the object underlying an RCW if applicable. If the incoming object cannot -// use a WinRT weak reference, nullptr is returned. Otherwise, an AddRef-ed IWeakReference* for the COM +// Get a native COM weak reference for the object underlying an RCW if applicable. If the incoming object cannot +// use a native COM weak reference, nullptr is returned. Otherwise, an AddRef-ed IWeakReference* for the COM // object underlying the RCW is returned. // -// In order to qualify to be used with a HNDTYPE_WEAK_WINRT, the incoming object must: +// In order to qualify to be used with a HNDTYPE_WEAK_NATIVE_COM, the incoming object must: // * be an RCW // * respond to a QI for IWeakReferenceSource // * succeed when asked for an IWeakReference* // // Note that *pObject should be GC protected on the way into this method -IWeakReference* GetWinRTWeakReference(OBJECTREF* pObject) +IWeakReference* GetComWeakReference(OBJECTREF* pObject) { CONTRACTL { @@ -132,21 +133,24 @@ IWeakReference* GetWinRTWeakReference(OBJECTREF* pObject) MethodTable* pMT = (*pObject)->GetMethodTable(); - // If the object is not an RCW, then we do not want to use a WinRT weak reference to it - if (!pMT->IsComObjectType()) - { - return nullptr; - } + SafeComHolder pWeakReferenceSource(nullptr); - // If the object is a managed type deriving from a COM type, then we also do not want to use a WinRT + // If the object is not an RCW, then we do not want to use a native COM weak reference to it + // If the object is a managed type deriving from a COM type, then we also do not want to use a native COM // weak reference to it. (Otherwise, we'll wind up resolving IWeakReference-s back into the CLR // when we don't want to have reentrancy). - if (pMT != g_pBaseCOMObject && pMT->IsExtensibleRCW()) + if (pMT->IsComObjectType() + && (pMT == g_pBaseCOMObject || !pMT->IsExtensibleRCW())) { - return nullptr; + pWeakReferenceSource = reinterpret_cast(GetComIPFromObjectRef(pObject, IID_IWeakReferenceSource, false /* throwIfNoComIP */)); + } +#ifdef FEATURE_COMWRAPPERS + else + { + pWeakReferenceSource = reinterpret_cast(ComWrappersNative::GetIdentityForObject(pObject, IID_IWeakReferenceSource)); } +#endif - SafeComHolder pWeakReferenceSource(reinterpret_cast(GetComIPFromObjectRef(pObject, IID_IWeakReferenceSource, false /* throwIfNoComIP */))); if (pWeakReferenceSource == nullptr) { return nullptr; @@ -162,17 +166,17 @@ IWeakReference* GetWinRTWeakReference(OBJECTREF* pObject) return pWeakReference.Extract(); } -// Given an object handle that stores a WinRT weak reference, attempt to create an RCW -// and store it back in the handle, returning the RCW. If the underlying WinRT object +// Given an object handle that stores a native COM weak reference, attempt to create an RCW +// and store it back in the handle, returning the RCW. If the underlying native COM object // is not alive, then the result is NULL. // // In order to create a new RCW, we must: -// * Have an m_handle of HNDTYPE_WEAK_WINRT (ie the bottom bit of m_handle is set) +// * Have an m_handle of HNDTYPE_WEAK_NATIVE_COM (ie the bottom bit of m_handle is set) // * Have stored an IWeakReference* in the handle extra info when setting up the handle -// (see GetWinRTWeakReference) +// (see GetComWeakReference) // * The IWeakReference* must respond to a Resolve request for IID_IInspectable // * -NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, TypeHandle targetType, LPVOID __me) +NOINLINE Object* LoadComWeakReferenceTarget(WEAKREFERENCEREF weakReference, TypeHandle targetType, LPVOID __me) { CONTRACTL { @@ -200,14 +204,14 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty // // Since we're acquiring and releasing the lock multiple times, we need to check the handle state each time we // reacquire the lock to make sure that another thread hasn't reassigned the target of the handle or finalized it - SafeComHolder pWinRTWeakReference = nullptr; + SafeComHolder pComWeakReference = nullptr; { WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); GCX_NOTRIGGER(); // Make sure that while we were not holding the spin lock, another thread did not change the target of // this weak reference. Only fetch the IWeakReference* if we still have a valid handle holding a NULL object - // and the handle is still a HNDTYPE_WEAK_WINRT type handle. + // and the handle is still a HNDTYPE_WEAK_NATIVE_COM type handle. if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) { if (*(Object **)(handle.Handle) != NULL) @@ -217,22 +221,22 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty // weak reference is targeting. gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); } - else if(IsWinRTWeakReferenceHandle(handle.RawHandle)) + else if(IsNativeComWeakReferenceHandle(handle.RawHandle)) { - _ASSERTE(GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handle.Handle) == HNDTYPE_WEAK_WINRT); + _ASSERTE(GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handle.Handle) == HNDTYPE_WEAK_NATIVE_COM); // Retrieve the associated IWeakReference* for this weak reference. Add a reference to it while we release // the spin lock so that another thread doesn't release it out from underneath us. // - // Setting pWinRTWeakReference will claim that it triggers a GC, however that's not true in this case because + // Setting pComWeakReference will claim that it triggers a GC, however that's not true in this case because // it's always set to NULL here and there's nothing for it to release. - _ASSERTE(pWinRTWeakReference.IsNull()); + _ASSERTE(pComWeakReference.IsNull()); CONTRACT_VIOLATION(GCViolation); IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); - pWinRTWeakReference = reinterpret_cast(mgr->GetExtraInfoFromHandle(handle.Handle)); - if (!pWinRTWeakReference.IsNull()) + pComWeakReference = reinterpret_cast(mgr->GetExtraInfoFromHandle(handle.Handle)); + if (!pComWeakReference.IsNull()) { - pWinRTWeakReference->AddRef(); + pComWeakReference->AddRef(); } } } @@ -242,16 +246,16 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty // identity of the underlying COM object (assuming that object is still alive). This work is done without holding the // spin lock since it will call out to arbitrary code and as such we need to switch to preemptive mode. SafeComHolder pTargetIdentity = nullptr; - if (pWinRTWeakReference != nullptr) + if (pComWeakReference != nullptr) { _ASSERTE(gc.target == NULL); GCX_PREEMP(); - // Using the IWeakReference*, get ahold of the target WinRT object's IInspectable*. If this resolve fails, then we - // assume that the underlying WinRT object is no longer alive, and thus we cannot create a new RCW for it. + // Using the IWeakReference*, get ahold of the target native COM object's IInspectable*. If this resolve fails, then we + // assume that the underlying native COM object is no longer alive, and thus we cannot create a new RCW for it. SafeComHolderPreemp pTarget = nullptr; - if (SUCCEEDED(pWinRTWeakReference->Resolve(IID_IInspectable, &pTarget))) + if (SUCCEEDED(pComWeakReference->Resolve(IID_IInspectable, &pTarget))) { if (!pTarget.IsNull()) { @@ -264,7 +268,11 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty // If we were able to get an IUnkown identity for the object, then we can find or create an associated RCW for it. if (!pTargetIdentity.IsNull()) { - GetObjectRefFromComIP(&gc.rcw, pTargetIdentity); + // Try the global COM wrappers first before falling back to the built-in system. + if (!GlobalComWrappersForTrackerSupport::TryGetOrCreateObjectForComInstance(pTargetIdentity, &gc.rcw)) + { + GetObjectRefFromComIP(&gc.rcw, pTargetIdentity); + } } // If we were able to get an RCW, then we need to reacquire the spin lock and store the RCW in the handle. Note that @@ -272,7 +280,7 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty // building the RCW. In that case, we will defer to the hadle that the other thread set, and let the RCW die. if (gc.rcw != NULL) { - // Make sure the type we got back from the WinRT object is compatible with the type the managed + // Make sure the type we got back from the native COM object is compatible with the type the managed // weak reference expects. (For instance, in the WeakReference case, the returned type // had better be compatible with T). TypeHandle rcwType(gc.rcw->GetMethodTable()); @@ -284,7 +292,7 @@ NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, Ty SString resolvedTypeName; TypeString::AppendType(resolvedTypeName, rcwType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); - COMPlusThrow(kInvalidCastException, IDS_EE_WINRT_WEAKREF_BAD_TYPE, weakReferenceTypeName.GetUnicode(), resolvedTypeName.GetUnicode()); + COMPlusThrow(kInvalidCastException, IDS_EE_NATIVE_COM_WEAKREF_BAD_TYPE, weakReferenceTypeName.GetUnicode(), resolvedTypeName.GetUnicode()); } WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); @@ -420,12 +428,21 @@ FCIMPL3(void, WeakReferenceNative::Create, WeakReferenceObject * pThisUNSAFE, Ob // Create the handle. #ifdef FEATURE_COMINTEROP - IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); - if (pRawWinRTWeakReference != nullptr) + IWeakReference* pRawComWeakReference = nullptr; + if (gc.pTarget != NULL) { - SafeComHolder pWinRTWeakReferenceHolder(pRawWinRTWeakReference); - gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); - pWinRTWeakReferenceHolder.SuppressRelease(); + SyncBlock* pSyncBlock = gc.pTarget->PassiveGetSyncBlock(); + if (pSyncBlock != nullptr && pSyncBlock->GetInteropInfoNoCreate() != nullptr) + { + pRawComWeakReference = GetComWeakReference(&gc.pTarget); + } + } + + if (pRawComWeakReference != nullptr) + { + SafeComHolder pComWeakReferenceHolder(pRawComWeakReference); + gc.pThis->m_Handle = SetNativeComWeakReferenceHandle(GetAppDomain()->CreateNativeComWeakHandle(gc.pTarget, pComWeakReferenceHolder)); + pComWeakReferenceHolder.SuppressRelease(); } else #endif // FEATURE_COMINTEROP @@ -463,12 +480,21 @@ FCIMPL3(void, WeakReferenceOfTNative::Create, WeakReferenceObject * pThisUNSAFE, // Create the handle. #ifdef FEATURE_COMINTEROP - IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); - if (pRawWinRTWeakReference != nullptr) + IWeakReference* pRawComWeakReference = nullptr; + if (gc.pTarget != NULL) + { + SyncBlock* pSyncBlock = gc.pTarget->PassiveGetSyncBlock(); + if (pSyncBlock != nullptr && pSyncBlock->GetInteropInfoNoCreate() != nullptr) + { + pRawComWeakReference = GetComWeakReference(&gc.pTarget); + } + } + + if (pRawComWeakReference != nullptr) { - SafeComHolder pWinRTWeakReferenceHolder(pRawWinRTWeakReference); - gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); - pWinRTWeakReferenceHolder.SuppressRelease(); + SafeComHolder pComWeakReferenceHolder(pRawComWeakReference); + gc.pThis->m_Handle = SetNativeComWeakReferenceHandle(GetAppDomain()->CreateNativeComWeakHandle(gc.pTarget, pComWeakReferenceHolder)); + pComWeakReferenceHolder.SuppressRelease(); } else #endif // FEATURE_COMINTEROP @@ -499,7 +525,7 @@ void FinalizeWeakReference(Object * obj) // The suspension state of the runtime must be prevented from changing while in this function in order for this to be safe. OBJECTHANDLE handle = ThreadSuspend::SysIsSuspended() ? pThis->m_Handle.LoadWithoutBarrier() : AcquireWeakHandleSpinLock(pThis); OBJECTHANDLE handleToDestroy = NULL; - bool isWeakWinRTHandle = false; + bool isWeakNativeComHandle = false; // Check for not yet constructed or already finalized handle if ((handle != NULL) && !IS_SPECIAL_HANDLE(handle)) @@ -509,8 +535,8 @@ void FinalizeWeakReference(Object * obj) // Cache the old handle value HandleType handleType = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handleToDestroy); #ifdef FEATURE_COMINTEROP - _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT || handleType == HNDTYPE_WEAK_WINRT); - isWeakWinRTHandle = handleType == HNDTYPE_WEAK_WINRT; + _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT || handleType == HNDTYPE_WEAK_NATIVE_COM); + isWeakNativeComHandle = handleType == HNDTYPE_WEAK_NATIVE_COM; #else // !FEATURE_COMINTEROP _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT); #endif // FEATURE_COMINTEROP @@ -528,9 +554,9 @@ void FinalizeWeakReference(Object * obj) if (handleToDestroy != NULL) { #ifdef FEATURE_COMINTEROP - if (isWeakWinRTHandle) + if (isWeakNativeComHandle) { - DestroyWinRTWeakHandle(handleToDestroy); + DestroyNativeComWeakHandle(handleToDestroy); } else #endif // FEATURE_COMINTEROP @@ -645,14 +671,14 @@ FCIMPL1(Object *, WeakReferenceNative::GetTarget, WeakReferenceObject * pThisUNS OBJECTREF pTarget = GetWeakReferenceTarget(pThis); #ifdef FEATURE_COMINTEROP - // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise - // we can try to create a new RCW to the underlying WinRT object if it's still alive. - if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) + // If we found an object, or we're not a native COM weak reference, then we're done. Othewrise + // we can try to create a new RCW to the underlying native COM object if it's still alive. + if (pTarget != NULL || !IsNativeComWeakReferenceHandle(pThis->m_Handle)) { FC_GC_POLL_AND_RETURN_OBJREF(pTarget); } - FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, g_pObjectClass, GetEEFuncEntryPointMacro(WeakReferenceNative::GetTarget))); + FC_INNER_RETURN(Object*, LoadComWeakReferenceTarget(pThis, g_pObjectClass, GetEEFuncEntryPointMacro(WeakReferenceNative::GetTarget))); #else // !FEATURE_COMINTEROP FC_GC_POLL_AND_RETURN_OBJREF(pTarget); #endif // FEATURE_COMINTEROP @@ -673,14 +699,14 @@ FCIMPL1(Object *, WeakReferenceOfTNative::GetTarget, WeakReferenceObject * pThis #ifdef FEATURE_COMINTEROP - // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise - // we can try to create a new RCW to the underlying WinRT object if it's still alive. - if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) + // If we found an object, or we're not a native COM weak reference, then we're done. Othewrise + // we can try to create a new RCW to the underlying native COM object if it's still alive. + if (pTarget != NULL || !IsNativeComWeakReferenceHandle(pThis->m_Handle)) { FC_GC_POLL_AND_RETURN_OBJREF(pTarget); } - FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, pThis->GetMethodTable()->GetInstantiation()[0], GetEEFuncEntryPointMacro(WeakReferenceOfTNative::GetTarget))); + FC_INNER_RETURN(Object*, LoadComWeakReferenceTarget(pThis, pThis->GetMethodTable()->GetInstantiation()[0], GetEEFuncEntryPointMacro(WeakReferenceOfTNative::GetTarget))); #else // !FEATURE_COMINTEROP FC_GC_POLL_AND_RETURN_OBJREF(pTarget); #endif // FEATURE_COMINTEROP @@ -711,7 +737,7 @@ FCIMPLEND #include -// Slow path helper for setting the target of a weak reference. This code is used if a WinRT weak reference might +// Slow path helper for setting the target of a weak reference. This code is used if a native COM weak reference might // be required. NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF target, LPVOID __me) { @@ -721,7 +747,7 @@ NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF t HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, target, weakReference); #ifdef FEATURE_COMINTEROP - SafeComHolder pTargetWeakReference(GetWinRTWeakReference(&target)); + SafeComHolder pTargetWeakReference(GetComWeakReference(&target)); #endif // FEATURE_COMINTEROP @@ -735,30 +761,30 @@ NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF t // Existing target is a GC object, new target is a GC object: // * Just store the new object in the handle // - // Existing target is WinRT, new target is WinRT: + // Existing target is native COM weak reference, new target is native COM weak reference: // * Release the existing IWeakReference* // * Store the new IWeakReference* // * Store the new object in the handle // - // Existing target is WinRT, new target is GC: + // Existing target is native COM weak reference, new target is GC: // * Release the existing IWeakReference* // * Store null to the IWeakReference* field // * Store the new object in the handle // - // Existing target is GC, new target is WinRT: + // Existing target is GC, new target is native COM weak reference: // * Destroy the existing handle - // * Allocate a new WinRT weak handle for the new target + // * Allocate a new native COM weak handle for the new target // - if (IsWinRTWeakReferenceHandle(handle.RawHandle)) + if (IsNativeComWeakReferenceHandle(handle.RawHandle)) { - // If the existing reference is a WinRT weak reference, we need to release its IWeakReference pointer + // If the existing reference is a native COM weak reference, we need to release its IWeakReference pointer // and update it with the new weak reference pointer. If the incoming object is not an RCW that can // use IWeakReference, then pTargetWeakReference will be null. Therefore, no matter what the incoming // object type is, we can unconditionally store pTargetWeakReference to the object handle's extra data. IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); IWeakReference* pExistingWeakReference = reinterpret_cast(mgr->GetExtraInfoFromHandle(handle.Handle)); - mgr->SetExtraInfoForHandle(handle.Handle, HNDTYPE_WEAK_WINRT, reinterpret_cast(pTargetWeakReference.GetValue())); + mgr->SetExtraInfoForHandle(handle.Handle, HNDTYPE_WEAK_NATIVE_COM, reinterpret_cast(pTargetWeakReference.GetValue())); StoreObjectInHandle(handle.Handle, target); if (pExistingWeakReference != nullptr) @@ -768,15 +794,15 @@ NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF t } else if (pTargetWeakReference != nullptr) { - // The existing handle is not a WinRT weak reference, but we need to store the new object in - // a WinRT weak reference. Therefore we need to destroy the old handle and create a new WinRT + // The existing handle is not a native COM weak reference, but we need to store the new object in + // a native COM weak reference. Therefore we need to destroy the old handle and create a new native COM // handle. The new handle needs to be allocated first to prevent the weak reference from holding // a destroyed handle if we fail to allocate the new one. - _ASSERTE(!IsWinRTWeakReferenceHandle(handle.RawHandle)); + _ASSERTE(!IsNativeComWeakReferenceHandle(handle.RawHandle)); OBJECTHANDLE previousHandle = handle.RawHandle; - handle.Handle = GetAppDomain()->CreateWinRTWeakHandle(target, pTargetWeakReference); - handle.RawHandle = SetWinRTWeakReferenceHandle(handle.Handle); + handle.Handle = GetAppDomain()->CreateNativeComWeakHandle(target, pTargetWeakReference); + handle.RawHandle = SetNativeComWeakReferenceHandle(handle.Handle); DestroyTypedHandle(previousHandle); } @@ -822,7 +848,7 @@ FCIMPL2(void, WeakReferenceNative::SetTarget, WeakReferenceObject * pThisUNSAFE, // If the existing handle is a GC weak handle and the new target is not an RCW, then // we can avoid setting up a helper method frame and just reset the handle directly. - if (!IsWinRTWeakReferenceHandle(handle)) + if (!IsNativeComWeakReferenceHandle(handle)) { if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) { @@ -875,7 +901,7 @@ FCIMPL2(void, WeakReferenceOfTNative::SetTarget, WeakReferenceObject * pThisUNSA // If the existing handle is a GC weak handle and the new target is not an RCW, then // we can avoid setting up a helper method frame and just reset the handle directly. - if (!IsWinRTWeakReferenceHandle(handle)) + if (!IsNativeComWeakReferenceHandle(handle)) { if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) { diff --git a/src/coreclr/tests/src/Interop/CMakeLists.txt b/src/coreclr/tests/src/Interop/CMakeLists.txt index 8a39e97..778d9ab 100644 --- a/src/coreclr/tests/src/Interop/CMakeLists.txt +++ b/src/coreclr/tests/src/Interop/CMakeLists.txt @@ -81,6 +81,7 @@ if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(COM/NativeClients/Dispatch) add_subdirectory(COM/NativeClients/Events) add_subdirectory(COM/ComWrappers/MockReferenceTrackerRuntime) + add_subdirectory(COM/ComWrappers/WeakReference) add_subdirectory(WinRT/NativeComponent) # IJW isn't supported on ARM64 diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/CMakeLists.txt b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/CMakeLists.txt new file mode 100644 index 0000000..8166db2 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/CMakeLists.txt @@ -0,0 +1,10 @@ +project (WeakReferenceNative) +include_directories( ${INC_PLATFORM_DIR} ) +set(SOURCES WeakReferenceNative.cpp) + +# add the shared library +add_library (WeakReferenceNative SHARED ${SOURCES}) +target_link_libraries(WeakReferenceNative ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS WeakReferenceNative DESTINATION bin) diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceNative.cpp b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceNative.cpp new file mode 100644 index 0000000..5617b11 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceNative.cpp @@ -0,0 +1,124 @@ +// 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 +#include +#include +#include + +namespace +{ + struct WeakReference : public IWeakReference, public UnknownImpl + { + IInspectable* _reference; + std::atomic _strongRefCount; + + WeakReference(IInspectable* reference, ULONG strongRefCount) + : _reference(reference), + _strongRefCount(strongRefCount) + {} + + ULONG AddStrongRef() + { + assert(_strongRefCount > 0); + return (++_strongRefCount); + } + + ULONG ReleaseStrongRef() + { + assert(_strongRefCount > 0); + return --_strongRefCount; + } + + STDMETHOD(Resolve)(REFIID riid, IInspectable** ppvObject) + { + if (_strongRefCount > 0) + { + void* pObject; + HRESULT hr = _reference->QueryInterface(riid, &pObject); + *ppvObject = reinterpret_cast(pObject); + return hr; + } + return E_NOINTERFACE; + } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + return DoQueryInterface(riid, ppvObject, static_cast(this)); + } + + DEFINE_REF_COUNTING() + }; + + struct WeakReferencableObject : public IWeakReferenceSource, public IInspectable, public UnknownImpl + { + ComSmartPtr _weakReference; + STDMETHOD(GetWeakReference)(_COM_Outptr_ IWeakReference** ppWeakReference) + { + if (!_weakReference) + { + ULONG refCount = UnknownImpl::GetRefCount(); + _weakReference = new WeakReference(this, refCount); + } + _weakReference->AddRef(); + *ppWeakReference = _weakReference; + return S_OK; + } + + STDMETHOD(GetRuntimeClassName)(HSTRING* pRuntimeClassName) + { + return E_NOTIMPL; + } + + STDMETHOD(GetIids)( + ULONG *iidCount, + IID **iids) + { + return E_NOTIMPL; + } + + STDMETHOD(GetTrustLevel)(TrustLevel *trustLevel) + { + *trustLevel = FullTrust; + return S_OK; + } + + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) + { + HRESULT hr = DoQueryInterface(riid, ppvObject, static_cast(this), static_cast(this), static_cast(this)); + if (SUCCEEDED(hr) && _weakReference) + { + _weakReference->AddStrongRef(); + } + return hr; + } + STDMETHOD_(ULONG, AddRef)(void) + { + if (_weakReference) + { + return _weakReference->AddStrongRef(); + } + return UnknownImpl::DoAddRef(); + } + STDMETHOD_(ULONG, Release)(void) + { + if (_weakReference) + { + ULONG c = _weakReference->ReleaseStrongRef(); + if (c == 0) + delete this; + return c; + } + return UnknownImpl::DoRelease(); + } + }; +} +extern "C" DLL_EXPORT WeakReferencableObject* STDMETHODCALLTYPE CreateWeakReferencableObject() +{ + return new WeakReferencableObject(); +} diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.cs b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.cs new file mode 100644 index 0000000..acde6f6 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.cs @@ -0,0 +1,137 @@ +// 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.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using TestLibrary; + + static class WeakReferenceNative + { + [DllImport(nameof(WeakReferenceNative))] + public static extern IntPtr CreateWeakReferencableObject(); + } + + public struct VtblPtr + { + public IntPtr Vtbl; + } + + public class WeakReferencableWrapper + { + private struct Vtbl + { + public IntPtr QueryInterface; + public _AddRef AddRef; + public _Release Release; + } + + private delegate int _AddRef(IntPtr This); + private delegate int _Release(IntPtr This); + + private readonly IntPtr instance; + private readonly Vtbl vtable; + + public WeakReferencableWrapper(IntPtr instance) + { + var inst = Marshal.PtrToStructure(instance); + this.vtable = Marshal.PtrToStructure(inst.Vtbl); + this.instance = instance; + } + + ~WeakReferencableWrapper() + { + if (this.instance != IntPtr.Zero) + { + this.vtable.Release(this.instance); + } + } + } + + class Program + { + class TestComWrappers : ComWrappers + { + protected unsafe override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + count = 0; + return null; + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flag) + { + return new WeakReferencableWrapper(externalComObject); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + } + + public static readonly ComWrappers Instance = new TestComWrappers(); + } + + static void ValidateNativeWeakReference() + { + Console.WriteLine($"Running {nameof(ValidateNativeWeakReference)}..."); + + static (WeakReference, IntPtr) GetWeakReference() + { + var cw = new TestComWrappers(); + + IntPtr objRaw = WeakReferenceNative.CreateWeakReferencableObject(); + + var obj = (WeakReferencableWrapper)cw.GetOrCreateObjectForComInstance(objRaw, CreateObjectFlags.None); + + // The returned WeakReferencableWrapper from ComWrappers takes ownership + // of the ref returned from CreateWeakReferencableObject. + // Call Marshal.AddRef to ensure that objRaw owns a reference. + Marshal.AddRef(objRaw); + + return (new WeakReference(obj), objRaw); + } + + static bool CheckIfWeakReferenceIsAlive(WeakReference wr) + { + return wr.TryGetTarget(out _); + } + + var (weakRef, nativeRef) = GetWeakReference(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + // A weak reference to an RCW wrapping an IWeakReference should stay alive even after the RCW dies + Assert.IsTrue(CheckIfWeakReferenceIsAlive(weakRef)); + + // Release the last native reference. + Marshal.Release(nativeRef); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // After all native references die and the RCW is collected, the weak reference should be dead and stay dead. + Assert.IsFalse(CheckIfWeakReferenceIsAlive(weakRef)); + + } + + static int Main(string[] doNotUse) + { + try + { + ComWrappers.RegisterForTrackerSupport(TestComWrappers.Instance); + ValidateNativeWeakReference(); + } + catch (Exception e) + { + Console.WriteLine($"Test Failure: {e}"); + return 101; + } + + return 100; + } + } +} + diff --git a/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.csproj b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.csproj new file mode 100644 index 0000000..edcc331 --- /dev/null +++ b/src/coreclr/tests/src/Interop/COM/ComWrappers/WeakReference/WeakReferenceTest.csproj @@ -0,0 +1,24 @@ + + + Exe + + true + + true + true + true + + + + + + + + + + 1 + + + + + diff --git a/src/coreclr/tests/src/Interop/common/ComHelpers.h b/src/coreclr/tests/src/Interop/common/ComHelpers.h index c90ff7a..673f190 100644 --- a/src/coreclr/tests/src/Interop/common/ComHelpers.h +++ b/src/coreclr/tests/src/Interop/common/ComHelpers.h @@ -105,6 +105,12 @@ public: return c; } +protected: + ULONG GetRefCount() + { + return _refCount; + } + private: std::atomic _refCount = 1; }; -- 2.7.4