summaryrefslogtreecommitdiff
path: root/src/vm/runtimecallablewrapper.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/runtimecallablewrapper.h')
-rw-r--r--src/vm/runtimecallablewrapper.h2233
1 files changed, 2233 insertions, 0 deletions
diff --git a/src/vm/runtimecallablewrapper.h b/src/vm/runtimecallablewrapper.h
new file mode 100644
index 0000000000..90fb6b6990
--- /dev/null
+++ b/src/vm/runtimecallablewrapper.h
@@ -0,0 +1,2233 @@
+// 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.
+
+/*============================================================
+**
+** Header: RuntimeCallableWrapper.h
+**
+**
+** Purpose: Contains types and method signatures for the RCW class
+**
+**
+
+===========================================================*/
+//---------------------------------------------------------------------------------
+// Runtime Callable WRAPPERS on COM objects
+// Purpose: wrap com objects to behave as CLR objects
+// Reqmts: Wrapper has to have the same layout as the CLR objects
+//
+// Data members of wrapper, are basically COM2 Interface pointers on the COM2 object
+// Interfaces that belong to the same object are stored in the same wrapper, IUnknown
+// pointer determines the identity of the object.
+// As new COM2 interfaces are seen on the same object, they need to be added to the
+// wrapper, wrapper is allocated as a fixed size object with overflow chain.
+//
+// struct IPMap
+// {
+// MethodTable *pMT; // identifies the managed interface class
+// IUnknown* m_ip; // COM IP
+// }
+//
+// Issues : Performance/Identity trade-offs, create new wrappers or find and reuse wrappers
+// we use a hash table to track the wrappers and reuse them, maintains identity
+// RCWCache class maintains the lookup table and handles the clean up
+// Cast operations: requires a QI, unless a QI for that interface was done previously
+//
+// Threading : apartment model COM objects have thread affinity
+// choices: COM+ can guarantee thread affinity by making sure
+// the calls are always made on the right thread
+// Advantanges: avoid an extra marshalling
+// Dis.Advt. : need to make sure legacy apartment semantics are preserved
+// this includes any weird behaviour currently built into DCOM.
+//
+// RCWs: Interface map (IMap) won't have any entries, the method table of RCWs
+// have a special flag to indicate that these managed objects
+// require special treatment for interface cast, call interface operations.
+//
+// Stubs : need to find the COM2 interface ptr, and the slot within the interface to
+// re-direct the call
+// Marshaling params and results (common case should be fast)
+//
+//-----------------------------------------------------------------------------------
+
+
+#ifndef _RUNTIMECALLABLEWRAPPER_H
+#define _RUNTIMECALLABLEWRAPPER_H
+
+#ifndef FEATURE_COMINTEROP
+#error FEATURE_COMINTEROP is required for this file
+#endif // FEATURE_COMINTEROP
+
+#include "utilcode.h"
+#include "vars.hpp"
+#include "objecthandle.h"
+#include "spinlock.h"
+#include "interoputil.h"
+#include "mngstdinterfaces.h"
+#include "excep.h"
+#include "comcache.h"
+#include "threads.h"
+#include "mdaassistants.h"
+#include "comcache.h"
+#include "jupiterobject.h"
+
+class Object;
+class ComCallWrapper;
+class Thread;
+
+#define GC_PRESSURE_PROCESS_LOCAL 3456
+#define GC_PRESSURE_MACHINE_LOCAL 4004
+#define GC_PRESSURE_REMOTE 4824
+
+#ifdef _WIN64
+#define GC_PRESSURE_WINRT_BASE 1000
+#define GC_PRESSURE_WINRT_LOW 12000
+#define GC_PRESSURE_WINRT_MEDIUM 120000
+#define GC_PRESSURE_WINRT_HIGH 1200000
+#else // _WIN64
+#define GC_PRESSURE_WINRT_BASE 750
+#define GC_PRESSURE_WINRT_LOW 8000
+#define GC_PRESSURE_WINRT_MEDIUM 80000
+#define GC_PRESSURE_WINRT_HIGH 800000
+#endif // _WIN64
+
+extern bool g_fShutDownCOM;
+
+enum {INTERFACE_ENTRY_CACHE_SIZE = 8};
+
+struct RCWAuxiliaryData;
+typedef DPTR(RCWAuxiliaryData) PTR_RCWAuxiliaryData;
+
+#define VARIANCE_STUB_TARGET_USE_STRING ((OBJECTHANDLE)(INT_PTR)0x1)
+#define VARIANCE_STUB_TARGET_USE_T ((OBJECTHANDLE)(INT_PTR)0x2)
+#define VARIANCE_STUB_TARGET_IS_HANDLE(handle) (((INT_PTR)(handle) & ~0x3) != 0)
+
+// Additional RCW data used for generic interop and auxiliary interface pointer cache.
+// This structure is lazily allocated and associated with the RCW via the m_pAuxiliaryData
+// field. It's needed only if the RCW supports IEnumerable<T> or another interface with
+// variance, or if a QI result could not be saved in the inline interface pointer cache
+// (code:RCW.m_aInterfaceEntries).
+struct RCWAuxiliaryData
+{
+ RCWAuxiliaryData()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ m_pGetEnumeratorMethod = NULL;
+ m_prVariantInterfaces = NULL;
+ m_VarianceCacheCrst.Init(CrstLeafLock);
+ m_pInterfaceCache = NULL;
+ m_ohObjectVariantCallTarget_IEnumerable = NULL;
+ m_ohObjectVariantCallTarget_IReadOnlyList = NULL;
+ m_AuxFlags.m_dwFlags = 0;
+ }
+
+ ~RCWAuxiliaryData();
+
+ struct InterfaceEntryEx;
+ typedef DPTR(InterfaceEntryEx) PTR_InterfaceEntryEx;
+
+ // Augments code:InterfaceEntry with a next pointer and context entry field.
+ struct InterfaceEntryEx
+ {
+ PTR_InterfaceEntryEx m_pNext;
+
+ InterfaceEntry m_BaseEntry;
+ PTR_CtxEntry m_pCtxEntry;
+
+ ~InterfaceEntryEx()
+ {
+ WRAPPER_NO_CONTRACT;
+ if (m_pCtxEntry != NULL)
+ {
+ m_pCtxEntry->Release();
+ }
+ }
+ };
+
+ // Iterator for cached interface entries.
+ class InterfaceEntryIterator
+ {
+ PTR_InterfaceEntryEx m_pCurrent;
+ bool m_fFirst;
+
+ public:
+ inline InterfaceEntryIterator(PTR_RCWAuxiliaryData pAuxiliaryData)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_pCurrent = (pAuxiliaryData == NULL ? NULL : pAuxiliaryData->m_pInterfaceCache);
+ m_fFirst = true;
+ }
+
+ // Move to the next item returning TRUE if an item exists or FALSE if we've run off the end
+ inline bool Next()
+ {
+ LIMITED_METHOD_CONTRACT;
+ if (m_fFirst)
+ {
+ m_fFirst = false;
+ }
+ else
+ {
+ m_pCurrent = m_pCurrent->m_pNext;
+ }
+ return (m_pCurrent != NULL);
+ }
+
+ inline InterfaceEntry *GetEntry()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &m_pCurrent->m_BaseEntry;
+ }
+
+ inline LPVOID GetCtxCookie()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_pCurrent->m_pCtxEntry == NULL ? NULL : m_pCurrent->m_pCtxEntry->GetCtxCookie());
+ }
+
+ inline CtxEntry *GetCtxEntry()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_pCurrent->m_pCtxEntry->AddRef();
+ return m_pCurrent->m_pCtxEntry;
+ }
+
+ inline CtxEntry *GetCtxEntryNoAddRef()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pCurrent->m_pCtxEntry;
+ }
+
+ inline void ResetCtxEntry()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_pCurrent->m_pCtxEntry = NULL;
+ }
+
+#ifndef DACCESS_COMPILE
+ inline void SetCtxCookie(LPVOID pCtxCookie)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ CtxEntry *pCtxEntry = NULL;
+ if (pCtxCookie != NULL)
+ {
+ pCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(pCtxCookie, GetThread());
+ }
+ m_pCurrent->m_pCtxEntry = pCtxEntry;
+ }
+#endif // !DACCESS_COMPILE
+ };
+
+ void CacheVariantInterface(MethodTable *pMT);
+
+ void CacheInterfacePointer(MethodTable *pMT, IUnknown *pUnk, LPVOID pCtxCookie);
+ IUnknown *FindInterfacePointer(MethodTable *pMT, LPVOID pCtxCookie);
+
+ inline InterfaceEntryIterator IterateInterfacePointers()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return InterfaceEntryIterator(dac_cast<PTR_RCWAuxiliaryData>(this));
+ }
+
+ // GetEnumerator method of the first IEnumerable<T> interface we successfully QI'ed for
+ PTR_MethodDesc m_pGetEnumeratorMethod;
+
+ // Interfaces with variance that we successfully QI'ed for
+ ArrayList *m_prVariantInterfaces;
+
+ // Lock to protect concurrent access to m_prVariantInterfaces
+ CrstExplicitInit m_VarianceCacheCrst;
+
+ // Linked list of cached interface pointers
+ PTR_InterfaceEntryEx m_pInterfaceCache;
+
+ // Cached object handles wrapping delegate objects that point to the right GetEnumerator/Indexer_Get
+ // stubs that should be used when calling these methods via IEnumerable<object>/IReadOnlyList<object>.
+ // Can also contain the special VARIANCE_STUB_TARGET_USE_STRING and VARIANCE_STUB_TARGET_USE_T values.
+ OBJECTHANDLE m_ohObjectVariantCallTarget_IEnumerable; // GetEnumerator
+ OBJECTHANDLE m_ohObjectVariantCallTarget_IReadOnlyList; // Indexer_Get
+
+ // Rarely used RCW flags (keep the commonly used ones in code:RCW::RCWFlags)
+ union RCWAuxFlags
+ {
+ DWORD m_dwFlags;
+
+ struct
+ {
+ // InterfaceVarianceBehavior for rarely used instantiations that could be supported via string:
+ DWORD m_InterfaceVarianceBehavior_OfIEnumerable:4;
+ DWORD m_InterfaceVarianceBehavior_OfIEnumerableOfChar:4;
+ };
+ }
+ m_AuxFlags;
+};
+
+typedef DPTR(RCW) PTR_RCW;
+
+//----------------------------------------------------------------------------
+// RCW, internal class
+// caches the IPs for a single com object, this wrapper is
+// not in the GC heap, this allows us to grab a pointer to this block
+// and play with-it without worrying about GC
+struct RCW
+{
+ enum CreationFlags
+ {
+ CF_None = 0x00,
+ CF_SupportsIInspectable = 0x01, // the underlying object supports IInspectable
+ CF_QueryForIdentity = 0x02, // Need to QI for the real identity IUnknown during creating RCW
+ CF_IsWeakReference = 0x04, // mark the RCW as "weak"
+ CF_NeedUniqueObject = 0x08, // always create a new RCW/object even if we have one cached already
+ CF_DontResolveClass = 0x10, // don't attempt to create a strongly typed RCW
+ CF_DetectDCOMProxy = 0x20, // attempt to determine if the RCW is for a DCOM proxy
+ };
+
+ static CreationFlags CreationFlagsFromObjForComIPFlags(ObjFromComIP::flags flags);
+
+ // List of RCW instances that have been freed since the last RCW cleanup.
+ static SLIST_HEADER s_RCWStandbyList;
+
+ // Simple read-only iterator for all cached interface pointers.
+ class CachedInterfaceEntryIterator
+ {
+ PTR_RCW m_pRCW;
+ int m_InlineCacheIndex;
+ RCWAuxiliaryData::InterfaceEntryIterator m_AuxIterator;
+
+ public:
+ inline CachedInterfaceEntryIterator(PTR_RCW pRCW)
+ : m_AuxIterator(pRCW->m_pAuxiliaryData)
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_pRCW = pRCW;
+ m_InlineCacheIndex = -1;
+ }
+
+ // Move to the next item returning TRUE if an item exists or FALSE if we've run off the end
+ inline bool Next()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_InlineCacheIndex < INTERFACE_ENTRY_CACHE_SIZE)
+ {
+ // stop incrementing m_InlineCacheIndex once we reach INTERFACE_ENTRY_CACHE_SIZE
+ if (++m_InlineCacheIndex < INTERFACE_ENTRY_CACHE_SIZE)
+ {
+ return TRUE;
+ }
+ }
+ return m_AuxIterator.Next();
+ }
+
+ inline InterfaceEntry *GetEntry()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE_MSG(m_InlineCacheIndex >= 0, "Iterator starts before the first element, you need to call Next");
+ if (m_InlineCacheIndex < INTERFACE_ENTRY_CACHE_SIZE)
+ {
+ return &m_pRCW->m_aInterfaceEntries[m_InlineCacheIndex];
+ }
+ return m_AuxIterator.GetEntry();
+ }
+
+ inline LPVOID GetCtxCookie()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE_MSG(m_InlineCacheIndex >= 0, "Iterator starts before the first element, you need to call Next");
+ if (m_InlineCacheIndex < INTERFACE_ENTRY_CACHE_SIZE)
+ {
+ return m_pRCW->GetWrapperCtxCookie();
+ }
+ return m_AuxIterator.GetCtxCookie();
+ }
+ };
+
+ // constructor
+ RCW()
+ {
+ WRAPPER_NO_CONTRACT;
+ ZeroMemory(this, sizeof(*this));
+ }
+
+ // Deletes all items in code:s_RCWStandbyList.
+ static void FlushStandbyList();
+
+ // Create a new wrapper for given IUnk, IDispatch
+ static RCW* CreateRCW(IUnknown *pUnk, DWORD dwSyncBlockIndex, DWORD flags, MethodTable *pClassMT);
+
+ //-------------------------------------------------
+ // initialize IUnknown and Identity, and associate with the managed object.
+ void Initialize(IUnknown* pUnk, DWORD dwSyncBlockIndex, MethodTable *pClassMT);
+
+ enum MarshalingType
+ {
+ MarshalingType_Unknown = 0, /* The MarshalingType has not been set*/
+ MarshalingType_Inhibit = 1, /* This value is same as the MarshalingType.Inhibit*/
+ MarshalingType_FreeThreaded = 2, /* This value is same as the MarshalingType.FreeThreaded*/
+ MarshalingType_Standard = 3 /* This value is same as the MarshalingType.Standard*/
+ };
+
+ //-------------------------------------------------
+ // Get the MarshalingType of the associated managed object.
+ MarshalingType GetMarshalingType(IUnknown* pUnk, MethodTable *pClassMT);
+
+
+ //-----------------------------------------------
+ // Free GC handle and remove SyncBlock entry
+ void DecoupleFromObject();
+
+ //---------------------------------------------------
+ // Cleanup free all interface pointers
+ void Cleanup();
+
+ //-----------------------------------------------------
+ // called during GC to do minor cleanup and schedule the ips to be
+ // released
+ void MinorCleanup();
+
+ //-----------------------------------------------------
+ // The amount of GC pressure we apply has one of a few possible values.
+ // We save space in the RCW structure by tracking this instead of the
+ // actual value.
+ enum GCPressureSize
+ {
+ GCPressureSize_None = 0,
+ GCPressureSize_ProcessLocal = 1,
+ GCPressureSize_MachineLocal = 2,
+ GCPressureSize_Remote = 3,
+ GCPressureSize_WinRT_Base = 4,
+ GCPressureSize_WinRT_Low = 5,
+ GCPressureSize_WinRT_Medium = 6,
+ GCPressureSize_WinRT_High = 7,
+ GCPressureSize_COUNT = 8
+ };
+
+ //---------------------------------------------------
+ // Add memory pressure to the GC representing the native cost
+ void AddMemoryPressure(GCPressureSize pressureSize);
+
+ //---------------------------------------------------
+ // Remove memory pressure from the GC representing the native cost
+ void RemoveMemoryPressure();
+
+ //-----------------------------------------------------
+ // AddRef
+ LONG AddRef(RCWCache* pCache);
+
+ //-----------------------------------------------------
+ // Release
+ static INT32 ExternalRelease(OBJECTREF* objPROTECTED);
+ static void FinalExternalRelease(OBJECTREF* objPROTECTED);
+
+ // Create a new wrapper for a different method table that represents the same
+ // COM object as the original wrapper.
+ void CreateDuplicateWrapper(MethodTable *pNewMT, RCWHolder* pNewRCW);
+
+ AppDomain* GetDomain();
+
+#ifndef DACCESS_COMPILE
+
+ //-------------------------------------------------
+ // return exposed ComObject
+ COMOBJECTREF GetExposedObject()
+ {
+ CONTRACT(COMOBJECTREF)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(m_SyncBlockIndex != 0);
+ POSTCONDITION(RETVAL != NULL);
+ }
+ CONTRACT_END;
+
+ RETURN (COMOBJECTREF) ObjectToOBJECTREF(g_pSyncTable[m_SyncBlockIndex].m_Object);
+ }
+
+ //-------------------------------------------------
+ // returns the sync block for the RCW
+ SyncBlock *GetSyncBlock()
+ {
+ CONTRACT(SyncBlock*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(m_SyncBlockIndex != 0);
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ RETURN g_pSyncTable[m_SyncBlockIndex].m_SyncBlock;
+ }
+
+ //--------------------------------------------------------------------------
+ // out of line call, takes a lock, does a QI if the interface was not found in local cache
+ IUnknown* GetComIPFromRCW(MethodTable* pMT);
+
+ //-----------------------------------------------------------------
+ // out of line call
+ IUnknown* GetComIPFromRCW(REFIID iid);
+
+#endif // #ifndef DACCESS_COMPILE
+
+ enum InterfaceRedirectionKind
+ {
+ InterfaceRedirection_None,
+ InterfaceRedirection_IEnumerable, // IEnumerable`1 - based interface
+ InterfaceRedirection_IEnumerable_RetryOnFailure, // IEnumerable`1 - based interface, retry on QI failure
+ InterfaceRedirection_UnresolvedIEnumerable, // unknown IEnumerable`1 instantiation
+ InterfaceRedirection_Other, // other interface
+ InterfaceRedirection_Other_RetryOnFailure, // non-generic redirected interface
+ };
+
+ // Returns a redirected collection interface corresponding to a given ICollection<T>, IReadOnlyCollection<T>, or NULL.
+ static MethodTable *ResolveICollectionInterface(MethodTable *pItfMT, BOOL fPreferIDictionary, BOOL *pfChosenIDictionary);
+
+ // Returns an interface with variance corresponding to pMT or NULL if pMT does not support variance.
+ static MethodTable *GetVariantMethodTable(MethodTable *pMT);
+ static MethodTable *ComputeVariantMethodTable(MethodTable *pMT);
+
+ // Determines the interface that should be QI'ed for when the RCW is cast to pItfMT.
+ // Returns the kind of interface redirection that has been performed.
+ InterfaceRedirectionKind GetInterfaceForQI(MethodTable *pItfMT, MethodTable **pNewItfMT);
+ static InterfaceRedirectionKind GetInterfacesForQI(MethodTable *pItfMT, MethodTable **ppNewItfMT1, MethodTable **ppNewItfMT2);
+ static InterfaceRedirectionKind ComputeInterfacesForQI(MethodTable *pItfMT, MethodTable **ppNewItfMT1, MethodTable **ppNewItfMT2);
+
+ // Performs QI for the given interface, optionally instantiating it with the given generic args.
+ HRESULT CallQueryInterface(MethodTable *pMT, Instantiation inst, IID *piid, IUnknown **ppUnk);
+
+ // Performs QI for interfaces that are castable to pMT using co-/contra-variance.
+ HRESULT CallQueryInterfaceUsingVariance(MethodTable *pMT, IUnknown **ppUnk);
+
+ // Returns the GetEnumerator method of the first IEnumerable<T> this RCW was successfully
+ // cast to, or NULL if no such cast has ever succeeded.
+ MethodDesc *GetGetEnumeratorMethod();
+
+ // Sets the first "known" GetEnumerator method if not set already.
+ void SetGetEnumeratorMethod(MethodTable *pMT);
+
+ // Retrieve cached GetEnumerator method or compute the right one for a specific type
+ static MethodDesc *GetOrComputeGetEnumeratorMethodForType(MethodTable *pMT);
+
+ // Compute the first GetEnumerator for a specific type
+ static MethodDesc *ComputeGetEnumeratorMethodForType(MethodTable *pMT);
+
+ // Get the GetEnumerator method for IEnumerable<T> or IIterable<T>
+ static MethodDesc *ComputeGetEnumeratorMethodForTypeInternal(MethodTable *pMT);
+
+ // Notifies the RCW of an interface that is known to be supported by the COM object.
+ void SetSupportedInterface(MethodTable *pItfMT, Instantiation originalInst);
+
+ //-----------------------------------------------------------------
+ // Retrieve correct COM IP for the current apartment.
+ // use the cache /update the cache
+ IUnknown* GetComIPForMethodTableFromCache(MethodTable * pMT);
+
+ // helpers to get to IUnknown, IDispatch, and IInspectable interfaces
+ // Returns an addref'd pointer - caller must Release
+ IUnknown* GetWellKnownInterface(REFIID riid);
+
+ IUnknown* GetIUnknown();
+ IUnknown* GetIUnknown_NoAddRef();
+ IDispatch* GetIDispatch();
+ IInspectable* GetIInspectable();
+
+ ULONG GetRefCount()
+ {
+ return m_cbRefCount;
+ }
+
+ IJupiterObject *GetJupiterObjectNoCheck()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(IsJupiterObject());
+
+ // We saved IJupiterObject * on the first slot
+ _ASSERTE((IUnknown *)m_aInterfaceEntries[0].m_pUnknown != NULL);
+ _ASSERTE((MethodTable *)m_aInterfaceEntries[0].m_pMT == NULL);
+
+ return (IJupiterObject *)m_aInterfaceEntries[0].m_pUnknown.Load();
+ }
+
+ IJupiterObject *GetJupiterObject()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (IsJupiterObject())
+ {
+ return GetJupiterObjectNoCheck();
+ }
+
+ return NULL;
+ }
+
+ void GetCachedInterfaceTypes(BOOL bIInspectableOnly,
+ SArray<PTR_MethodTable> * rgItfTables)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ CachedInterfaceEntryIterator it = IterateCachedInterfacePointers();
+ while (it.Next())
+ {
+ PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
+ if (pMT != NULL &&
+ (!bIInspectableOnly || pMT->IsProjectedFromWinRT() || pMT->SupportsGenericInterop(TypeHandle::Interop_NativeToManaged)))
+ {
+ // Don't return mscorlib-internal declarations of WinRT types.
+ if (!(pMT->GetModule()->IsSystem() && pMT->IsProjectedFromWinRT()))
+ {
+ rgItfTables->Append(pMT);
+ }
+ }
+ }
+ }
+
+ void GetCachedInterfacePointers(BOOL bIInspectableOnly,
+ SArray<TADDR> * rgItfPtrs)
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ CachedInterfaceEntryIterator it = IterateCachedInterfacePointers();
+ while (it.Next())
+ {
+ PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
+ if (pMT != NULL &&
+ (!bIInspectableOnly || pMT->IsProjectedFromWinRT() || pMT->SupportsGenericInterop(TypeHandle::Interop_NativeToManaged)))
+ {
+ TADDR taUnk = (TADDR)(it.GetEntry()->m_pUnknown.Load());
+ if (taUnk != NULL)
+ {
+ rgItfPtrs->Append(taUnk);
+ }
+ }
+ }
+ }
+
+ // Save IJupiterObject * on the first slot
+ // Only call this in Initialize code
+ void SetJupiterObject(IJupiterObject *pJupiterObject)
+ {
+
+ LIMITED_METHOD_CONTRACT;
+
+ m_Flags.m_fIsJupiterObject = 1;
+
+ //
+ // Save pJupiterObject* on the first SLOT
+ // Only AddRef if not aggregated
+ //
+ _ASSERTE(m_aInterfaceEntries[0].IsFree());
+
+ m_aInterfaceEntries[0].Init(NULL, pJupiterObject);
+ }
+
+ LPVOID GetVTablePtr() { LIMITED_METHOD_CONTRACT; return m_vtablePtr; }
+
+ // Remoting aware QI that will attempt to re-unmarshal on object disconnect.
+ HRESULT SafeQueryInterfaceRemoteAware(REFIID iid, IUnknown** pResUnk);
+
+ BOOL IsValid()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_SyncBlockIndex != 0;
+ }
+
+ BOOL SupportsIProvideClassInfo();
+
+ VOID MarkURTAggregated();
+
+ VOID MarkURTContained()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(m_Flags.m_fURTAggregated == 0);
+ }
+ CONTRACTL_END;
+
+ m_Flags.m_fURTContained = 1;
+ }
+
+
+ BOOL IsURTAggregated()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_Flags.m_fURTAggregated == 1;
+ }
+
+ BOOL IsURTContained()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_Flags.m_fURTContained == 1;
+ }
+
+ BOOL SupportsIInspectable()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_Flags.m_fSupportsIInspectable == 1;
+ }
+
+ //
+ // This COM object aggregates FTM?
+ //
+ bool IsFreeThreaded()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return (m_Flags.m_MarshalingType == MarshalingType_FreeThreaded) ;
+ }
+
+ //
+ // Is this COM object a DCOM Proxy? (For WinRT the RCW must have been created with CF_DetectDCOMProxy)
+ //
+ bool IsDCOMProxy()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_Flags.m_fIsDCOMProxy == 1;
+ }
+
+ //
+ // This COM object implements INoMarshal?
+ //
+ bool IsMarshalingInhibited()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (m_Flags.m_MarshalingType == MarshalingType_Inhibit) ;
+ }
+
+ BOOL IsJupiterObject()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_Flags.m_fIsJupiterObject == 1;
+ }
+
+ // Returns TRUE if this RCW has been detached. Detached RCWs are fully functional but have been found
+ // dead during GC, before finalizable/f-reachable objects were promoted. If we ever find such an RCW
+ // in the RCW cache during marshaling (i.e. an interface pointer with the same identity enters managed
+ // code), we re-insert it as "unique", and create a new RCW. This is to prevent unexpected resurrection
+ // of objects that may already be finalized.
+ BOOL IsDetached()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_Flags.m_Detached == 1;
+ }
+
+ BOOL MatchesCleanupBucket(RCW *pOtherRCW)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (IsFreeThreaded() == pOtherRCW->IsFreeThreaded() &&
+ m_Flags.m_fAllowEagerSTACleanup == pOtherRCW->m_Flags.m_fAllowEagerSTACleanup &&
+ GetSTAThread() == pOtherRCW->GetSTAThread() &&
+ GetWrapperCtxCookie() == pOtherRCW->GetWrapperCtxCookie()
+ );
+ }
+
+ // Note that this is not a simple field getter
+ BOOL AllowEagerSTACleanup();
+
+ // GetWrapper context cookie
+ LPVOID GetWrapperCtxCookie()
+ {
+ CONTRACT (LPVOID)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ RETURN m_UnkEntry.m_pCtxCookie;
+ }
+
+ inline Thread *GetSTAThread()
+ {
+ CONTRACT (Thread *)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ CtxEntry *pCtxEntry = GetWrapperCtxEntryNoRef();
+ if (pCtxEntry)
+ RETURN pCtxEntry->GetSTAThread();
+ RETURN NULL;
+ }
+
+ // Function to enter the context. The specified callback function will
+ // be called from within the context.
+ HRESULT EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData);
+
+ inline CachedInterfaceEntryIterator IterateCachedInterfacePointers()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return CachedInterfaceEntryIterator(dac_cast<PTR_RCW>(this));
+ }
+
+ //---------------------------------------------------------------------
+ // Returns RCWAuxiliaryData associated with this RCW. Allocates the
+ // structure if it does not exist already.
+ PTR_RCWAuxiliaryData GetOrCreateAuxiliaryData();
+
+ //---------------------------------------------------------------------
+ // Returns true iff pItfMT is a "standard managed" interface, such as
+ // IEnumerator, and the RCW supports the interface through classic COM
+ // interop mechanisms.
+ bool SupportsMngStdInterface(MethodTable *pItfMT);
+
+ //---------------------------------------------------------------------
+ // Determines whether a call through the given interface should use new
+ // WinRT interop (as opposed to classic COM). pItfMT should be a non-generic
+ // redirected interface such as IEnumerable whose interop behavior is
+ // ambiguous. This is a NoGC variant, if it returns TypeHandle::MaybeCast,
+ // SupportsWinRTInteropInterface should be called.
+ TypeHandle::CastResult SupportsWinRTInteropInterfaceNoGC(MethodTable *pItfMT);
+
+ //---------------------------------------------------------------------
+ // This is a GC-triggering variant of code:SupportsWinRTInteropInterfaceNoGC.
+ bool SupportsWinRTInteropInterface(MethodTable *pItfMT);
+
+ //---------------------------------------------------------------------
+ // True if the object supports legacy (not WinRT) IEnumerable marshaling.
+ bool SupportsLegacyEnumerableInterface()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(SupportsWinRTInteropInterfaceNoGC(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE)) == TypeHandle::CannotCast);
+ return m_Flags.m_RedirectionBehavior_IEnumerable_LegacySupported;
+ }
+
+ enum RedirectionBehavior
+ {
+ RedirectionBehaviorComputed = 1, // the second bit is valid
+ RedirectionBehaviorEnabled = 2 // if RedirectionBehaviorComputed is set, true means the interface is redirected on this RCW
+ };
+
+ enum InterfaceVarianceBehavior
+ {
+ IEnumerableSupported = 1, // IEnumerable<T> is supported on this RCW
+ IEnumerableSupportedViaStringInstantiation = 2, // the object failed QI for IEnumerable<T> but succeeded QI for IEnumerable<string>
+
+ IReadOnlyListSupported = 4, // IReadOnlyList<T> is supported on this RCW
+ IReadOnlyListSupportedViaStringInstantiation = 8, // the object failed QI for IReadOnlyList<T> but succeeded QI for IReadOnlyList<string>
+ };
+
+ // Returns a delegate object that points to the right GetEnumerator/Indexer_Get stub that should be used when calling these methods via
+ // IEnumerable<object>/IReadOnlyList<object> or NULL in which case the BOOL argument are relevant:
+ // *pfUseString == true means that the caller should use IEnumerable<string>/IReadOnlyList<string>
+ // *pfUseT == true means that the caller should handle the call as normal, i.e. invoking the stub instantiated over T.
+ OBJECTREF GetTargetForAmbiguousVariantCall(BOOL fIsEnumerable, WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType, BOOL *pfUseString, BOOL *pfUseT)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pAuxiliaryData != NULL)
+ {
+ if (baseType == WinRTInterfaceRedirector::BaseType_Object)
+ {
+ if (fIsEnumerable)
+ {
+ if (VARIANCE_STUB_TARGET_IS_HANDLE(m_pAuxiliaryData->m_ohObjectVariantCallTarget_IEnumerable))
+ return ObjectFromHandle(m_pAuxiliaryData->m_ohObjectVariantCallTarget_IEnumerable);
+
+ if (m_pAuxiliaryData->m_ohObjectVariantCallTarget_IEnumerable == VARIANCE_STUB_TARGET_USE_STRING)
+ *pfUseString = TRUE;
+ else if (m_pAuxiliaryData->m_ohObjectVariantCallTarget_IEnumerable == VARIANCE_STUB_TARGET_USE_T)
+ *pfUseT = TRUE;
+ }
+ else
+ {
+ if (VARIANCE_STUB_TARGET_IS_HANDLE(m_pAuxiliaryData->m_ohObjectVariantCallTarget_IReadOnlyList))
+ return ObjectFromHandle(m_pAuxiliaryData->m_ohObjectVariantCallTarget_IReadOnlyList);
+
+ if (m_pAuxiliaryData->m_ohObjectVariantCallTarget_IReadOnlyList == VARIANCE_STUB_TARGET_USE_STRING)
+ *pfUseString = TRUE;
+ else if (m_pAuxiliaryData->m_ohObjectVariantCallTarget_IReadOnlyList == VARIANCE_STUB_TARGET_USE_T)
+ *pfUseT = TRUE;
+ }
+ }
+ else
+ {
+ InterfaceVarianceBehavior varianceBehavior = (baseType == WinRTInterfaceRedirector::BaseType_IEnumerable) ?
+ (InterfaceVarianceBehavior)m_pAuxiliaryData->m_AuxFlags.m_InterfaceVarianceBehavior_OfIEnumerable :
+ (InterfaceVarianceBehavior)m_pAuxiliaryData->m_AuxFlags.m_InterfaceVarianceBehavior_OfIEnumerableOfChar;
+
+ if (fIsEnumerable)
+ {
+ if ((varianceBehavior & IEnumerableSupported) != 0)
+ {
+ if ((varianceBehavior & IEnumerableSupportedViaStringInstantiation) != 0)
+ *pfUseString = TRUE;
+ else
+ *pfUseT = TRUE;
+ }
+ }
+ else
+ {
+ if ((varianceBehavior & IReadOnlyListSupported) != 0)
+ {
+ if ((varianceBehavior & IReadOnlyListSupportedViaStringInstantiation) != 0)
+ *pfUseString = TRUE;
+ else
+ *pfUseT = TRUE;
+ }
+ }
+ }
+ }
+ return NULL;
+ }
+
+#ifdef _DEBUG
+ // Does not throw if m_UnkEntry.m_pUnknown is no longer valid, debug only.
+ IUnknown *GetRawIUnknown_NoAddRef_NoThrow()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_UnkEntry.GetRawIUnknown_NoAddRef_NoThrow();
+ }
+#endif // _DEBUG
+
+ IUnknown *GetRawIUnknown_NoAddRef()
+ {
+ WRAPPER_NO_CONTRACT;
+ return m_UnkEntry.GetRawIUnknown_NoAddRef();
+ }
+
+ bool IsDisconnected()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_UnkEntry.IsDisconnected();
+ }
+
+ void IncrementUseCount()
+ {
+ LIMITED_METHOD_CONTRACT;
+ InterlockedIncrement(&m_cbUseCount);
+ }
+
+ void DecrementUseCount()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (InterlockedDecrement(&m_cbUseCount) == 0)
+ {
+ // this was the final decrement, go ahead and delete/recycle the RCW
+ {
+ GCX_PREEMP();
+ m_UnkEntry.Free();
+ }
+
+ if (g_fEEShutDown)
+ {
+ delete this;
+ }
+ else
+ {
+ InterlockedPushEntrySList(&RCW::s_RCWStandbyList, (PSLIST_ENTRY)this);
+ }
+ }
+ }
+
+private:
+ //---------------------------------------------------------------------
+ // Computes the result of code:SupportsWinRTInteropInterface.
+ RedirectionBehavior ComputeRedirectionBehavior(MethodTable *pItfMT, bool *pfLegacySupported);
+
+ //---------------------------------------------------------------------
+ // Callback called to release the interfaces in the auxiliary cache.
+ static HRESULT __stdcall ReleaseAuxInterfacesCallBack(LPVOID pData);
+
+ //---------------------------------------------------------------------
+ // Callback called to release the IUnkEntry and the InterfaceEntries,
+ static HRESULT __stdcall ReleaseAllInterfacesCallBack(LPVOID pData);
+
+ //---------------------------------------------------------------------
+ // Helper function called from ReleaseAllInterfaces_CallBack do do the
+ // actual releases.
+ void ReleaseAllInterfaces();
+
+public:
+ // Points to the next RCW bucket if this RCW is part of a code:RCWCleanupList
+ PTR_RCW m_pNextCleanupBucket;
+
+ // interface entries
+ InterfaceEntry m_aInterfaceEntries[INTERFACE_ENTRY_CACHE_SIZE];
+
+ // Identity
+ LPVOID m_pIdentity;
+
+ // Sync block index for the exposed managed object
+ DWORD m_SyncBlockIndex;
+
+ //ref-count
+ ULONG m_cbRefCount;
+
+ // Wrapper Cache
+ RCWCache* m_pRCWCache;
+
+ // thread in which the wrapper has been created
+ // if this thread is an STA thread, then when the STA dies
+ // we need to cleanup this wrapper
+ Thread* m_pCreatorThread;
+
+ union RCWFlags
+ {
+ DWORD m_dwFlags;
+
+ struct
+ {
+ static_assert((1 << 4) > INTERFACE_ENTRY_CACHE_SIZE, "m_iEntryToRelease needs a bigger data type");
+ DWORD m_iEntryToRelease:4;
+
+ DWORD m_fURTAggregated:1; // this RCW represents a COM object aggregated by a managed object
+ DWORD m_fURTContained:1; // this RCW represents a COM object contained by a managed object
+ DWORD m_fAllowEagerSTACleanup:1; // this RCW can be cleaned up eagerly (as opposed to via CleanupUnusedObjectsInCurrentContext)
+ DWORD m_fSupportsIInspectable:1; // the underlying COM object is known to support IInspectable
+ DWORD m_fIsJupiterObject:1; // this RCW represents a COM object from Jupiter
+
+ static_assert((1 << 3) >= GCPressureSize_COUNT, "m_GCPressure needs a bigger data type");
+ DWORD m_GCPressure:3; // index into s_rGCPressureTable
+
+ // RedirectionBehavior of non-generic redirected interfaces:
+ DWORD m_RedirectionBehavior_IEnumerable:2;
+ DWORD m_RedirectionBehavior_IEnumerable_LegacySupported:1; // one extra bit for IEnumerable
+
+ DWORD m_RedirectionBehavior_ICollection:2;
+ DWORD m_RedirectionBehavior_IList:2;
+ DWORD m_RedirectionBehavior_INotifyCollectionChanged:2;
+ DWORD m_RedirectionBehavior_INotifyPropertyChanged:2;
+ DWORD m_RedirectionBehavior_ICommand:2;
+ DWORD m_RedirectionBehavior_IDisposable:2;
+
+ // Reserve 2 bits for marshaling behavior
+ DWORD m_MarshalingType:2; // MarshalingBehavior of the COM object.
+
+ DWORD m_Detached:1; // set if the RCW was found dead during GC
+
+ DWORD m_fIsDCOMProxy:1; // Is the object a proxy to a remote process
+ };
+ }
+ m_Flags;
+
+ static_assert(sizeof(RCWFlags) == 4, "Flags don't fit in 4 bytes, there's too many of them");
+
+ // GC pressure sizes in bytes
+ static const int s_rGCPressureTable[GCPressureSize_COUNT];
+
+ // Tracks concurrent access to this RCW to prevent using RCW instances that have already been released
+ LONG m_cbUseCount;
+
+ // additional RCW data used for generic interop and advanced interface pointer caching (NULL unless needed)
+ PTR_RCWAuxiliaryData m_pAuxiliaryData;
+
+ PTR_RCW m_pNextRCW;
+
+ // This field is useful for debugging purposes, please do not remove. The typical scenario is a crash in
+ // SafeRelease because the COM object disappeared. Knowing the vtable usually helps find the culprit.
+ LPVOID m_vtablePtr;
+
+private :
+ // cookies for tracking IUnknown on the correct thread
+ IUnkEntry m_UnkEntry;
+
+ // IUnkEntry needs to access m_UnkEntry field
+ friend IUnkEntry;
+
+private :
+ static RCW* CreateRCWInternal(IUnknown *pUnk, DWORD dwSyncBlockIndex, DWORD flags, MethodTable *pClassMT);
+
+ // Returns an addref'ed context entry
+ CtxEntry* GetWrapperCtxEntry()
+ {
+ CONTRACT (CtxEntry*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(!IsFreeThreaded()); // Must not be free-threaded, otherwise CtxEntry = NULL
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ CtxEntry *pCtxEntry = m_UnkEntry.GetCtxEntry();
+ pCtxEntry->AddRef();
+ RETURN pCtxEntry;
+ }
+
+ // Returns an non-addref'ed context entry
+ CtxEntry *GetWrapperCtxEntryNoRef()
+ {
+ CONTRACT (CtxEntry *)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ CtxEntry *pCtxEntry = m_UnkEntry.GetCtxEntry();
+ RETURN pCtxEntry;
+ }
+};
+
+inline RCW::CreationFlags operator|(RCW::CreationFlags lhs, RCW::CreationFlags rhs)
+{
+ LIMITED_METHOD_CONTRACT;
+ return static_cast<RCW::CreationFlags>(static_cast<DWORD>(lhs) | static_cast<DWORD>(rhs));
+}
+inline RCW::CreationFlags operator|=(RCW::CreationFlags & lhs, RCW::CreationFlags rhs)
+{
+ LIMITED_METHOD_CONTRACT;
+ lhs = static_cast<RCW::CreationFlags>(static_cast<DWORD>(lhs) | static_cast<DWORD>(rhs));
+ return lhs;
+}
+
+// In order to save vtablePtr in minidumps, we put it on the stack as a volatile local
+// (so it's not optimized away by the compiler). Most places where we call out to COM
+// can absorb the cost of one stack slot and one instruction to improve debuggability.
+#define RCW_VTABLEPTR(pRCW) Volatile<LPVOID> __vtablePtr = (pRCW)->m_vtablePtr
+
+
+// 01 REQUIRE_IINSPECTABLE 01 ITF_MARSHAL_INSP_ITF 01 CF_SupportsIInspectable
+// 02 SUPPRESS_ADDREF 02 ITF_MARSHAL_SUPPRESS_ADDREF 02 CF_SuppressAddRef
+// 04 CF_IsWeakReference
+// 04 CLASS_IS_HINT 04 ITF_MARSHAL_CLASS_IS_HINT
+// 08 UNIQUE_OBJECT 08 CF_NeedUniqueObject
+// 08 ITF_MARSHAL_DISP_ITF
+// 10 IGNORE_WINRT_AND_SKIP_UNBOXING 10 CF_DontResolveClass
+// 10 ITF_MARSHAL_USE_BASIC_ITF
+// 20 ITF_MARSHAL_WINRT_SCENARIO
+inline RCW::CreationFlags RCW::CreationFlagsFromObjForComIPFlags(ObjFromComIP::flags dwFlags)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ static_assert_no_msg(CF_NeedUniqueObject == ObjFromComIP::UNIQUE_OBJECT);
+ static_assert_no_msg(CF_SupportsIInspectable == ObjFromComIP::REQUIRE_IINSPECTABLE);
+ static_assert_no_msg(CF_DontResolveClass == ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING);
+
+ RCW::CreationFlags result = (RCW::CreationFlags)(dwFlags &
+ (ObjFromComIP::UNIQUE_OBJECT
+ | ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING));
+ if ((dwFlags & (ObjFromComIP::REQUIRE_IINSPECTABLE|ObjFromComIP::CLASS_IS_HINT))
+ == (ObjFromComIP::REQUIRE_IINSPECTABLE|ObjFromComIP::CLASS_IS_HINT))
+ {
+ result |= CF_SupportsIInspectable;
+ }
+ return result;
+}
+
+
+// RCW data attached to MethodTable's that represent interesting types. Types without RCWPerTypeData
+// (i.e. those with MethodTable::GetRCWPerTypeData() == NULL) are not interesting and are assumed to
+// use NULL/default values for m_pVariantMT/m_pMTForQI1/m_pMTForQI2/m_pGetEnumeratorMethod.
+struct RCWPerTypeData
+{
+ // Corresponding type with variance or NULL if the type does not exhibit variant behavior.
+ MethodTable *m_pVariantMT;
+
+ // Types that should be used for QI. m_pMTForQI1 is tried first; if it fails and m_pMTForQI2
+ // is not NULL, QI for m_pMTForQI2 is performed. We need two types to supports ambiguous casts
+ // to ICollection<KeyValuePair<K, V>>.
+ MethodTable *m_pMTForQI1;
+ MethodTable *m_pMTForQI2;
+
+ // The corresponding IEnumerator<T>::GetEnumerator instantiation or NULL if the type does not
+ // act like IEnumerable.
+ MethodDesc *m_pGetEnumeratorMethod;
+
+ // The kind of redirection performed by QI'ing for m_pMTForQI1.
+ RCW::InterfaceRedirectionKind m_RedirectionKind;
+
+ enum
+ {
+ VariantTypeInited = 0x01, // m_pVariantMT is set
+ RedirectionInfoInited = 0x02, // m_pMTForQI1, m_pMTForQI2, and m_RedirectionKind are set
+ GetEnumeratorInited = 0x04, // m_pGetEnumeratorMethod is set
+ InterfaceFlagsInited = 0x08, // IsRedirectedInterface and IsICollectionGeneric are set
+
+ IsRedirectedInterface = 0x10, // the type is a redirected interface
+ IsICollectionGeneric = 0x20, // the type is ICollection`1
+ };
+ DWORD m_dwFlags;
+};
+
+#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
+
+class ComClassFactory;
+class WinRTClassFactory;
+class WinRTManagedClassFactory;
+
+class ClassFactoryBase
+{
+public:
+ //-------------------------------------------------------------
+ // Function to clean up
+ virtual void Cleanup() = 0;
+
+ ComClassFactory *AsComClassFactory()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_pClassMT == NULL || (!m_pClassMT->IsProjectedFromWinRT() && !m_pClassMT->IsExportedToWinRT()));
+ return (ComClassFactory *)this;
+ }
+
+ WinRTClassFactory *AsWinRTClassFactory()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_pClassMT->IsProjectedFromWinRT() || m_pClassMT->IsExportedToWinRT());
+ return (WinRTClassFactory *)this;
+ }
+
+ WinRTManagedClassFactory *AsWinRTManagedClassFactory()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_pClassMT->IsExportedToWinRT());
+ return (WinRTManagedClassFactory *)this;
+ }
+
+protected:
+ ClassFactoryBase(MethodTable *pClassMT = NULL)
+ : m_pClassMT(pClassMT)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ MethodTable *m_pClassMT;
+};
+
+class ComClassFactoryCreator;
+//-------------------------------------------------------------------------
+// Class that wraps an IClassFactory
+// This class allows a Reflection Class to wrap an IClassFactory
+// Class::GetClassFromProgID("ProgID", "Server") can be used to get a Class
+// object that wraps an IClassFactory.
+// Calling class.CreateInstance() will create an instance of the COM object and
+// wrap it with a RCW, the wrapper can be cast to the appropriate interface
+// and used.
+//
+class ComClassFactory : public ClassFactoryBase
+{
+protected:
+ friend ComClassFactoryCreator;
+
+ // We have two types of ComClassFactory:
+ // 1. We build for reflection purpose. We should not clean up.
+ // 2. We build for IClassFactory. We should clean up.
+ //-----------------------------------------------------------
+ // constructor
+ ComClassFactory(REFCLSID rclsid)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ m_pwszProgID = NULL;
+ m_pwszServer = NULL;
+
+ // Default to unmanaged version.
+ m_bManagedVersion = FALSE;
+ m_rclsid = rclsid;
+ }
+
+public :
+ //---------------------------------------------------------
+ // Mark this instance as Managed Version, so we will not do clean up.
+ void SetManagedVersion()
+ {
+ LIMITED_METHOD_CONTRACT;
+ m_bManagedVersion = TRUE;
+ }
+
+ //--------------------------------------------------------------
+ // Init the ComClassFactory
+ void Init(__in_opt WCHAR* pwszProgID, __in_opt WCHAR* pwszServer, MethodTable* pClassMT);
+
+ //-------------------------------------------------------------
+ // create instance, calls IClassFactory::CreateInstance
+ OBJECTREF CreateInstance(MethodTable* pMTClass, BOOL ForManaged = FALSE);
+
+ //-------------------------------------------------------------
+ // Function to clean up
+ void Cleanup();
+
+protected :
+#ifndef CROSSGEN_COMPILE
+ //-------------------------------------------------------------
+ // Create instance. Overridable from child classes
+ virtual IUnknown *CreateInstanceInternal(IUnknown *pOuter, BOOL *pfDidContainment);
+#endif
+ //-------------------------------------------------------------
+ // Throw exception message
+ void ThrowHRMsg(HRESULT hr, DWORD dwMsgResID);
+
+
+private:
+ //-------------------------------------------------------------
+ // ComClassFactory::CreateAggregatedInstance(MethodTable* pMTClass)
+ // create a COM+ instance that aggregates a COM instance
+ OBJECTREF CreateAggregatedInstance(MethodTable* pMTClass, BOOL ForManaged);
+
+ //--------------------------------------------------------------
+ // Retrieve the IClassFactory.
+ IClassFactory *GetIClassFactory();
+
+ //--------------------------------------------------------------
+ // Create an instance of the component from the class factory.
+ IUnknown *CreateInstanceFromClassFactory(IClassFactory *pClassFact, IUnknown *punkOuter, BOOL *pfDidContainment);
+
+public:;
+ WCHAR* m_pwszProgID; // progId
+ CLSID m_rclsid; // CLSID
+ WCHAR* m_pwszServer; // server name
+
+private:
+ BOOL m_bManagedVersion;
+};
+
+//
+// WinRT override information for ToString/GetHashCode/Equals
+//
+struct WinRTOverrideInfo
+{
+ MethodDesc *m_pToStringMD;
+ MethodDesc *m_pGetHashCodeMD;
+ MethodDesc *m_pEqualsMD;
+
+ WinRTOverrideInfo(EEClass *pClass);
+ static WinRTOverrideInfo *GetOrCreateWinRTOverrideInfo(MethodTable *pMT);
+ MethodDesc* GetIStringableToStringMD(MethodTable *pMT);
+};
+
+//--------------------------------------------------------------
+// Special ComClassFactory for AppX scenarios only
+// Call CoCreateInstanceFromApp to ensure compatibility
+class AppXComClassFactory : public ComClassFactory
+{
+protected :
+ friend ComClassFactoryCreator;
+
+ AppXComClassFactory(REFCLSID rclsid)
+ :ComClassFactory(rclsid)
+ {
+ LIMITED_METHOD_CONTRACT;
+ }
+
+protected :
+#ifndef CROSSGEN_COMPILE
+ //-------------------------------------------------------------
+ // Create instance using CoCreateInstanceFromApp
+ virtual IUnknown *CreateInstanceInternal(IUnknown *pOuter, BOOL *pfDidContainment);
+#endif
+};
+
+//--------------------------------------------------------------
+// Creates the right ComClassFactory for you
+class ComClassFactoryCreator
+{
+public :
+ static ComClassFactory *Create(REFCLSID rclsid)
+ {
+ CONTRACT(ComClassFactory *)
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACT_END;
+
+#ifdef FEATURE_APPX
+ if (AppX::IsAppXProcess())
+ RETURN new AppXComClassFactory(rclsid);
+ else
+#endif
+ RETURN new ComClassFactory(rclsid);
+ }
+};
+//-------------------------------------------------------------------------
+// Encapsulates data needed to instantiate WinRT runtime classes.
+class WinRTClassFactory : public ClassFactoryBase
+{
+public:
+ WinRTClassFactory(MethodTable *pClassMT)
+ : ClassFactoryBase(pClassMT)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_hClassName = NULL;
+ m_pDefaultItfMT = NULL;
+ m_pWinRTOverrideInfo = NULL;
+ m_GCPressure = RCW::GCPressureSize_WinRT_Base;
+ }
+
+ //-------------------------------------------------------------
+ // Initialize this instance by parsing factory-related attributes.
+ void Init();
+
+ //-------------------------------------------------------------
+ // Returns a factory method that matches the given signature.
+ MethodDesc *FindFactoryMethod(PCCOR_SIGNATURE pSig, DWORD cSig, Module *pModule);
+
+ //-------------------------------------------------------------
+ // Returns a static interface method that matches the given signature.
+ MethodDesc *FindStaticMethod(LPCUTF8 pszName, PCCOR_SIGNATURE pSig, DWORD cSig, Module *pModule);
+
+ //-------------------------------------------------------------
+ // Function to clean up
+ void Cleanup();
+
+ // If true, the class can be activated only using the composition pattern
+ BOOL IsComposition()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return !m_pClassMT->IsSealed();
+ }
+
+ MethodTable *GetClass()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pClassMT;
+ }
+
+ HSTRING GetClassName()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_hClassName;
+ }
+
+ SArray<MethodTable *> *GetFactoryInterfaces()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &m_factoryInterfaces;
+ }
+
+ SArray<MethodTable *> *GetStaticInterfaces()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return &m_staticInterfaces;
+ }
+
+ MethodTable *GetDefaultInterface()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pDefaultItfMT;
+ }
+
+ RCW::GCPressureSize GetGCPressure()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_GCPressure;
+ }
+
+ FORCEINLINE WinRTOverrideInfo *GetWinRTOverrideInfo ()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pWinRTOverrideInfo;
+ }
+
+ BOOL SetWinRTOverrideInfo (WinRTOverrideInfo *pWinRTOverrideInfo)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (InterlockedCompareExchangeT(&m_pWinRTOverrideInfo, pWinRTOverrideInfo, NULL) == NULL);
+ }
+
+protected:
+ MethodTable *GetTypeFromAttribute(IMDInternalImport *pImport, mdCustomAttribute tkAttribute);
+
+ HSTRING m_hClassName;
+
+ InlineSArray<MethodTable *, 1> m_factoryInterfaces;
+ InlineSArray<MethodTable *, 1> m_staticInterfaces;
+
+ MethodTable *m_pDefaultItfMT; // Default interface of the class
+
+ WinRTOverrideInfo *m_pWinRTOverrideInfo; // ToString/GetHashCode/GetValue override information
+
+ RCW::GCPressureSize m_GCPressure; // GC pressure size associated with instances of this class
+};
+#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
+
+//-------------------------------------------------------------------------
+// Encapsulates data needed to instantiate WinRT runtime classes implemented
+// in managed code.
+class WinRTManagedClassFactory : public WinRTClassFactory
+{
+public:
+ WinRTManagedClassFactory(MethodTable *pClassMT)
+ : WinRTClassFactory(pClassMT)
+ {
+ m_pCCWTemplate = NULL;
+ LIMITED_METHOD_CONTRACT;
+ }
+
+ //-------------------------------------------------------------
+ // Function to clean up
+ void Cleanup();
+
+ ComCallWrapperTemplate *GetComCallWrapperTemplate()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pCCWTemplate;
+ }
+
+ BOOL SetComCallWrapperTemplate(ComCallWrapperTemplate *pTemplate)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (InterlockedCompareExchangeT(&m_pCCWTemplate, pTemplate, NULL) == NULL);
+ }
+
+ ComCallWrapperTemplate *GetOrCreateComCallWrapperTemplate(MethodTable *pFactoryMT);
+
+protected:
+ ComCallWrapperTemplate *m_pCCWTemplate; // CCW template for the factory object
+};
+
+FORCEINLINE void NewRCWHolderRelease(RCW* p)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (p)
+ {
+ GCX_COOP();
+
+ p->DecoupleFromObject();
+ p->Cleanup();
+ }
+};
+
+class NewRCWHolder : public Wrapper<RCW*, NewRCWHolderDoNothing, NewRCWHolderRelease, NULL>
+{
+public:
+ NewRCWHolder(RCW* p = NULL)
+ : Wrapper<RCW*, NewRCWHolderDoNothing, NewRCWHolderRelease, NULL>(p)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ FORCEINLINE void operator=(RCW* p)
+ {
+ WRAPPER_NO_CONTRACT;
+ Wrapper<RCW*, NewRCWHolderDoNothing, NewRCWHolderRelease, NULL>::operator=(p);
+ }
+};
+
+#ifndef DACCESS_COMPILE
+class RCWHolder
+{
+public:
+ RCWHolder(PTR_Thread pThread)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(pThread));
+ }
+ CONTRACTL_END;
+
+ m_pThread = pThread;
+ m_pRCW = NULL;
+ m_pSB = NULL;
+ m_fValid = FALSE;
+ m_fRCWInUse = FALSE;
+#ifdef MDA_SUPPORTED
+ m_pMDA = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
+#endif // MDA_SUPPORTED
+ }
+
+ ~RCWHolder()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ if (m_fRCWInUse) GC_TRIGGERS; else GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+#ifdef MDA_SUPPORTED
+ // Unregister this RCW on the thread
+ if (m_pThread && m_pSB && m_fValid)
+ {
+ if (m_pMDA)
+ m_pThread->UnregisterRCW(INDEBUG(m_pSB));
+ }
+#endif // MDA_SUPPORTED
+
+ if (m_fRCWInUse)
+ {
+ m_pRCW->DecrementUseCount();
+ }
+ }
+
+ void Init(PTR_SyncBlock pSB)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(pSB));
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ }
+ CONTRACTL_END;
+
+ m_pSB = pSB;
+ m_pRCW = m_pSB->GetInteropInfoNoCreate()->GetRCWAndIncrementUseCount();
+
+ if (!m_pRCW)
+ {
+ COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER);
+ }
+ m_fRCWInUse = TRUE;
+
+#ifdef MDA_SUPPORTED
+ if (m_pMDA)
+ {
+ m_pThread->RegisterRCW(m_pRCW);
+ }
+#endif // MDA_SUPPORTED
+
+ m_fValid = TRUE;
+ }
+
+ void Init(OBJECTREF pObject)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ }
+ CONTRACTL_END;
+
+ Init(pObject->GetSyncBlock());
+ }
+
+ // Like Init() but does not increment the use count on the RCW. To be used on perf-critical code paths.
+ void InitFastCheck(PTR_SyncBlock pSB)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(pSB));
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ }
+ CONTRACTL_END;
+
+ m_pSB = pSB;
+ m_pRCW = m_pSB->GetInteropInfoNoCreate()->GetRawRCW();
+
+ if (!m_pRCW)
+ {
+ COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER);
+ }
+
+#ifdef MDA_SUPPORTED
+ if (m_pMDA)
+ {
+ m_pThread->RegisterRCW(m_pRCW);
+ }
+#endif // MDA_SUPPORTED
+
+ m_fValid = TRUE;
+ }
+
+ void InitNoCheck(PTR_SyncBlock pSB)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(pSB));
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ PRECONDITION(GetThread() == m_pThread);
+ }
+ CONTRACTL_END;
+
+ m_pSB = pSB;
+ m_pRCW = m_pSB->GetInteropInfoNoCreate()->GetRawRCW();
+
+#ifdef MDA_SUPPORTED
+ if (m_pMDA)
+ {
+ m_fValid = m_pThread->RegisterRCWNoThrow(m_pRCW);
+ }
+ else
+#endif // MDA_SUPPORTED
+ {
+ m_fValid = TRUE;
+ }
+ }
+
+ void InitNoCheck(OBJECTREF pObject)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ }
+ CONTRACTL_END;
+
+ InitNoCheck((PTR_SyncBlock)pObject->GetSyncBlock());
+ }
+
+ void InitNoCheck(RCW *pRCW)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(m_pRCW == NULL);
+ PRECONDITION(CheckPointer(m_pThread));
+ PRECONDITION(CheckPointer(pRCW));
+ }
+ CONTRACTL_END;
+
+ InitNoCheck(pRCW->GetSyncBlock());
+ }
+
+ void UnInit()
+ {
+ CONTRACTL
+ {
+ if (m_fRCWInUse)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ }
+ else
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(m_pThread));
+ PRECONDITION(CheckPointer(m_pSB));
+ PRECONDITION(GetThread() == m_pThread);
+ }
+ CONTRACTL_END;
+
+ // Unregister this RCW on the thread
+ if (m_fValid)
+ {
+ m_fValid = FALSE;
+
+#ifdef MDA_SUPPORTED
+ if (m_pMDA)
+ m_pThread->UnregisterRCW(INDEBUG(m_pSB));
+#endif // MDA_SUPPORTED
+ }
+
+ BOOL fThrowException = FALSE;
+ if (m_fRCWInUse)
+ {
+ // Now's the perfect time to check the RCW again. If the SyncBlock doesn't point to
+ // our RCW anymore, we know that we must have raced with an explicit release.
+ if (m_pSB->GetInteropInfoNoCreate()->GetRawRCW() != m_pRCW)
+ {
+ fThrowException = TRUE;
+ }
+
+ m_pRCW->DecrementUseCount();
+ m_fRCWInUse = FALSE;
+ }
+
+ m_pRCW = NULL;
+ m_pSB = NULL;
+
+ if (fThrowException)
+ {
+ // Since the object demonstrably had the RCW when we executed Init, we know for sure that
+ // this must be a race. Use the same exception for compatibility but pass resource ID of
+ // a slightly enhanced error message.
+ COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_RELEASE_RACE);
+ }
+ }
+
+ PTR_RCW GetRawRCWUnsafe()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_pRCW;
+ }
+
+ BOOL IsNull()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+ return (m_pRCW == NULL) ? TRUE : FALSE;
+ }
+
+ inline PTR_RCW operator->()
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ PRECONDITION(CheckPointer(m_pRCW));
+ }
+ CONTRACTL_END;
+
+ return m_pRCW;
+ }
+
+private:
+ PTR_RCW m_pRCW;
+
+ // Used for per-thread registration.
+ PTR_SyncBlock m_pSB;
+ PTR_Thread m_pThread;
+
+ // Used for de-registration
+ BOOL m_fValid;
+ BOOL m_fRCWInUse;
+
+#ifdef MDA_SUPPORTED
+ // Stores the MDA.
+ MdaRaceOnRCWCleanup* m_pMDA;
+#endif // MDA_SUPPORTED
+};
+#endif // !DACCESS_COMPILE
+
+
+//---------------------------------------------------------------------
+// When the RCW is used for actual calls out to the COM object, we want to check for cleanup race
+// when we're done with it, ideally at the point where the RCWHolder goes out of scope. But, since
+// throwing exceptions from destructors is generally a bad idea, we use the RCWPROTECT_BEGIN
+// RCWPROTECT_END brackets instead of the plain RCWHolder.
+//---------------------------------------------------------------------
+#define RCWPROTECT_BEGIN(pRCWHolder, arg) \
+ { \
+ pRCWHolder.Init(arg);
+
+#define RCWPROTECT_END(pRCWHolder) \
+ pRCWHolder.UnInit(); \
+ }
+
+//---------------------------------------------------------------------
+// RCW cache, act as the manager for the RCWs
+// uses a hash table to map IUnknown to the corresponding wrappers.
+// There is one such cache per thread affinity domain.
+//
+// <TODO>@TODO context cwb: revisit. One could have a cache per thread affinity
+// domain, or one per context. It depends on how we do the handshake between
+// ole32 and runtime contexts. For now, we only worry about apartments, so
+// thread affinity domains are sufficient.</TODO>
+//---------------------------------------------------------------------
+class RCWCache
+{
+ friend class RCWRefCache;
+
+public:
+ class LockHolder : public CrstHolder
+ {
+ public:
+ LockHolder(RCWCache *pCache)
+ : CrstHolder(&pCache->m_lock)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE; // The RCWCache lock must be taken
+ // in coop mode. It syncs RCW releases
+ // with the GC.
+ // This lock will *not* be taken by the GC
+ // during collection.
+ }
+ CONTRACTL_END;
+ }
+ };
+
+
+ RCWCache(AppDomain *pDomain);
+
+ static RCWCache* GetRCWCache();
+ static RCWCache* GetRCWCacheNoCreate();
+
+#ifndef DACCESS_COMPILE
+ // Insert wrapper into hash table.
+ // Since lock is held, no need to report RCW use to thread.
+ void InsertWrapper(RCWHolder* pRCW)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pRCW));
+ PRECONDITION(CheckPointer(pRCW->GetRawRCWUnsafe()));
+ PRECONDITION(LOCKHELD());
+ PRECONDITION(LookupWrapperUnsafe(pRCW->GetRawRCWUnsafe()->m_pIdentity) == NULL);
+ }
+ CONTRACTL_END;
+
+ m_HashMap.Add(pRCW->GetRawRCWUnsafe());
+ }
+
+ void RemoveWrapper(RCWHolder* pRCW)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ RemoveWrapper(pRCW->GetRawRCWUnsafe());
+ }
+#endif // DACCESS_COMPILE
+
+ // Delete wrapper for a given IUnk from hash table
+ void RemoveWrapper(RCW* pRCW)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pRCW));
+ }
+ CONTRACTL_END;
+
+ // Note that the GC thread doesn't have to take the lock
+ // since all other threads access in cooperative mode
+
+ _ASSERTE_IMPL(LOCKHELD() && GetThread()->PreemptiveGCDisabled()
+ || Debug_IsLockedViaThreadSuspension());
+
+ LPVOID pIdentity;
+ pIdentity = pRCW->m_pIdentity;
+ _ASSERTE(pIdentity != NULL);
+
+ m_HashMap.Remove(pIdentity);
+ }
+
+ // Lookup to see if we already have a wrapper else insert this wrapper
+ // return a valid wrapper that has been inserted into the cache
+ BOOL FindOrInsertWrapper_NoLock(IUnknown* pIdentity, RCWHolder* pWrap, BOOL fAllowReinit);
+
+ AppDomain* GetDomain()
+ {
+ CONTRACT (AppDomain*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ RETURN m_pDomain;
+ }
+
+ // Worker function called to release wrappers in the pCtxCookie context.
+ // Zero indicates all wrappers.
+ void ReleaseWrappersWorker(LPVOID pCtxCookie);
+
+ // Worker function called to detach GC-unmarked wrappers from their
+ // underlying COM pUnk identities to prevent resurrection.
+ void DetachWrappersWorker();
+
+#ifndef DACCESS_COMPILE
+
+ // Lookup wrapper, lookup hash table for a wrapper for a given IUnk
+ void LookupWrapper(LPVOID pUnk, RCWHolder* pRCW)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(LOCKHELD());
+ //POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ // We don't want the GC messing with the hash table underneath us.
+ GCX_FORBID();
+
+ RCW* pRawRCW = LookupWrapperUnsafe(pUnk);
+
+ if (pRawRCW == NULL)
+ return;
+
+ // Assume that we already have a sync block for this object.
+ pRCW->InitNoCheck(pRawRCW);
+ }
+
+ RCW* LookupWrapperUnsafe(LPVOID pUnk)
+ {
+ CONTRACT (RCW*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(LOCKHELD());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ // We don't want the GC messing with the hash table underneath us.
+ GCX_FORBID();
+
+ RETURN m_HashMap.Lookup(pUnk);
+ }
+
+#endif //DACCESS_COMPILE
+
+#ifdef _DEBUG
+ BOOL LOCKHELD()
+ {
+ WRAPPER_NO_CONTRACT;
+ return m_lock.OwnedByCurrentThread();
+ }
+#endif
+
+private :
+ friend class COMInterfaceMarshaler;
+
+ // Look up to see if we already have an valid wrapper in cache for this IUnk
+ // DOES NOT hold a lock inside the function - locking in the caller side IS REQUIRED
+ void FindWrapperInCache_NoLock(IUnknown* pIdentity, RCWHolder* pRCW);
+
+private:
+ class RCWCacheTraits : public DefaultSHashTraits<RCW *>
+ {
+ public:
+ typedef LPVOID key_t;
+ static RCW *Null() { LIMITED_METHOD_CONTRACT; return NULL; }
+ static bool IsNull(RCW *e) { LIMITED_METHOD_CONTRACT; return (e == NULL); }
+ static const LPVOID GetKey(RCW *e) { LIMITED_METHOD_CONTRACT; return e->m_pIdentity; }
+ static count_t Hash(LPVOID key_t) { LIMITED_METHOD_CONTRACT; return (count_t)key_t; }
+ static BOOL Equals(LPVOID lhs, LPVOID rhs) { LIMITED_METHOD_CONTRACT; return (lhs == rhs); }
+ static RCW *Deleted() { LIMITED_METHOD_CONTRACT; return (RCW *)-1; }
+ static bool IsDeleted(RCW *e) { LIMITED_METHOD_CONTRACT; return e == (RCW *)-1; }
+ };
+
+ SHash<RCWCacheTraits> m_HashMap;
+
+ // spin lock for fast synchronization
+ Crst m_lock;
+ AppDomain* m_pDomain;
+};
+
+struct ReleaseRCWList_Args
+{
+ RCW *pHead;
+ BOOL ctxTried;
+ BOOL ctxBusy;
+};
+
+// RCWCleanupList represents a list of RCWs whose corresponding managed objects have been collected.
+// These RCWs must be released, potentially involving transitioning into the right apartment/context.
+// That is why the operation is deferred and done in chunks instead of individual RCWs so the
+// transition overhead is minimized. This data structure is a two-dimensional linked list with
+// individual RCWs grouped into buckets that share the same COM apartment/context.
+//
+// Adding RCWs into the cleanup list must not allocate memory or perform any similar operation that
+// may fail. The only operation allowed to fail is the release itself (out of our control). Therefore
+// the data structure uses only a single statically allocated instance of RCWCleanupList and the
+// "links" are taken care of by the RCW structures themselves.
+//
+// m_pFirstBucket m_pNextCleanupBucket m_pNextCleanupBucket
+// RCWCleanupList ------> RCW_1a -------------------> RCW_2a -------------------> RCW_3a -->...--> NULL
+// | | |
+// | m_pNextRCW | m_pNextRCW | m_pNextRCW
+// v v v
+// RCW_1b RCW_2b RCW_3b
+// | | |
+// | m_pNextRCW | m_pNextRCW | m_pNextRCW
+// v v v
+// RCW_1c RCW_2c RCW_3c
+// | | |
+// v v v
+// ... ... ...
+// | | |
+// v v v
+// NULL NULL NULL
+//
+// In the picture above, RCW_1a, RCW_1b, RCW_1c, ... are in the same bucket, RCW_2a, RCW_2b, RCW_2c, ...
+// are in another bucket etc. The supported operations are adding an RCW (see code:RCWCleanupList::AddWrapper)
+// and removing entire buckets that meet given criteria (see code:RCWCleanupList::CleanupAllWrappers and
+// code:RCWCleanupList::CleanupWrappersInCurrentCtxThread).
+
+class RCWCleanupList
+{
+#ifdef DACCESS_COMPILE
+ friend class ClrDataAccess;
+#endif // DACCESS_COMPILE
+
+public:
+ RCWCleanupList()
+ : m_lock(CrstRCWCleanupList, CRST_UNSAFE_ANYMODE),
+ m_pCurCleanupThread(NULL), m_doCleanupInContexts(FALSE),
+ m_pFirstBucket(NULL)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ ~RCWCleanupList()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(IsEmpty());
+ }
+
+ VOID AddWrapper(RCW* pRCW);
+ VOID AddWrapper_NoLock(RCW *pRCW);
+ VOID CleanupAllWrappers();
+ VOID CleanupWrappersInCurrentCtxThread(BOOL fWait = TRUE, BOOL fManualCleanupRequested = FALSE, BOOL bIgnoreComObjectEagerCleanupSetting = FALSE);
+
+ BOOL IsEmpty();
+
+private:
+ // These 2 functions are static so we can call them through the Context Callback mechanism.
+ static HRESULT ReleaseRCWListInCorrectCtx(LPVOID pData);
+ static VOID ReleaseRCWListRaw(RCW* pRCW);
+
+#ifndef DACCESS_COMPILE
+ // Utility class that maintains a list of buckets removed from the cleanup list.
+ struct RemovedBuckets
+ {
+ RemovedBuckets()
+ : m_pFirstBucket(NULL),
+ m_pLastBucket(NULL)
+ { }
+
+ ~RemovedBuckets()
+ {
+ // we must always end up with an empty list, otherwise we leak RCWs
+ _ASSERTE(m_pFirstBucket == NULL);
+ }
+
+ void Append(PTR_RCW pBucket)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pLastBucket == NULL)
+ {
+ // appending the first bucket
+ _ASSERTE(m_pFirstBucket == NULL);
+ m_pFirstBucket = pBucket;
+ }
+ else
+ {
+ // appending >first bucket
+ m_pLastBucket->m_pNextCleanupBucket = pBucket;
+ }
+
+ pBucket->m_pNextCleanupBucket = NULL;
+ m_pLastBucket = pBucket;
+ }
+
+ RCW *PopHead()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ RCW *pRetVal = m_pFirstBucket;
+ if (m_pFirstBucket != NULL)
+ m_pFirstBucket = m_pFirstBucket->m_pNextCleanupBucket;
+
+ return pRetVal;
+ }
+
+ RCW *m_pFirstBucket;
+ RCW *m_pLastBucket;
+ };
+#endif // !DACCESS_COMPILE
+
+ RCW *m_pFirstBucket;
+ Crst m_lock;
+ Thread* m_pCurCleanupThread;
+
+ // Fast check for whether threads should help cleanup wrappers in their contexts
+ BOOL m_doCleanupInContexts;
+};
+
+FORCEINLINE void CtxEntryHolderRelease(CtxEntry *p)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (p != NULL)
+ {
+ p->Release();
+ }
+}
+
+class CtxEntryHolder : public Wrapper<CtxEntry *, CtxEntryDoNothing, CtxEntryHolderRelease, NULL>
+{
+public:
+ CtxEntryHolder(CtxEntry *p = NULL)
+ : Wrapper<CtxEntry *, CtxEntryDoNothing, CtxEntryHolderRelease, NULL>(p)
+ {
+ WRAPPER_NO_CONTRACT;
+ }
+
+ FORCEINLINE void operator=(CtxEntry *p)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ Wrapper<CtxEntry *, CtxEntryDoNothing, CtxEntryHolderRelease, NULL>::operator=(p);
+ }
+
+};
+
+#endif // _RUNTIMECALLABLEWRAPPER_H