summaryrefslogtreecommitdiff
path: root/src/vm/runtimecallablewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/runtimecallablewrapper.cpp')
-rw-r--r--src/vm/runtimecallablewrapper.cpp5615
1 files changed, 5615 insertions, 0 deletions
diff --git a/src/vm/runtimecallablewrapper.cpp b/src/vm/runtimecallablewrapper.cpp
new file mode 100644
index 0000000000..d12d5568f6
--- /dev/null
+++ b/src/vm/runtimecallablewrapper.cpp
@@ -0,0 +1,5615 @@
+// 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.
+
+/*============================================================
+**
+** Class: RCW
+**
+**
+** Purpose: The implementation of the ComObject class
+**
+
+===========================================================*/
+
+#include "common.h"
+
+#include <ole2.h>
+#include <inspectable.h>
+
+class Object;
+#include "vars.hpp"
+#include "object.h"
+#include "excep.h"
+#include "frames.h"
+#include "vars.hpp"
+#include "threads.h"
+#include "field.h"
+#include "runtimecallablewrapper.h"
+#include "hash.h"
+#include "interoputil.h"
+#include "comcallablewrapper.h"
+#include "eeconfig.h"
+#include "comdelegate.h"
+#include "comcache.h"
+#include "notifyexternals.h"
+#include "winrttypenameconverter.h"
+#include "../md/compiler/custattr.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "mdaassistants.h"
+#include "olevariant.h"
+#include "interopconverter.h"
+#include "constrainedexecutionregion.h"
+#ifdef FEATURE_REMOTING
+#include "crossdomaincalls.h"
+#endif
+#include "caparser.h"
+#include "classnames.h"
+#include "objectnative.h"
+#include "rcwwalker.h"
+#include "finalizerthread.h"
+
+// static
+SLIST_HEADER RCW::s_RCWStandbyList;
+
+#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
+#include "olecontexthelpers.h"
+#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
+
+#ifndef CROSSGEN_COMPILE
+
+void ComClassFactory::ThrowHRMsg(HRESULT hr, DWORD dwMsgResID)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+ SString strMessage;
+ SString strResource;
+ WCHAR strClsid[39];
+ SString strHRDescription;
+
+ // Obtain the textual representation of the HRESULT.
+ StringFromGUID2(m_rclsid, strClsid, sizeof(strClsid) / sizeof(WCHAR));
+
+ SString strHRHex;
+ strHRHex.Printf("%.8x", hr);
+
+ // Obtain the description of the HRESULT.
+ GetHRMsg(hr, strHRDescription);
+
+ // Load the appropriate resource and throw
+ COMPlusThrowHR(hr, dwMsgResID, strHRHex, strClsid, strHRDescription.GetUnicode());
+}
+
+//-------------------------------------------------------------
+// Common code for licensing
+//
+IUnknown *ComClassFactory::CreateInstanceFromClassFactory(IClassFactory *pClassFact, IUnknown *punkOuter, BOOL *pfDidContainment)
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pClassFact));
+ PRECONDITION(CheckPointer(punkOuter, NULL_OK));
+ PRECONDITION(CheckPointer(pfDidContainment, NULL_OK));
+ PRECONDITION(CheckPointer(m_pClassMT, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ HRESULT hr = S_OK;
+ SafeComHolder<IClassFactory2> pClassFact2 = NULL;
+ SafeComHolder<IUnknown> pUnk = NULL;
+ BSTRHolder bstrKey = NULL;
+
+ Thread *pThread = GetThread();
+
+ // Does this support licensing?
+ if (FAILED(SafeQueryInterface(pClassFact, IID_IClassFactory2, (IUnknown**)&pClassFact2)))
+ {
+ // not a licensed class - just createinstance the usual way.
+ // Create an instance of the object.
+ FrameWithCookie<DebuggerExitFrame> __def;
+ {
+ GCX_PREEMP();
+ {
+ LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pClassFact);
+ hr = pClassFact->CreateInstance(punkOuter, IID_IUnknown, (void **)&pUnk);
+ }
+ if (FAILED(hr) && punkOuter)
+ {
+ LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pClassFact);
+ hr = pClassFact->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+ }
+ __def.Pop();
+ }
+ else
+ {
+ if (m_pClassMT == NULL)
+ {
+ // Create an instance of the object.
+ FrameWithCookie<DebuggerExitFrame> __def;
+ {
+ GCX_PREEMP();
+ LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pClassFact);
+ hr = pClassFact->CreateInstance(punkOuter, IID_IUnknown, (void **)&pUnk);
+ if (FAILED(hr) && punkOuter)
+ {
+ hr = pClassFact->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+ }
+ __def.Pop();
+ }
+ else
+ {
+ MethodTable *pHelperMT = pThread->GetDomain()->GetLicenseInteropHelperMethodTable();
+ MethodDesc *pMD = MemberLoader::FindMethod(pHelperMT, "GetCurrentContextInfo", &gsig_IM_LicenseInteropHelper_GetCurrentContextInfo);
+ MethodDescCallSite getCurrentContextInfo(pMD);
+
+ TypeHandle rth = TypeHandle(m_pClassMT);
+
+ struct _gc {
+ OBJECTREF pHelper;
+ OBJECTREF pType;
+ } gc;
+ gc.pHelper = NULL; // LicenseInteropHelper
+ gc.pType = NULL;
+
+ GCPROTECT_BEGIN(gc);
+
+ gc.pHelper = pHelperMT->Allocate();
+ gc.pType = rth.GetManagedClassObject();
+
+ // First, crack open the current licensing context.
+ INT32 fDesignTime = 0;
+ ARG_SLOT args[4];
+ args[0] = ObjToArgSlot(gc.pHelper);
+ args[1] = (ARG_SLOT)&fDesignTime;
+ args[2] = (ARG_SLOT)(BSTR*)&bstrKey;
+ args[3] = ObjToArgSlot(gc.pType);
+
+ getCurrentContextInfo.Call(args);
+
+ if (fDesignTime)
+ {
+ // If designtime, we're supposed to obtain the runtime license key
+ // from the component and save it away in the license context
+ // (the design tool can then grab it and embedded it into the
+ // app it's creating.)
+
+ if (bstrKey != NULL)
+ {
+ // It's illegal for our helper to return a non-null bstrKey
+ // when the context is design-time. But we'll try to do the
+ // right thing anway.
+ _ASSERTE(!"We're not supposed to get here, but we'll try to cope anyway.");
+ SysFreeString(bstrKey);
+ bstrKey = NULL;
+ }
+
+ {
+ GCX_PREEMP();
+ hr = pClassFact2->RequestLicKey(0, &bstrKey);
+ }
+
+ // E_NOTIMPL is not a true failure. It simply indicates that
+ // the component doesn't support a runtime license key.
+ if (hr == E_NOTIMPL)
+ hr = S_OK;
+
+ if (SUCCEEDED(hr))
+ {
+ MethodDesc *pMDSaveKey = MemberLoader::FindMethod(pHelperMT, "SaveKeyInCurrentContext", &gsig_IM_LicenseInteropHelper_SaveKeyInCurrentContext);
+ MethodDescCallSite saveKeyInCurrentContext(pMDSaveKey);
+
+ args[0] = ObjToArgSlot(gc.pHelper);
+ args[1] = (ARG_SLOT)(BSTR)bstrKey;
+ saveKeyInCurrentContext.Call(args);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ FrameWithCookie<DebuggerExitFrame> __def;
+ {
+ GCX_PREEMP();
+
+ if (fDesignTime || bstrKey == NULL)
+ {
+ // Either it's design time, or the current context doesn't
+ // supply a runtime license key.
+ LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pClassFact);
+ hr = pClassFact->CreateInstance(punkOuter, IID_IUnknown, (void **)&pUnk);
+ if (FAILED(hr) && punkOuter)
+ {
+ hr = pClassFact->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+ }
+ else
+ {
+ // It's runtime, and we do have a non-null license key.
+ _ASSERTE(bstrKey != NULL);
+ LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pClassFact);
+ hr = pClassFact2->CreateInstanceLic(punkOuter, NULL, IID_IUnknown, bstrKey, (void**)&pUnk);
+ if (FAILED(hr) && punkOuter)
+ {
+ hr = pClassFact2->CreateInstanceLic(NULL, NULL, IID_IUnknown, bstrKey, (void**)&pUnk);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+
+ }
+ }
+ __def.Pop();
+ }
+
+ GCPROTECT_END();
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ if (bstrKey == NULL)
+ ThrowHRMsg(hr, IDS_EE_CREATEINSTANCE_FAILED);
+ else
+ ThrowHRMsg(hr, IDS_EE_CREATEINSTANCE_LIC_FAILED);
+ }
+
+ pUnk.SuppressRelease();
+ RETURN pUnk;
+}
+
+
+//-------------------------------------------------------------
+// ComClassFactory::CreateAggregatedInstance(MethodTable* pMTClass)
+// create a COM+ instance that aggregates a COM instance
+
+OBJECTREF ComClassFactory::CreateAggregatedInstance(MethodTable* pMTClass, BOOL ForManaged)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pMTClass));
+ }
+ CONTRACTL_END;
+
+ BOOL fDidContainment = FALSE;
+
+#ifdef _DEBUG
+ // verify the class extends a COM import class
+ MethodTable * pMT = pMTClass;
+ do
+ {
+ pMT = pMT->GetParentMethodTable();
+ }
+ while (pMT == NULL || pMT->IsComImport());
+ _ASSERTE(pMT != NULL);
+#endif
+
+ SafeComHolder<IUnknown> pOuter = NULL;
+ SafeComHolder<IClassFactory> pClassFact = NULL;
+ SafeComHolder<IUnknown> pUnk = NULL;
+
+ HRESULT hr = S_OK;
+ NewRCWHolder pNewRCW;
+ BOOL bUseDelegate = FALSE;
+
+ MethodTable *pCallbackMT = NULL;
+
+ OBJECTREF oref = NULL;
+ COMOBJECTREF cref = NULL;
+ GCPROTECT_BEGIN(cref)
+ {
+ cref = (COMOBJECTREF)ComObject::CreateComObjectRef(pMTClass);
+
+ //get wrapper for the object, this could enable GC
+ CCWHolder pComWrap = ComCallWrapper::InlineGetWrapper((OBJECTREF *)&cref);
+
+ // Make sure the ClassInitializer has run, since the user might have
+ // wanted to set up a COM object creation callback.
+ pMTClass->CheckRunClassInitThrowing();
+
+ // If the user is going to use a delegate to allocate the COM object
+ // (rather than CoCreateInstance), we need to know now, before we enable
+ // preemptive GC mode (since we touch object references in the
+ // determination).
+ // We don't just check the current class to see if it has a cllabck
+ // registered, we check up the class chain to see if any of our parents
+ // did.
+
+ pCallbackMT = pMTClass;
+ while ((pCallbackMT != NULL) &&
+ (pCallbackMT->GetObjCreateDelegate() == NULL) &&
+ !pCallbackMT->IsComImport())
+ {
+ pCallbackMT = pCallbackMT->GetParentMethodTable();
+ }
+
+ if (pCallbackMT && !pCallbackMT->IsComImport())
+ bUseDelegate = TRUE;
+
+ FrameWithCookie<DebuggerExitFrame> __def;
+
+ // get the IUnknown interface for the managed object
+ pOuter = ComCallWrapper::GetComIPFromCCW(pComWrap, IID_IUnknown, NULL);
+ _ASSERTE(pOuter != NULL);
+
+ // If the user has set a delegate to allocate the COM object, use it.
+ // Otherwise we just CoCreateInstance it.
+ if (bUseDelegate)
+ {
+ ARG_SLOT args[2];
+
+ OBJECTREF orDelegate = pCallbackMT->GetObjCreateDelegate();
+ MethodDesc *pMeth = COMDelegate::GetMethodDesc(orDelegate);
+
+ GCPROTECT_BEGIN(orDelegate)
+ {
+ _ASSERTE(pMeth);
+ MethodDescCallSite delegateMethod(pMeth, &orDelegate);
+
+ // Get the OR on which we are going to invoke the method and set it
+ // as the first parameter in arg above.
+ args[0] = (ARG_SLOT)OBJECTREFToObject(COMDelegate::GetTargetObject(orDelegate));
+
+ // Pass the IUnknown of the aggregator as the second argument.
+ args[1] = (ARG_SLOT)(IUnknown*)pOuter;
+
+ // Call the method...
+ pUnk = (IUnknown *)delegateMethod.Call_RetArgSlot(args);
+ if (!pUnk)
+ COMPlusThrowHR(E_FAIL);
+ }
+ GCPROTECT_END();
+ }
+ else
+ {
+ _ASSERTE(m_pClassMT);
+ pUnk = CreateInstanceInternal(pOuter, &fDidContainment);
+ }
+
+ __def.Pop();
+
+ // give up the extra addref that we did in our QI and suppress the auto-release.
+ pComWrap->Release();
+ pComWrap.SuppressRelease();
+
+ // Here's the scary part. If we are doing a managed 'new' of the aggregator,
+ // then COM really isn't involved. We should not be counting for our caller
+ // because our caller relies on GC references rather than COM reference counting
+ // to keep us alive.
+ //
+ // Drive the instances count down to 0 -- and rely on the GCPROTECT to keep us
+ // alive until we get back to our caller.
+ if (ForManaged)
+ pComWrap->Release();
+
+ RCWCache* pCache = RCWCache::GetRCWCache();
+
+ _ASSERTE(cref->GetSyncBlock()->IsPrecious()); // the object already has a CCW
+ DWORD dwSyncBlockIndex = cref->GetSyncBlockIndex();
+
+ // create a wrapper for this COM object
+ pNewRCW = RCW::CreateRCW(pUnk, dwSyncBlockIndex, RCW::CF_None, pMTClass);
+
+ RCWHolder pRCW(GetThread());
+ pRCW.InitNoCheck(pNewRCW);
+
+ // we used containment
+ // we need to store this wrapper in our hash table
+ {
+ RCWCache::LockHolder lh(pCache);
+
+ GCX_FORBID();
+
+ BOOL fInserted = pCache->FindOrInsertWrapper_NoLock(pUnk, &pRCW, /* fAllowReInit = */ FALSE);
+ if (!fInserted)
+ {
+ // OK. Looks like the factory returned a singleton on us and the cache already
+ // has an entry for this pIdentity. This should always happen in containment
+ // scenario, not for aggregation.
+ // In this case, we should insert this new RCW into cache as a unique RCW,
+ // because these are separate objects, and we need two separate RCWs with
+ // different flags (should be contained, impossible to be aggregated) pointing
+ // to them, separately
+ pNewRCW->m_pIdentity = pNewRCW;
+
+ fInserted = pCache->FindOrInsertWrapper_NoLock((IUnknown*)pNewRCW->m_pIdentity, &pRCW, /* fAllowReInit = */ FALSE);
+ _ASSERTE(fInserted);
+ }
+ }
+
+ if (fDidContainment)
+ {
+ // mark the wrapper as contained
+ pRCW->MarkURTContained();
+ }
+ else
+ {
+ // mark the wrapper as aggregated
+ pRCW->MarkURTAggregated();
+ }
+
+ // pUnk has to be released inside GC-protected block and before oref get assigned value
+ // because it could trigger GC
+ SafeRelease(pUnk);
+ pUnk.SuppressRelease();
+
+ // If the object was created successfully then we need to copy the OBJECTREF
+ // to oref because the GCPROTECT_END() will destroy the contents of cref.
+ oref = ObjectToOBJECTREF(*(Object **)&cref);
+ }
+ GCPROTECT_END();
+
+ if (oref != NULL)
+ {
+ pOuter.SuppressRelease();
+ pClassFact.SuppressRelease();
+ pNewRCW.SuppressRelease();
+ }
+
+ return oref;
+}
+
+//--------------------------------------------------------------
+// Create instance using IClassFactory
+// Overridable
+IUnknown *ComClassFactory::CreateInstanceInternal(IUnknown *pOuter, BOOL *pfDidContainment)
+{
+ CONTRACT(IUnknown *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pOuter, NULL_OK));
+ PRECONDITION(CheckPointer(pfDidContainment, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ SafeComHolder<IClassFactory> pClassFactory = GetIClassFactory();
+ RETURN CreateInstanceFromClassFactory(pClassFactory, pOuter, pfDidContainment);
+}
+
+IClassFactory *ComClassFactory::GetIClassFactory()
+{
+ CONTRACT(IClassFactory *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ HRESULT hr = S_OK;
+ IClassFactory *pClassFactory = NULL;
+
+ GCX_PREEMP();
+
+ // If a server name is specified, then first try CLSCTX_REMOTE_SERVER.
+ if (m_pwszServer)
+ {
+ // Set up the COSERVERINFO struct.
+ COSERVERINFO ServerInfo;
+ memset(&ServerInfo, 0, sizeof(COSERVERINFO));
+ ServerInfo.pwszName = m_pwszServer;
+
+ // Try to retrieve the IClassFactory passing in CLSCTX_REMOTE_SERVER.
+ LeaveRuntimeHolder lrh((size_t)CoGetClassObject);
+ hr = CoGetClassObject(m_rclsid, CLSCTX_REMOTE_SERVER, &ServerInfo, IID_IClassFactory, (void**)&pClassFactory);
+ }
+ else
+ {
+ // No server name is specified so we use CLSCTX_SERVER.
+ LeaveRuntimeHolder lrh((size_t)CoGetClassObject);
+
+#ifdef FEATURE_CLASSIC_COMINTEROP
+ // If the CLSID is hosted by the CLR itself, then we do not want to go through the COM registration
+ // entries, as this will trigger our COM activation code that may not activate against this runtime.
+ // In this scenario, we want to get the address of the DllGetClassObject method on this CLR or a DLL
+ // that lives in the same directory as the CLR and use it directly. The code falls back to
+ // CoGetClassObject if we fail on the call to DllGetClassObject, but it might be better to fail outright.
+ if (Clr::Util::Com::CLSIDHasMscoreeAsInprocServer32(m_rclsid))
+ {
+ typedef HRESULT (STDMETHODCALLTYPE *PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
+
+ StackSString ssServer;
+ if (FAILED(Clr::Util::Com::FindServerUsingCLSID(m_rclsid, ssServer)))
+ {
+#ifndef FEATURE_CORECLR
+ // If there is no server entry, then that implies the CLSID could be implemented by CLR.DLL itself,
+ // if the CLSID is one of the special ones implemented by the CLR. We need to check against the
+ // specific list of CLSIDs here because CLR.DLL-implemented CLSIDs and managed class-implemented
+ // CLSIDs look the same until you start interating the subkeys. For now, the set of CLSIDs implemented
+ // by CLR.DLL is a short and tractable list, but at some point it might become worthwhile to move over
+ // to the more generalized solution of looking for the entries that identify when the CLSID is
+ // implemented by a managed type to avoid having to maintain the hardcoded list.
+ if (IsClrHostedLegacyComObject(m_rclsid))
+ {
+ PDllGetClassObject pFN = NULL;
+ hr = g_pCLRRuntime->GetProcAddress("DllGetClassObjectInternal", reinterpret_cast<void**>(&pFN));
+
+ if (FAILED(hr))
+ hr = g_pCLRRuntime->GetProcAddress("DllGetClassObject", reinterpret_cast<void**>(&pFN));
+
+ if (SUCCEEDED(hr))
+ hr = pFN(m_rclsid, IID_IClassFactory, (void**)&pClassFactory);
+ }
+#endif
+ }
+ else
+ {
+#ifndef FEATURE_CORECLR
+ // @CORESYSTODO: ?
+
+ // There is a SxS DLL that implements this CLSID.
+ // NOTE: It is standard practise for RCWs and P/Invokes to leak their module handles,
+ // as there is no automated mechanism for the runtime to call CanUnloadDllNow.
+ HMODULE hServer = NULL;
+ if (SUCCEEDED(hr = g_pCLRRuntime->LoadLibrary(ssServer.GetUnicode(), &hServer)))
+ {
+ PDllGetClassObject pFN = reinterpret_cast<PDllGetClassObject>(GetProcAddress(hServer, "DllGetClassObject"));
+ if (pFN != NULL)
+ {
+ hr = pFN(m_rclsid, IID_IClassFactory, (void**)&pClassFactory);
+ }
+ else
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ }
+#endif
+ }
+ }
+#endif // FEATURE_CLASSIC_COMINTEROP
+
+ if (pClassFactory == NULL)
+ hr = CoGetClassObject(m_rclsid, CLSCTX_SERVER, NULL, IID_IClassFactory, (void**)&pClassFactory);
+ }
+
+ // If we failed to obtain the IClassFactory, throw an exception with rich information
+ // explaining the failure.
+ if (FAILED(hr))
+ {
+ SString strMessage;
+ SString strResource;
+ WCHAR strClsid[39];
+ SString strHRDescription;
+
+ // Obtain the textual representation of the HRESULT.
+ StringFromGUID2(m_rclsid, strClsid, sizeof(strClsid) / sizeof(WCHAR));
+
+ SString strHRHex;
+ strHRHex.Printf("%.8x", hr);
+
+ // Obtain the description of the HRESULT.
+ GetHRMsg(hr, strHRDescription);
+
+ // Throw the actual exception indicating we couldn't find the class factory.
+ if (m_pwszServer == NULL)
+ COMPlusThrowHR(hr, IDS_EE_LOCAL_COGETCLASSOBJECT_FAILED, strHRHex, strClsid, strHRDescription.GetUnicode());
+ else
+ COMPlusThrowHR(hr, IDS_EE_REMOTE_COGETCLASSOBJECT_FAILED, strHRHex, strClsid, m_pwszServer, strHRDescription.GetUnicode());
+ }
+
+ RETURN pClassFactory;
+}
+
+//-------------------------------------------------------------
+// ComClassFactory::CreateInstance()
+// create instance, calls IClassFactory::CreateInstance
+OBJECTREF ComClassFactory::CreateInstance(MethodTable* pMTClass, BOOL ForManaged)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pMTClass, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ // Check for aggregates
+ if (pMTClass != NULL && !pMTClass->IsComImport())
+ return CreateAggregatedInstance(pMTClass, ForManaged);
+
+ HRESULT hr = S_OK;
+ OBJECTREF coref = NULL;
+ OBJECTREF RetObj = NULL;
+
+ GCPROTECT_BEGIN(coref)
+ {
+ {
+ SafeComHolder<IUnknown> pUnk = NULL;
+ SafeComHolder<IClassFactory> pClassFact = NULL;
+
+ // Create the instance
+ pUnk = CreateInstanceInternal(NULL, NULL);
+
+ // Even though we just created the object, it's possible that we got back a context
+ // wrapper from the COM side. For instance, it could have been an existing object
+ // or it could have been created in a different context than we are running in.
+
+ // pMTClass is the class that wraps the com ip
+ // if a class was passed in use it
+ // otherwise use the class that we know
+ if (pMTClass == NULL)
+ pMTClass = m_pClassMT;
+
+ GetObjectRefFromComIP(&coref, pUnk, pMTClass);
+
+ if (coref == NULL)
+ COMPlusThrowOM();
+ }
+
+ // Set the value of the return object after the COM guys are cleaned up.
+ RetObj = coref;
+ }
+ GCPROTECT_END();
+
+ return RetObj;
+}
+#endif //#ifndef CROSSGEN_COMPILE
+
+//--------------------------------------------------------------
+// Init the ComClassFactory.
+void ComClassFactory::Init(__in_opt WCHAR* pwszProgID, __in_opt WCHAR* pwszServer, MethodTable* pClassMT)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pwszProgID = pwszProgID;
+ m_pwszServer = pwszServer;
+ _ASSERTE(pClassMT == NULL || !pClassMT->Collectible());
+ m_pClassMT = pClassMT;
+}
+
+//-------------------------------------------------------------
+void ComClassFactory::Cleanup()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_bManagedVersion)
+ return;
+
+ if (m_pwszProgID != NULL)
+ delete [] m_pwszProgID;
+
+ if (m_pwszServer != NULL)
+ delete [] m_pwszServer;
+
+ delete this;
+}
+
+#if defined(FEATURE_APPX) && !defined(CROSSGEN_COMPILE)
+//-------------------------------------------------------------
+// Create instance using CoCreateIntanceFromApp
+// CoCreateInstanceFromApp is a new Windows 8 API that only
+// allow creating COM objects (not WinRT objects) in the allow
+// list
+// Note: We don't QI for IClassFactory2 in this case as it is not
+// supported in ModernSDK
+IUnknown *AppXComClassFactory::CreateInstanceInternal(IUnknown *pOuter, BOOL *pfDidContainment)
+{
+ CONTRACT(IUnknown *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pOuter, NULL_OK));
+ PRECONDITION(CheckPointer(pfDidContainment, NULL_OK));
+ PRECONDITION(AppX::IsAppXProcess());
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ GCX_PREEMP();
+
+ MULTI_QI multiQI;
+ ::ZeroMemory(&multiQI, sizeof(MULTI_QI));
+ multiQI.pIID = &IID_IUnknown;
+
+ HRESULT hr;
+
+#ifdef FEATURE_CORESYSTEM
+ // This works around a bug in the Windows 7 loader that prevents us from loading the
+ // forwarder for this function
+ typedef HRESULT (*CoCreateInstanceFromAppFnPtr) (REFCLSID rclsid, IUnknown *punkOuter, DWORD dwClsCtx,
+ void *reserved, DWORD dwCount, MULTI_QI *pResults);
+
+ static CoCreateInstanceFromAppFnPtr CoCreateInstanceFromApp = NULL;
+ if (NULL == CoCreateInstanceFromApp)
+ {
+ HMODULE hmod = LoadLibraryExW(W("api-ms-win-core-com-l1-1-1.dll"), NULL, 0);
+
+ if (hmod)
+ CoCreateInstanceFromApp = (CoCreateInstanceFromAppFnPtr)GetProcAddress(hmod, "CoCreateInstanceFromApp");
+ }
+
+ if (NULL == CoCreateInstanceFromApp)
+ {
+ // This shouldn't happen
+ _ASSERTE(false);
+ IfFailThrow(E_FAIL);
+ }
+#endif
+
+ LeaveRuntimeHolder lrh((size_t)CoCreateInstanceFromApp);
+
+ if (m_pwszServer)
+ {
+ //
+ // Remote server activation
+ //
+ COSERVERINFO ServerInfo;
+ ::ZeroMemory(&ServerInfo, sizeof(COSERVERINFO));
+ ServerInfo.pwszName = m_pwszServer;
+
+ hr = CoCreateInstanceFromApp(
+ m_rclsid,
+ pOuter,
+ CLSCTX_REMOTE_SERVER,
+ &ServerInfo,
+ 1,
+ &multiQI);
+ if (FAILED(hr) && pOuter)
+ {
+ //
+ // Aggregation attempt failed. Retry containment
+ //
+ hr = CoCreateInstanceFromApp(
+ m_rclsid,
+ NULL,
+ CLSCTX_REMOTE_SERVER,
+ &ServerInfo,
+ 1,
+ &multiQI);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+ }
+ else
+ {
+ //
+ // Normal activation
+ //
+ hr = CoCreateInstanceFromApp(
+ m_rclsid,
+ pOuter,
+ CLSCTX_SERVER,
+ NULL,
+ 1,
+ &multiQI);
+ if (FAILED(hr) && pOuter)
+ {
+ //
+ // Aggregation attempt failed. Retry containment
+ //
+ hr = CoCreateInstanceFromApp(
+ m_rclsid,
+ NULL,
+ CLSCTX_SERVER,
+ NULL,
+ 1,
+ &multiQI);
+ if (pfDidContainment)
+ *pfDidContainment = TRUE;
+ }
+ }
+
+ if (FAILED(hr))
+ ThrowHRMsg(hr, IDS_EE_CREATEINSTANCEFROMAPP_FAILED);
+ if (FAILED(multiQI.hr))
+ ThrowHRMsg(multiQI.hr, IDS_EE_CREATEINSTANCEFROMAPP_FAILED);
+
+ RETURN multiQI.pItf;
+}
+#endif //FEATURE_APPX
+
+//-------------------------------------------------------------
+MethodTable *WinRTClassFactory::GetTypeFromAttribute(IMDInternalImport *pImport, mdCustomAttribute tkAttribute)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // get raw custom attribute
+ const BYTE *pbAttr = NULL;
+ ULONG cbAttr = 0;
+ IfFailThrowBF(pImport->GetCustomAttributeAsBlob(tkAttribute, (const void **)&pbAttr, &cbAttr), BFA_INVALID_TOKEN, m_pClassMT->GetModule());
+
+ CustomAttributeParser cap(pbAttr, cbAttr);
+ IfFailThrowBF(cap.ValidateProlog(), BFA_BAD_CA_HEADER, m_pClassMT->GetModule());
+
+ // retrieve the factory interface name
+ LPCUTF8 szName;
+ ULONG cbName;
+ IfFailThrow(cap.GetNonNullString(&szName, &cbName));
+
+ // copy the name to a temporary buffer and NULL terminate it
+ StackSString ss(SString::Utf8, szName, cbName);
+
+ // load the factory interface
+ return TypeName::GetTypeUsingCASearchRules(ss.GetUnicode(), m_pClassMT->GetAssembly()).GetMethodTable();
+}
+
+//-------------------------------------------------------------
+// Returns true if the first parameter of the CA's method ctor is a System.Type
+static BOOL AttributeFirstParamIsSystemType(mdCustomAttribute tkAttribute, IMDInternalImport *pImport)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pImport));
+ }
+ CONTRACTL_END;
+
+ mdToken ctorToken;
+ IfFailThrow(pImport->GetCustomAttributeProps(tkAttribute, &ctorToken));
+
+ LPCSTR ctorName;
+ PCCOR_SIGNATURE ctorSig;
+ ULONG cbCtorSig;
+
+ if (TypeFromToken(ctorToken) == mdtMemberRef)
+ {
+ IfFailThrow(pImport->GetNameAndSigOfMemberRef(ctorToken, &ctorSig, &cbCtorSig, &ctorName));
+ }
+ else if (TypeFromToken(ctorToken) == mdtMethodDef)
+ {
+ IfFailThrow(pImport->GetNameAndSigOfMethodDef(ctorToken, &ctorSig, &cbCtorSig, &ctorName));
+ }
+ else
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ SigParser sigParser(ctorSig, cbCtorSig);
+
+ ULONG callingConvention;
+ IfFailThrow(sigParser.GetCallingConvInfo(&callingConvention));
+ if (callingConvention != IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ ULONG cParameters;
+ IfFailThrow(sigParser.GetData(&cParameters));
+ if (cParameters < 1)
+ {
+ return FALSE;
+ }
+
+ BYTE returnElmentType;
+ IfFailThrow(sigParser.GetByte(&returnElmentType));
+ if (returnElmentType != ELEMENT_TYPE_VOID)
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ BYTE paramElementType;
+ IfFailThrow(sigParser.GetByte(&paramElementType));
+ if (paramElementType != ELEMENT_TYPE_CLASS)
+ {
+ return FALSE;
+ }
+
+ mdToken paramTypeToken;
+ IfFailThrow(sigParser.GetToken(&paramTypeToken));
+
+ if (TypeFromToken(paramTypeToken) != mdtTypeRef)
+ {
+ return FALSE;
+ }
+
+ LPCSTR paramTypeNamespace;
+ LPCSTR paramTypeName;
+ IfFailThrow(pImport->GetNameOfTypeRef(paramTypeToken, &paramTypeNamespace, &paramTypeName));
+ if (strcmp("System", paramTypeNamespace) != 0 || strcmp("Type", paramTypeName) != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//-------------------------------------------------------------
+void WinRTClassFactory::Init()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+ IMDInternalImport *pImport = m_pClassMT->GetMDImport();
+
+ {
+ // Sealed classes may have Windows.Foundation.Activatable attributes. Such classes must be sealed, because we'd
+ // have no way to use their ctor from a derived class (no composition)
+ // Unsealed classes may have Windows.Foundation.Composable attributes. These are currently mutually exclusive, but we
+ // may need to relax this in the future for versioning reasons (so a class can be unsealed in a new version without
+ // being binary breaking).
+ // Note that we just ignore activation attributes if they occur on the wrong type of class
+ LPCSTR attributeName;
+ UINT numExpectedParams;
+ if (IsComposition())
+ {
+ attributeName = g_WindowsFoundationComposableAttributeClassName;
+ }
+ else
+ {
+ attributeName = g_WindowsFoundationActivatableAttributeClassName;
+ }
+
+ MDEnumHolder hEnum(pImport);
+
+ // find and parse all WindowsFoundationActivatableAttribute/WindowsFoundationComposableAttribute attributes
+ hr = pImport->EnumCustomAttributeByNameInit(m_pClassMT->GetCl(), attributeName, &hEnum);
+ IfFailThrow(hr);
+
+ if (hr == S_OK) // there are factory interfaces
+ {
+ mdCustomAttribute tkAttribute;
+ while (pImport->EnumNext(&hEnum, &tkAttribute))
+ {
+ if (!AttributeFirstParamIsSystemType(tkAttribute, pImport))
+ {
+ // The first parameter of the Composable/Activatable attribute is not a System.Type
+ // and therefore the attribute does not specify a factory interface so we ignore the attribute
+ continue;
+ }
+ // get raw custom attribute
+ const BYTE *pbAttr = NULL;
+ ULONG cbAttr = 0;
+ IfFailThrowBF(pImport->GetCustomAttributeAsBlob(tkAttribute, (const void **)&pbAttr, &cbAttr), BFA_INVALID_TOKEN, m_pClassMT->GetModule());
+ CustomAttributeParser cap(pbAttr, cbAttr);
+ IfFailThrowBF(cap.ValidateProlog(), BFA_BAD_CA_HEADER, m_pClassMT->GetModule());
+
+ // The activation factory interface is stored in the attribute by type name
+ LPCUTF8 szFactoryInterfaceName;
+ ULONG cbFactoryInterfaceName;
+ IfFailThrow(cap.GetNonNullString(&szFactoryInterfaceName, &cbFactoryInterfaceName));
+
+ StackSString strFactoryInterface(SString::Utf8, szFactoryInterfaceName, cbFactoryInterfaceName);
+ MethodTable *pMTFactoryInterface = GetWinRTType(&strFactoryInterface, /* bThrowIfNotFound = */ TRUE).GetMethodTable();
+
+ _ASSERTE(pMTFactoryInterface);
+ m_factoryInterfaces.Append(pMTFactoryInterface);
+ }
+ }
+ }
+
+ {
+ // find and parse all Windows.Foundation.Static attributes
+ MDEnumHolder hEnum(pImport);
+ hr = pImport->EnumCustomAttributeByNameInit(m_pClassMT->GetCl(), g_WindowsFoundationStaticAttributeClassName, &hEnum);
+ IfFailThrow(hr);
+
+ if (hr == S_OK) // there are static interfaces
+ {
+ mdCustomAttribute tkAttribute;
+ while (pImport->EnumNext(&hEnum, &tkAttribute))
+ {
+ if (!AttributeFirstParamIsSystemType(tkAttribute, pImport))
+ {
+ // The first parameter of the Static attribute is not a System.Type
+ // and therefore the attribute does not specify a factory interface so we ignore the attribute
+ continue;
+ }
+
+ const BYTE *pbAttr = NULL;
+ ULONG cbAttr = 0;
+ IfFailThrowBF(pImport->GetCustomAttributeAsBlob(tkAttribute, (const void **)&pbAttr, &cbAttr), BFA_INVALID_TOKEN, m_pClassMT->GetModule());
+
+ CustomAttributeParser cap(pbAttr, cbAttr);
+ IfFailThrowBF(cap.ValidateProlog(), BFA_BAD_CA_HEADER, m_pClassMT->GetModule());
+
+ // retrieve the factory interface name
+ LPCUTF8 szName;
+ ULONG cbName;
+ IfFailThrow(cap.GetNonNullString(&szName, &cbName));
+
+ // copy the name to a temporary buffer and NULL terminate it
+ StackSString ss(SString::Utf8, szName, cbName);
+ TypeHandle th = GetWinRTType(&ss, /* bThrowIfNotFound = */ TRUE);
+
+ MethodTable *pMTStaticInterface = th.GetMethodTable();
+ m_staticInterfaces.Append(pMTStaticInterface);
+ }
+ }
+ }
+
+ {
+
+ // Special case (not pretty): WinMD types requires you to put DefaultAttribute on interfaceImpl to
+ // mark the interface as default interface. But C# doesn't allow you to do that so we have
+ // to do it manually here.
+ MethodTable* pAsyncTracingEventArgsMT = MscorlibBinder::GetClass(CLASS__ASYNC_TRACING_EVENT_ARGS);
+ if(pAsyncTracingEventArgsMT == m_pClassMT)
+ {
+ m_pDefaultItfMT = MscorlibBinder::GetClass(CLASS__IASYNC_TRACING_EVENT_ARGS);
+ }
+ else
+ {
+ // parse the DefaultAttribute to figure out the default interface of the class
+ HENUMInternalHolder hEnumInterfaceImpl(pImport);
+ hEnumInterfaceImpl.EnumInit(mdtInterfaceImpl, m_pClassMT->GetCl());
+
+ DWORD cInterfaces = pImport->EnumGetCount(&hEnumInterfaceImpl);
+ if (cInterfaces != 0)
+ {
+ mdInterfaceImpl ii;
+ while (pImport->EnumNext(&hEnumInterfaceImpl, &ii))
+ {
+ const BYTE *pbAttr;
+ ULONG cbAttr;
+ HRESULT hr = pImport->GetCustomAttributeByName(ii, g_WindowsFoundationDefaultClassName, (const void **)&pbAttr, &cbAttr);
+ IfFailThrow(hr);
+ if (hr == S_OK)
+ {
+ mdToken typeRefOrDefOrSpec;
+ IfFailThrow(pImport->GetTypeOfInterfaceImpl(ii, &typeRefOrDefOrSpec));
+
+ TypeHandle th = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(
+ m_pClassMT->GetModule(),
+ typeRefOrDefOrSpec,
+ NULL,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::FailIfUninstDefOrRef,
+ ClassLoader::LoadTypes, CLASS_LOAD_EXACTPARENTS);
+
+ m_pDefaultItfMT = th.GetMethodTable();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // initialize m_hClassName
+ InlineSString<DEFAULT_NONSTACK_CLASSNAME_SIZE> ssClassName;
+ m_pClassMT->_GetFullyQualifiedNameForClass(ssClassName);
+
+#ifndef CROSSGEN_COMPILE
+ if (!GetAppDomain()->IsCompilationDomain())
+ {
+ // don't bother creating the HSTRING when NGENing - we may run on downlevel
+ IfFailThrow(WindowsCreateString(ssClassName.GetUnicode(), ssClassName.GetCount(), &m_hClassName));
+ }
+#endif
+
+ if (ssClassName.BeginsWith(SL(W("Windows."))))
+ {
+ // parse the GCPressureAttribute only on first party runtime classes
+ const BYTE *pVal = NULL;
+ ULONG cbVal = 0;
+
+ if (S_OK == pImport->GetCustomAttributeByName(m_pClassMT->GetCl(), g_WindowsFoundationGCPressureAttributeClassName, (const void **)&pVal, &cbVal))
+ {
+ CustomAttributeParser cap(pVal, cbVal);
+ CaNamedArg namedArgs[1];
+
+ // First, the void constructor
+ IfFailThrow(ParseKnownCaArgs(cap, NULL, 0));
+
+ // Then, find the named argument
+ namedArgs[0].InitI4FieldEnum("amount", "Windows.Foundation.Metadata.GCPressureAmount", -1);
+
+ IfFailThrow(ParseKnownCaNamedArgs(cap, namedArgs, lengthof(namedArgs)));
+
+ static_assert(RCW::GCPressureSize_WinRT_Medium == RCW::GCPressureSize_WinRT_Low + 1, "RCW::GCPressureSize does not match Windows.Foundation.Metadata.GCPressureAmount");
+ static_assert(RCW::GCPressureSize_WinRT_High == RCW::GCPressureSize_WinRT_Medium + 1, "RCW::GCPressureSize does not match Windows.Foundation.Metadata.GCPressureAmount");
+
+ int amount = namedArgs[0].val.i4;
+ if (amount >= 0 && amount < (RCW::GCPressureSize_COUNT - RCW::GCPressureSize_WinRT_Low))
+ {
+ m_GCPressure = (RCW::GCPressureSize)(amount + RCW::GCPressureSize_WinRT_Low);
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------
+MethodDesc *WinRTClassFactory::FindFactoryMethod(PCCOR_SIGNATURE pSig, DWORD cSig, Module *pModule)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pSig));
+ PRECONDITION(CheckPointer(pModule));
+ }
+ CONTRACTL_END;
+
+ COUNT_T count = m_factoryInterfaces.GetCount();
+ for (UINT i = 0; i < count; i++)
+ {
+ MethodTable *pMT = m_factoryInterfaces[i];
+
+ MethodDesc *pMD = MemberLoader::FindMethod(pMT, "", pSig, cSig, pModule, MemberLoader::FM_IgnoreName);
+ if (pMD != NULL)
+ {
+ return pMD;
+ }
+ }
+
+ return NULL;
+}
+
+//-------------------------------------------------------------
+MethodDesc *WinRTClassFactory::FindStaticMethod(LPCUTF8 pszName, PCCOR_SIGNATURE pSig, DWORD cSig, Module *pModule)
+{
+ CONTRACTL
+ {
+ STANDARD_VM_CHECK;
+ PRECONDITION(CheckPointer(pszName));
+ PRECONDITION(CheckPointer(pSig));
+ PRECONDITION(CheckPointer(pModule));
+ }
+ CONTRACTL_END;
+
+ COUNT_T count = m_staticInterfaces.GetCount();
+ for (UINT i = 0; i < count; i++)
+ {
+ MethodTable *pMT = m_staticInterfaces[i];
+
+ MethodDesc *pMD = MemberLoader::FindMethod(pMT, pszName, pSig, cSig, pModule);
+ if (pMD != NULL)
+ {
+ return pMD;
+ }
+ }
+
+ return NULL;
+}
+
+//-------------------------------------------------------------
+void WinRTClassFactory::Cleanup()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_hClassName != NULL)
+ {
+ // HSTRING has been created, which means combase should have been loaded.
+ // Delay load will not fail.
+ _ASSERTE(WszGetModuleHandle(W("combase.dll")) != NULL);
+ CONTRACT_VIOLATION(ThrowsViolation);
+
+#ifndef CROSSGEN_COMPILE
+ WindowsDeleteString(m_hClassName);
+#endif
+ }
+ delete m_pWinRTOverrideInfo;
+ delete this;
+}
+
+//-------------------------------------------------------------
+void WinRTManagedClassFactory::Cleanup()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pCCWTemplate != NULL)
+ {
+ m_pCCWTemplate->Release();
+ m_pCCWTemplate = NULL;
+ }
+
+ WinRTClassFactory::Cleanup(); // deletes 'this'
+}
+#ifndef CROSSGEN_COMPILE
+//-------------------------------------------------------------
+ComCallWrapperTemplate *WinRTManagedClassFactory::GetOrCreateComCallWrapperTemplate(MethodTable *pFactoryMT)
+{
+ CONTRACT (ComCallWrapperTemplate *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pFactoryMT));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ if (m_pCCWTemplate == NULL)
+ {
+ ComCallWrapperTemplate::CreateTemplate(TypeHandle(pFactoryMT), this);
+ }
+
+ RETURN m_pCCWTemplate;
+}
+#endif // CROSSGEN_COMPILE
+
+#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
+
+#ifndef CROSSGEN_COMPILE
+//---------------------------------------------------------------------
+// RCW cache, act as the manager for the RCWs
+// uses a hash table to map IUnknown to the corresponding wrappers
+//---------------------------------------------------------------------
+
+// Obtain the appropriate wrapper cache from the current context.
+RCWCache* RCWCache::GetRCWCache()
+{
+ CONTRACT (RCWCache*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ AppDomain * pDomain = GetAppDomain();
+ RETURN (pDomain ? pDomain->GetRCWCache() : NULL);
+}
+
+RCWCache* RCWCache::GetRCWCacheNoCreate()
+{
+ CONTRACT (RCWCache*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ AppDomain * pDomain = GetAppDomain();
+ RETURN (pDomain ? pDomain->GetRCWCacheNoCreate() : NULL);
+}
+
+
+//---------------------------------------------------------------------
+// Constructor. Note we init the global RCW cleanup list in here too.
+RCWCache::RCWCache(AppDomain *pDomain)
+ : m_lock(CrstRCWCache, CRST_UNSAFE_COOPGC)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pDomain));
+ }
+ CONTRACTL_END;
+
+ m_pDomain = pDomain;
+}
+
+// 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
+// If pfMadeWrapperStrong is TRUE upon return, you NEED to call AddRef on pIdentity
+void RCWCache::FindWrapperInCache_NoLock(IUnknown* pIdentity, RCWHolder* pRCW)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pIdentity));
+ PRECONDITION(CheckPointer(pRCW));
+ }
+ CONTRACTL_END;
+
+ // lookup in our hash table
+ LookupWrapper(pIdentity, pRCW);
+
+ // check if we found the wrapper,
+ if (!pRCW->IsNull())
+ {
+ if ((*pRCW)->IsValid())
+ {
+ if ((*pRCW)->IsDetached())
+ {
+ _ASSERTE((LPVOID)pIdentity != (LPVOID)pRCW->GetRawRCWUnsafe()); // we should never find "unique" RCWs
+
+ // remove and re-insert the RCW using its unique identity
+ RemoveWrapper(pRCW);
+ (*pRCW)->m_pIdentity = (LPVOID)pRCW->GetRawRCWUnsafe();
+ InsertWrapper(pRCW);
+
+ pRCW->UnInit();
+ }
+ else
+ {
+ // addref the wrapper
+ (*pRCW)->AddRef(this);
+ }
+ }
+ else
+ {
+ pRCW->UnInit();
+ }
+ }
+
+ return;
+}
+
+BOOL RCWCache::FindOrInsertWrapper_NoLock(IUnknown* pIdentity, RCWHolder* pRCW, BOOL fAllowReinit)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pIdentity));
+ PRECONDITION(pIdentity != (IUnknown*)-1);
+ PRECONDITION(CheckPointer(pRCW));
+ PRECONDITION(CheckPointer(pRCW->GetRawRCWUnsafe()));
+ }
+ CONTRACTL_END;
+
+ BOOL fInserted = FALSE;
+
+ // we have created a wrapper, let us insert it into the hash table
+ // but we need to check if somebody beat us to it
+ {
+ // see if somebody beat us to it
+ // perf: unfold LookupWrapper to avoid creating RCWHolder in common cases
+ RCW *pRawRCW = LookupWrapperUnsafe(pIdentity);
+ if (pRawRCW == NULL)
+ {
+ InsertWrapper(pRCW);
+ fInserted = TRUE;
+ }
+ else
+ {
+ RCWHolder pTempRCW(GetThread());
+
+ // Assume that we already have a sync block for this object.
+ pTempRCW.InitNoCheck(pRawRCW);
+
+ // if we didn't find a valid wrapper, Insert our own wrapper
+ if (pTempRCW.IsNull() || !pTempRCW->IsValid())
+ {
+ // if we found a bogus wrapper, let us get rid of it
+ // so that when we insert we insert a valid wrapper, instead of duplicate
+ if (!pTempRCW.IsNull())
+ {
+ _ASSERTE(!pTempRCW->IsValid());
+ RemoveWrapper(&pTempRCW);
+ }
+
+ InsertWrapper(pRCW);
+ fInserted = TRUE;
+ }
+ else
+ {
+ _ASSERTE(!pTempRCW.IsNull() && pTempRCW->IsValid());
+ // okay we found a valid wrapper,
+
+ if (pTempRCW->IsDetached())
+ {
+ _ASSERTE((LPVOID)pIdentity != (LPVOID)pTempRCW.GetRawRCWUnsafe()); // we should never find "unique" RCWs
+
+ // remove and re-insert the RCW using its unique identity
+ RemoveWrapper(&pTempRCW);
+ pTempRCW->m_pIdentity = (LPVOID)pTempRCW.GetRawRCWUnsafe();
+ InsertWrapper(&pTempRCW);
+
+ // and insert the new incoming RCW
+ InsertWrapper(pRCW);
+ fInserted = TRUE;
+ }
+ else if (fAllowReinit)
+ {
+ // addref the wrapper
+ pTempRCW->AddRef(this);
+
+ // Initialize the holder with the rcw we're going to return.
+ OBJECTREF objref = pTempRCW->GetExposedObject();
+ pTempRCW.UnInit();
+ pRCW->UnInit();
+ pRCW->InitNoCheck(objref);
+ }
+ }
+ }
+ }
+
+ return fInserted;
+}
+
+//--------------------------------------------------------------------------------
+// ULONG RCWCache::ReleaseWrappers()
+// Helper to release the complus wrappers in the cache that lives in the specified
+// context (including Jupiter RCWs) or all the wrappers in the cache if the pCtxCookie is null.
+void RCWCache::ReleaseWrappersWorker(LPVOID pCtxCookie)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCtxCookie, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ RCWCleanupList CleanupList;
+ RCWCleanupList AggregatedCleanupList;
+
+ struct RCWInterfacePointer
+ {
+ IUnknown *m_pUnk;
+ RCW *m_pRCW;
+ CtxEntry *m_pCtxEntry;
+ };
+
+ // Arrays of individual interface pointers to call Release on
+ CQuickArrayList<RCWInterfacePointer> InterfacePointerList;
+ CQuickArrayList<RCWInterfacePointer> AggregatedInterfacePointerList;
+
+ // Switch to cooperative GC mode before we take the lock.
+ GCX_COOP();
+ {
+ {
+ RCWCache::LockHolder lh(this);
+
+ // Go through the hash table and add the wrappers to the cleanup lists.
+ for (SHash<RCWCacheTraits>::Iterator it = m_HashMap.Begin(); it != m_HashMap.End(); it++)
+ {
+ RCW *pWrap = *it;
+ _ASSERTE(pWrap != NULL);
+
+ // If a context cookie was specified, then only clean up wrappers that
+ // are in that context, including non-FTM regular RCWs, and FTM Jupiter objects
+ // Otherwise clean up all the wrappers.
+ // Ignore RCWs that aggregate the FTM if we are cleaning up context
+ // specific RCWs (note that we rely on this behavior in WinRT factory cache code)
+ // Note that Jupiter RCWs are special and they are considered to be context-bound
+ if (!pCtxCookie || ((pWrap->GetWrapperCtxCookie() == pCtxCookie) && (pWrap->IsJupiterObject() || !pWrap->IsFreeThreaded())))
+ {
+ if (!pWrap->IsURTAggregated())
+ CleanupList.AddWrapper_NoLock(pWrap);
+ else
+ AggregatedCleanupList.AddWrapper_NoLock(pWrap);
+
+ pWrap->DecoupleFromObject();
+ RemoveWrapper(pWrap);
+ }
+ else if (!pWrap->IsFreeThreaded())
+ {
+ // We have a non-zero pCtxCookie but this RCW was not created in that context. We still
+ // need to take a closer look at the RCW because its interface pointer cache may contain
+ // pointers acquired in the given context - and those need to be released here.
+ if (pWrap->m_pAuxiliaryData != NULL)
+ {
+ RCWAuxiliaryData::InterfaceEntryIterator it = pWrap->m_pAuxiliaryData->IterateInterfacePointers();
+ while (it.Next())
+ {
+ InterfaceEntry *pEntry = it.GetEntry();
+ if (!pEntry->IsFree() && it.GetCtxCookie() == pCtxCookie)
+ {
+ RCWInterfacePointer intfPtr;
+ intfPtr.m_pUnk = pEntry->m_pUnknown;
+ intfPtr.m_pRCW = pWrap;
+ intfPtr.m_pCtxEntry = it.GetCtxEntryNoAddRef();
+
+ if (!pWrap->IsURTAggregated())
+ InterfacePointerList.Push(intfPtr);
+ else
+ AggregatedInterfacePointerList.Push(intfPtr);
+
+ // Reset the CtxEntry first, so we don't race with RCWAuxiliaryData::CacheInterfacePointer
+ // which may try to reuse the InterfaceEntry for another (pUnk, MT, CtxEntry) triplet.
+ it.ResetCtxEntry();
+ pEntry->Free();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Clean up the non URT aggregated RCW's first then clean up the URT aggregated RCW's.
+ CleanupList.CleanupAllWrappers();
+
+ for (SIZE_T i = 0; i < InterfacePointerList.Size(); i++)
+ {
+ RCWInterfacePointer &intfPtr = InterfacePointerList[i];
+
+ RCW_VTABLEPTR(intfPtr.m_pRCW);
+ SafeRelease(intfPtr.m_pUnk, intfPtr.m_pRCW);
+
+ intfPtr.m_pCtxEntry->Release();
+ }
+
+ AggregatedCleanupList.CleanupAllWrappers();
+
+ for (SIZE_T i = 0; i < AggregatedInterfacePointerList.Size(); i++)
+ {
+ RCWInterfacePointer &intfPtr = AggregatedInterfacePointerList[i];
+
+ RCW_VTABLEPTR(intfPtr.m_pRCW);
+ SafeRelease(intfPtr.m_pUnk, intfPtr.m_pRCW);
+
+ intfPtr.m_pCtxEntry->Release();
+ }
+
+ }
+
+ if (!CleanupList.IsEmpty() || !AggregatedCleanupList.IsEmpty())
+ {
+ _ASSERTE(!"Cannot cleanup RCWs in cleanup list. Most likely because the RCW is disabled for eager cleanup.");
+ LOG((LF_INTEROP, LL_INFO1000, "Cannot cleanup RCWs in cleanup list. Most likely because the RCW is disabled for eager cleanup."));
+ }
+}
+
+//--------------------------------------------------------------------------------
+// ULONG RCWCache::DetachWrappersWorker()
+// Helper to mark RCWs that are not GC-promoted at this point as detached.
+class DetachWrappersFunctor
+{
+public:
+ FORCEINLINE void operator() (RCW *pRCW)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (pRCW->IsValid())
+ {
+ if (!GCHeap::GetGCHeap()->IsPromoted(OBJECTREFToObject(pRCW->GetExposedObject())) &&
+ !pRCW->IsDetached())
+ {
+ // No need to use InterlockedOr here since every other place that modifies the flags
+ // runs in cooperative GC mode (i.e. definitely not concurrently with this function).
+ pRCW->m_Flags.m_Detached = 1;
+
+ if (pRCW->IsJupiterObject())
+ RCWWalker::BeforeJupiterRCWDestroyed(pRCW);
+ }
+ }
+ }
+};
+
+void RCWCache::DetachWrappersWorker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(GCHeap::IsGCInProgress()); // GC is in progress and the runtime is suspended
+ }
+ CONTRACTL_END;
+
+ DetachWrappersFunctor functor;
+ m_HashMap.ForEach(functor);
+}
+
+VOID RCWCleanupList::AddWrapper(RCW* pRCW)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // For the global cleanup list, this is called only from the finalizer thread
+ _ASSERTE(this != g_pRCWCleanupList || GetThread() == FinalizerThread::GetFinalizerThread());
+
+ {
+ CrstHolder ch(&m_lock);
+
+ AddWrapper_NoLock(pRCW);
+ }
+}
+
+VOID RCWCleanupList::AddWrapper_NoLock(RCW* pRCW)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Traverse the list for match - when found, insert as the matching bucket head.
+ RCW *pBucket = m_pFirstBucket;
+ RCW *pPrevBucket = NULL;
+ while (pBucket != NULL)
+ {
+ if (pRCW->MatchesCleanupBucket(pBucket))
+ {
+ // Insert as bucket head.
+ pRCW->m_pNextRCW = pBucket;
+ pRCW->m_pNextCleanupBucket = pBucket->m_pNextCleanupBucket;
+
+ // Not necessary but makes it clearer that pBucket is no longer a bucket head.
+ pBucket->m_pNextCleanupBucket = NULL;
+ break;
+ }
+ pPrevBucket = pBucket;
+ pBucket = pBucket->m_pNextCleanupBucket;
+ }
+
+ // If we didn't find a match, insert as a new bucket.
+ if (pBucket == NULL)
+ {
+ pRCW->m_pNextRCW = NULL;
+ pRCW->m_pNextCleanupBucket = NULL;
+ }
+
+ // pRCW is now a bucket head - the only thing missing is a link from the previous bucket head.
+ if (pPrevBucket != NULL)
+ pPrevBucket->m_pNextCleanupBucket = pRCW;
+ else
+ m_pFirstBucket = pRCW;
+}
+
+VOID RCWCleanupList::CleanupAllWrappers()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ // For the global cleanup list, this is called only from the finalizer thread
+ PRECONDITION( (this != g_pRCWCleanupList) || (GetThread() == FinalizerThread::GetFinalizerThread()));
+ }
+ CONTRACTL_END;
+
+ RemovedBuckets NonSTABuckets;
+ RemovedBuckets STABuckets;
+
+ // We sweep the cleanup list once and remove all MTA/Free-Threaded buckets as well as STA buckets, leaving only
+ // those with disabled eager cleanup in the list. Then we drop the lock, walk the removed buckets,
+ // and perform the actual release. We cannot be releasing during the initial sweep because we would
+ // need to drop and reacquire the lock for each bucket which would invalidate the entire linked
+ // list so we would need to start the enumeration over after each bucket.
+ {
+ // Take the lock
+ CrstHolder ch(&m_lock);
+
+ RCW *pBucket = m_pFirstBucket;
+ RCW *pPrevBucket = NULL;
+ while (pBucket != NULL)
+ {
+ RCW *pNextBucket = pBucket->m_pNextCleanupBucket;
+ Thread *pSTAThread = pBucket->GetSTAThread();
+
+ if (pSTAThread == NULL || pBucket->AllowEagerSTACleanup())
+ {
+ // Remove the list from the CleanupList structure
+ if (pPrevBucket != NULL)
+ pPrevBucket->m_pNextCleanupBucket = pBucket->m_pNextCleanupBucket;
+ else
+ m_pFirstBucket = pBucket->m_pNextCleanupBucket;
+
+ if (pSTAThread == NULL)
+ {
+ // and add it to the local MTA/Free-Threaded chain
+ NonSTABuckets.Append(pBucket);
+ }
+ else
+ {
+ // or to the local STA chain
+ STABuckets.Append(pBucket);
+ }
+ }
+ else
+ {
+ // move the 'previous' pointer only if we didn't remove the current bucket
+ pPrevBucket = pBucket;
+ }
+ pBucket = pNextBucket;
+ }
+ // Release the lock so we can correctly transition to cleanup.
+ }
+
+ // Request help from other threads
+ m_doCleanupInContexts = TRUE;
+
+ // First, cleanup the MTA/Free-Threaded buckets
+ RCW *pRCWToCleanup;
+ while ((pRCWToCleanup = NonSTABuckets.PopHead()) != NULL)
+ {
+ ReleaseRCWList_Args args;
+ args.pHead = pRCWToCleanup;
+ args.ctxTried = FALSE;
+ args.ctxBusy = FALSE;
+
+ ReleaseRCWListInCorrectCtx(&args);
+ }
+
+ // Now, cleanup the STA buckets
+ while ((pRCWToCleanup = STABuckets.PopHead()) != NULL)
+ {
+ //
+ // CAUTION: DONOT access pSTAThread fields here as pSTAThread
+ // could've already been deleted, if
+ // 1) the RCW is a free threaded RCW
+ // 2) the RCW is a regular RCW, and marked by GC but not finalized yet
+ // Only pointer comparison is allowed.
+ //
+ ReleaseRCWList_Args args;
+ args.pHead = pRCWToCleanup;
+ args.ctxTried = FALSE;
+ args.ctxBusy = FALSE;
+
+ // Advertise the fact that we're cleaning up this thread.
+ m_pCurCleanupThread = pRCWToCleanup->GetSTAThread();
+ _ASSERTE(pRCWToCleanup->GetSTAThread() != NULL);
+
+ ReleaseRCWListInCorrectCtx(&args);
+
+ // Done cleaning this thread for now...reset
+ m_pCurCleanupThread = NULL;
+ }
+
+ // No more stuff for other threads to help with
+ m_doCleanupInContexts = FALSE;
+}
+
+
+VOID RCWCleanupList::CleanupWrappersInCurrentCtxThread(BOOL fWait, BOOL fManualCleanupRequested, BOOL bIgnoreComObjectEagerCleanupSetting)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (!m_doCleanupInContexts && !fManualCleanupRequested)
+ return;
+
+ // Find out our STA (if any)
+ Thread *pThread = GetThread();
+ LPVOID pCurrCtxCookie = GetCurrentCtxCookie();
+
+ Thread::ApartmentState aptState = pThread->GetApartment();
+
+ RemovedBuckets BucketsToCleanup;
+
+ {
+ // Take the lock
+ CrstHolder ch(&m_lock);
+
+ RCW *pBucket = m_pFirstBucket;
+ RCW *pPrevBucket = NULL;
+ while (pBucket != NULL)
+ {
+ BOOL fMatch = FALSE;
+ RCW *pNextBucket = pBucket->m_pNextCleanupBucket;
+
+ if (aptState != Thread::AS_InSTA)
+ {
+ // If we're in an MTA, just look for a matching contexts (including free-threaded and non-free threaded)
+ if (pBucket->GetSTAThread() == NULL &&
+ (pCurrCtxCookie == NULL || pBucket->GetWrapperCtxCookie() == pCurrCtxCookie))
+ {
+ fMatch = TRUE;
+ }
+ }
+ else
+ {
+ // If we're in an STA, clean all matching STA contexts (including free-threaded and non-free threaded)
+ if (pBucket->GetWrapperCtxCookie() == pCurrCtxCookie &&
+ (bIgnoreComObjectEagerCleanupSetting || pBucket->AllowEagerSTACleanup()))
+ {
+ fMatch = TRUE;
+ }
+ }
+
+ if (fMatch)
+ {
+ // Remove the list from the CleanupList structure
+ if (pPrevBucket != NULL)
+ pPrevBucket->m_pNextCleanupBucket = pBucket->m_pNextCleanupBucket;
+ else
+ m_pFirstBucket = pBucket->m_pNextCleanupBucket;
+
+ // and add it to the local cleanup chain
+ BucketsToCleanup.Append(pBucket);
+ }
+ else
+ {
+ // move the 'previous' pointer only if we didn't remove the current bucket
+ pPrevBucket = pBucket;
+ }
+ pBucket = pNextBucket;
+ }
+ }
+
+ // Clean it up
+ RCW *pRCWToCleanup;
+ while ((pRCWToCleanup = BucketsToCleanup.PopHead()) != NULL)
+ {
+ if (pRCWToCleanup->GetSTAThread() == NULL)
+ {
+ // We're already in the correct context, just clean it.
+ ReleaseRCWListRaw(pRCWToCleanup);
+ }
+ else
+ {
+ ReleaseRCWList_Args args;
+ args.pHead = pRCWToCleanup;
+ args.ctxTried = FALSE;
+ args.ctxBusy = FALSE;
+
+ ReleaseRCWListInCorrectCtx(&args);
+ }
+ }
+
+ if (aptState == Thread::AS_InSTA)
+ {
+ if (fWait && m_pCurCleanupThread == pThread)
+ {
+ // The finalizer thread may be trying to enter our STA -
+ // make sure it can get in.
+
+ LOG((LF_INTEROP, LL_INFO1000, "Thread %p: Yielding to finalizer thread.\n", pThread));
+
+ // Do a noop wait just to make sure we are cooperating
+ // with the finalizer thread
+ pThread->Join(1, TRUE);
+ }
+ }
+}
+
+BOOL RCWCleanupList::IsEmpty()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (m_pFirstBucket == NULL);
+}
+
+// static
+HRESULT RCWCleanupList::ReleaseRCWListInCorrectCtx(LPVOID pData)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pData));
+ }
+ CONTRACTL_END;
+
+ ReleaseRCWList_Args* args = (ReleaseRCWList_Args*)pData;
+
+#ifdef FEATURE_REMOTING
+ if (InSendMessage())
+ {
+ args->ctxBusy = TRUE;
+ return S_OK;
+ }
+#endif
+
+ RCW* pHead = (RCW *)args->pHead;
+
+ LPVOID pCurrCtxCookie = GetCurrentCtxCookie();
+
+ // If we are releasing our IP's as a result of shutdown, we MUST not transition
+ // into cooperative GC mode. This "fix" will prevent us from doing so.
+ if (g_fEEShutDown & ShutDown_Finalize2)
+ {
+ Thread *pThread = GetThread();
+ if (pThread && !FinalizerThread::IsCurrentThreadFinalizer())
+ pThread->SetThreadStateNC(Thread::TSNC_UnsafeSkipEnterCooperative);
+ }
+
+
+ // Make sure we're in the right context / apartment.
+ // Also - if we've already transitioned once, we don't want to do so again.
+ // If the cookie exists in multiple MTA apartments, and the STA has gone away
+ // (leaving the old STA thread as unknown state with a context value equal to
+ // the MTA context), we will infinitely loop. So, we short circuit this with ctxTried.
+
+ Thread *pHeadThread = pHead->GetSTAThread();
+ BOOL fCorrectThread = (pHeadThread == NULL) ? TRUE : (pHeadThread == GetThread());
+ BOOL fCorrectCookie = (pCurrCtxCookie == NULL) ? TRUE : (pHead->GetWrapperCtxCookie() == pCurrCtxCookie);
+
+ if ( pHead->IsFreeThreaded() || // Avoid context transition if the list is for free threaded RCW
+ (fCorrectThread && fCorrectCookie) || args->ctxTried )
+ {
+ ReleaseRCWListRaw(pHead);
+ }
+ else
+ {
+ // Mark that we're trying a context transition
+ args->ctxTried = TRUE;
+
+ // Transition into the context to release the interfaces.
+ HRESULT hr = pHead->EnterContext(ReleaseRCWListInCorrectCtx, args);
+ if (FAILED(hr) || args->ctxBusy)
+ {
+ // We are having trouble transitioning into the context (typically because the context is disconnected)
+ // or the context is busy so we cannot transition into it to clean up.
+ // The only option we have left is to try and clean up the RCW's from the current context.
+ ReleaseRCWListRaw(pHead);
+ }
+ }
+
+ // Reset the bit indicating we cannot transition into cooperative GC mode.
+ if (g_fEEShutDown & ShutDown_Finalize2)
+ {
+ Thread *pThread = GetThread();
+ if (pThread && !FinalizerThread::IsCurrentThreadFinalizer())
+ pThread->ResetThreadStateNC(Thread::TSNC_UnsafeSkipEnterCooperative);
+ }
+
+ return S_OK;
+}
+
+// static
+VOID RCWCleanupList::ReleaseRCWListRaw(RCW* pRCW)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pRCW));
+ }
+ CONTRACTL_END;
+
+ // Release all these RCWs
+ RCW* pNext = NULL;
+ while (pRCW != NULL)
+ {
+ pNext = pRCW->m_pNextRCW;
+ pRCW->Cleanup();
+ pRCW = pNext;
+ }
+}
+
+// Destroys RCWAuxiliaryData. Note that we do not release interface pointers stored in the
+// auxiliary interface pointer cache here. That needs to be done in the right COM context
+// (see code:RCW::ReleaseAuxInterfacesCallBack).
+RCWAuxiliaryData::~RCWAuxiliaryData()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_prVariantInterfaces != NULL)
+ {
+ delete m_prVariantInterfaces;
+ }
+
+ InterfaceEntryEx *pEntry = m_pInterfaceCache;
+ while (pEntry)
+ {
+ InterfaceEntryEx *pNextEntry = pEntry->m_pNext;
+
+ delete pEntry;
+ pEntry = pNextEntry;
+ }
+
+ if (VARIANCE_STUB_TARGET_IS_HANDLE(m_ohObjectVariantCallTarget_IEnumerable))
+ {
+ DestroyHandle(m_ohObjectVariantCallTarget_IEnumerable);
+ }
+ if (VARIANCE_STUB_TARGET_IS_HANDLE(m_ohObjectVariantCallTarget_IReadOnlyList))
+ {
+ DestroyHandle(m_ohObjectVariantCallTarget_IReadOnlyList);
+ }
+}
+
+// Inserts variant interfaces to the cache.
+void RCWAuxiliaryData::CacheVariantInterface(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ CrstHolder ch(&m_VarianceCacheCrst);
+
+ if (m_prVariantInterfaces == NULL)
+ {
+ m_prVariantInterfaces = new ArrayList();
+ }
+
+ if (pMT->HasVariance() && m_prVariantInterfaces->FindElement(0, pMT) == ArrayList::NOT_FOUND)
+ {
+ m_prVariantInterfaces->Append(pMT);
+ }
+
+ // check implemented interfaces as well
+ MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
+ while (it.Next())
+ {
+ MethodTable *pItfMT = it.GetInterface();
+ if (pItfMT->HasVariance() && m_prVariantInterfaces->FindElement(0, pItfMT) == ArrayList::NOT_FOUND)
+ {
+ m_prVariantInterfaces->Append(pItfMT);
+ }
+ }
+}
+
+// Inserts an interface pointer in the cache.
+void RCWAuxiliaryData::CacheInterfacePointer(MethodTable *pMT, IUnknown *pUnk, LPVOID pCtxCookie)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ InterfaceEntryEx *pEntry = NULL;
+
+ // first, try to find a free entry to reuse
+ InterfaceEntryIterator it = IterateInterfacePointers();
+ while (it.Next())
+ {
+ InterfaceEntry *pEntry = it.GetEntry();
+ if (pEntry->IsFree() && pEntry->Init(pMT, pUnk))
+ {
+ // setting the cookie after "publishing" the entry is fine, at worst
+ // we may miss the cache if someone looks for this pMT concurrently
+ _ASSERTE_MSG(it.GetCtxCookie() == NULL, "Race condition detected, we are supposed to own the InterfaceEntry at this point");
+ it.SetCtxCookie(pCtxCookie);
+ return;
+ }
+ }
+
+ // create a new entry if a free one was not found
+ InterfaceEntryEx *pEntryEx = new InterfaceEntryEx();
+ ZeroMemory(pEntryEx, sizeof(InterfaceEntryEx));
+
+ pEntryEx->m_BaseEntry.Init(pMT, pUnk);
+
+ if (pCtxCookie != NULL)
+ {
+ pEntryEx->m_pCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(pCtxCookie, GetThread());
+ }
+ else
+ {
+ pEntryEx->m_pCtxEntry = NULL;
+ }
+
+ // and insert it into the linked list (the interlocked operation ensures that
+ // the list is walkable by other threads at all times)
+ InterfaceEntryEx *pNext;
+ do
+ {
+ pNext = VolatileLoad(&m_pInterfaceCache); // our candidate "next"
+ pEntryEx->m_pNext = pNext;
+ }
+ while (FastInterlockCompareExchangePointer(&m_pInterfaceCache, pEntryEx, pNext) != pNext);
+}
+
+// Returns a cached interface pointer or NULL if there was no match.
+IUnknown *RCWAuxiliaryData::FindInterfacePointer(MethodTable *pMT, LPVOID pCtxCookie)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ InterfaceEntryIterator it = IterateInterfacePointers();
+ while (it.Next())
+ {
+ InterfaceEntry *pEntry = it.GetEntry();
+ if (!pEntry->IsFree() && pEntry->m_pMT == (IE_METHODTABLE_PTR)pMT && it.GetCtxCookie() == pCtxCookie)
+ {
+ return pEntry->m_pUnknown;
+ }
+ }
+
+ return NULL;
+}
+
+const int RCW::s_rGCPressureTable[GCPressureSize_COUNT] =
+{
+ 0, // GCPressureSize_None
+ GC_PRESSURE_PROCESS_LOCAL, // GCPressureSize_ProcessLocal
+ GC_PRESSURE_MACHINE_LOCAL, // GCPressureSize_MachineLocal
+ GC_PRESSURE_REMOTE, // GCPressureSize_Remote
+ GC_PRESSURE_WINRT_BASE, // GCPressureSize_WinRT_Base
+ GC_PRESSURE_WINRT_LOW, // GCPressureSize_WinRT_Low
+ GC_PRESSURE_WINRT_MEDIUM, // GCPressureSize_WinRT_Medium
+ GC_PRESSURE_WINRT_HIGH, // GCPressureSize_WinRT_High
+};
+
+// Deletes all items in code:s_RCWStandbyList.
+void RCW::FlushStandbyList()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ PSLIST_ENTRY pEntry = InterlockedFlushSList(&RCW::s_RCWStandbyList);
+ while (pEntry)
+ {
+ PSLIST_ENTRY pNextEntry = pEntry->Next;
+ delete (RCW *)pEntry;
+ pEntry = pNextEntry;
+ }
+}
+//--------------------------------------------------------------------------------
+// The IUnknown passed in is AddRef'ed if we succeed in creating the wrapper unless
+// the CF_SuppressAddRef flag is set.
+RCW* RCW::CreateRCW(IUnknown *pUnk, DWORD dwSyncBlockIndex, DWORD flags, MethodTable *pClassMT)
+{
+ CONTRACT (RCW*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACT_END;
+
+ RCW *pRCW = NULL;
+
+ {
+ GCX_PREEMP();
+ pRCW = RCW::CreateRCWInternal(pUnk, dwSyncBlockIndex, flags, pClassMT);
+ }
+
+ // No exception after this point
+ if (pRCW->IsJupiterObject())
+ RCWWalker::AfterJupiterRCWCreated(pRCW);
+
+ RETURN pRCW;
+}
+
+RCW* RCW::CreateRCWInternal(IUnknown *pUnk, DWORD dwSyncBlockIndex, DWORD flags, MethodTable *pClassMT)
+{
+ CONTRACT (RCW*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(dwSyncBlockIndex != 0);
+ PRECONDITION(CheckPointer(pClassMT));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ // now allocate the wrapper
+ RCW *pWrap = (RCW *)InterlockedPopEntrySList(&RCW::s_RCWStandbyList);
+ if (pWrap != NULL)
+ {
+ // cache hit - reinitialize the data structure
+ new (pWrap) RCW();
+ }
+ else
+ {
+ pWrap = new RCW();
+ }
+
+ AppDomain * pAppDomain = GetAppDomain();
+ if((flags & CF_QueryForIdentity) ||
+ (pAppDomain && pAppDomain->GetDisableInterfaceCache()))
+ {
+ IUnknown *pUnkTemp = NULL;
+ HRESULT hr = SafeQueryInterfacePreemp(pUnk, IID_IUnknown, &pUnkTemp);
+ LogInteropQI(pUnk, IID_IUnknown, hr, "QI for IID_IUnknown in RCW::CreateRCW");
+ if(SUCCEEDED(hr))
+ {
+ pUnk = pUnkTemp;
+
+ }
+ }
+ else
+ {
+ ULONG cbRef = SafeAddRefPreemp(pUnk);
+ LogInteropAddRef(pUnk, cbRef, "RCWCache::CreateRCW: Addref pUnk because creating new RCW");
+ }
+
+ // Make sure we release AddRef-ed pUnk in case of exceptions
+ SafeComHolderPreemp<IUnknown> pUnkHolder = pUnk;
+
+ // Log the creation
+ LogRCWCreate(pWrap, pUnk);
+
+ // Remember that the object is known to support IInspectable
+ pWrap->m_Flags.m_fSupportsIInspectable = !!(flags & CF_SupportsIInspectable);
+
+ // Initialize wrapper
+ pWrap->Initialize(pUnk, dwSyncBlockIndex, pClassMT);
+
+ if (flags & CF_SupportsIInspectable)
+ {
+ // WinRT objects always apply some GC pressure
+ GCPressureSize pressureSize = GCPressureSize_WinRT_Base;
+
+ // if we have a strongly-typed non-delegate RCW, we may have read the GC pressure amount from metadata
+ if (pClassMT->IsProjectedFromWinRT() && !pClassMT->IsDelegate())
+ {
+ WinRTClassFactory *pFactory = GetComClassFactory(pClassMT)->AsWinRTClassFactory();
+ pressureSize = pFactory->GetGCPressure();
+ }
+
+ pWrap->AddMemoryPressure(pressureSize);
+ }
+
+ // Check to see if this is a DCOM proxy if either we've been explicitly asked to, or if
+ // we're talking to a non-WinRT object and we need to add memory pressure
+ const bool checkForDCOMProxy = (flags & CF_DetectDCOMProxy) ||
+ !(flags & CF_SupportsIInspectable);
+
+ if (checkForDCOMProxy)
+ {
+ // If the object is a DCOM proxy...
+ SafeComHolderPreemp<IRpcOptions> pRpcOptions = NULL;
+ GCPressureSize pressureSize = GCPressureSize_None;
+ HRESULT hr = pWrap->SafeQueryInterfaceRemoteAware(IID_IRpcOptions, (IUnknown**)&pRpcOptions);
+ LogInteropQI(pUnk, IID_IRpcOptions, hr, "QI for IRpcOptions");
+ if (S_OK == hr)
+ {
+ ULONG_PTR dwValue = 0;
+ hr = pRpcOptions->Query(pUnk, COMBND_SERVER_LOCALITY, &dwValue);
+
+ if (SUCCEEDED(hr))
+ {
+ if (dwValue == SERVER_LOCALITY_MACHINE_LOCAL || dwValue == SERVER_LOCALITY_REMOTE)
+ {
+ pWrap->m_Flags.m_fIsDCOMProxy = 1;
+ }
+
+ // Only add memory pressure for proxies for non-WinRT objects
+ if (!(flags & CF_SupportsIInspectable))
+ {
+ switch(dwValue)
+ {
+ case SERVER_LOCALITY_PROCESS_LOCAL:
+ pressureSize = GCPressureSize_ProcessLocal;
+ break;
+ case SERVER_LOCALITY_MACHINE_LOCAL:
+ pressureSize = GCPressureSize_MachineLocal;
+ break;
+ case SERVER_LOCALITY_REMOTE:
+ pressureSize = GCPressureSize_Remote;
+ break;
+ default:
+ pressureSize = GCPressureSize_None;
+ break;
+ }
+ }
+ }
+ }
+
+ // ...add the appropriate amount of memory pressure to the GC.
+ if (pressureSize != GCPressureSize_None)
+ {
+ pWrap->AddMemoryPressure(pressureSize);
+ }
+ }
+
+ pUnkHolder.SuppressRelease();
+
+ RETURN pWrap;
+}
+
+//----------------------------------------------------------
+// Init IUnknown and IDispatch cookies with the pointers, and assocaiate the COMOBJECTREF with this RCW
+void RCW::Initialize(IUnknown* pUnk, DWORD dwSyncBlockIndex, MethodTable *pClassMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ INJECT_FAULT(ThrowOutOfMemory());
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(dwSyncBlockIndex != 0);
+ PRECONDITION(CheckPointer(pClassMT));
+ }
+ CONTRACTL_END;
+
+ m_cbRefCount = 1;
+
+ // Start with use count 1 (this is counteracted in RCW::Cleanup)
+ m_cbUseCount = 1;
+
+ // Cache the IUnk and thread
+ m_pIdentity = pUnk;
+
+ // Remember the VTable pointer of the COM IP.
+ // This is very helpful for tracking down early released COM objects
+ // that AV when you call IUnknown::Release.
+ m_vtablePtr = *(LPVOID*)pUnk;
+
+ // track the thread that created this wrapper
+ // if this thread is an STA thread, then when the STA dies
+ // we need to cleanup this wrapper
+ m_pCreatorThread = GetThread();
+ _ASSERTE(m_pCreatorThread != NULL);
+
+ m_pRCWCache = RCWCache::GetRCWCache();
+
+ m_Flags.m_MarshalingType = GetMarshalingType(pUnk, pClassMT);
+
+ // Initialize the IUnkEntry
+ m_UnkEntry.Init(pUnk, IsFreeThreaded(), m_pCreatorThread DEBUGARG(this));
+
+ // Determine AllowEagerSTACleanup setting right now
+ // We don't want to access the thread object to get the status
+ // as m_pCreatorThread could be already dead at RCW cleanup time
+ //
+ // Free threaded RCWs created from STA "survives" even after the pSTAThread is terminated
+ // and destroyed. For free threaded objects, there will be no pumping at all and user should always
+ // expect the object to be accessed concurrently.
+ //
+ // So, only disallow eager STA cleanup for non free-threaded RCWs. Free threaded RCWs
+ // should be cleaned up regardless of the setting on thread.
+ bool disableEagerCleanup = m_pCreatorThread->IsDisableComObjectEagerCleanup();
+
+ if (disableEagerCleanup && !IsFreeThreaded())
+ m_Flags.m_fAllowEagerSTACleanup = 0;
+ else
+ m_Flags.m_fAllowEagerSTACleanup = 1;
+
+ // store the wrapper in the sync block, that is the only way we can get cleaned up
+ // the syncblock is guaranteed to be present
+ SyncBlock *pSyncBlock = g_pSyncTable[(int)dwSyncBlockIndex].m_SyncBlock;
+ InteropSyncBlockInfo *pInteropInfo = pSyncBlock->GetInteropInfo();
+ pInteropInfo->SetRawRCW(this);
+
+ // Store the sync block index.
+ m_SyncBlockIndex = dwSyncBlockIndex;
+
+ // Check if this object is a Jupiter object (only for WinRT scenarios)
+ _ASSERTE(m_Flags.m_fIsJupiterObject == 0);
+ if (SupportsIInspectable())
+ {
+ SafeComHolderPreemp<IJupiterObject> pJupiterObject = NULL;
+ HRESULT hr = SafeQueryInterfacePreemp(pUnk, IID_IJupiterObject, (IUnknown **)&pJupiterObject);
+ LogInteropQI(pUnk, IID_IJupiterObject, hr, "QI for IJupiterObject");
+
+ if (SUCCEEDED(hr))
+ {
+ // A Jupiter object that is not free threaded is not allowed
+ if (!IsFreeThreaded())
+ {
+ StackSString ssObjClsName;
+ StackSString ssDestClsName;
+
+ pClassMT->_GetFullyQualifiedNameForClass(ssObjClsName);
+
+ COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST,
+ ssObjClsName.GetUnicode(), W("IAgileObject"));
+ }
+
+ RCWWalker::OnJupiterRCWCreated(this, pJupiterObject);
+
+ SetJupiterObject(pJupiterObject);
+
+ if (!IsURTAggregated())
+ {
+ pJupiterObject.SuppressRelease();
+ }
+ }
+ }
+
+ // Log the wrapper initialization.
+ LOG((LF_INTEROP, LL_INFO100, "Initializing RCW %p with SyncBlock index %d\n", this, dwSyncBlockIndex));
+
+ // To help combat finalizer thread starvation, we check to see if there are any wrappers
+ // scheduled to be cleaned up for our context. If so, we'll do them here to avoid making
+ // the finalizer thread do a transition.
+ // @perf: This may need a bit of tuning.
+ // Note: This will enter a message pump in order to synchronize with the finalizer thread.
+
+ // We can't safely pump here for Releasing (or directly release)
+ // if we're currently in a SendMessage.
+ // Also, clients can opt out of this. The option is is a per-thread flag which they can
+ // set by calling DisableComEagerCleanup on the appropriate thread. Why would they
+ // want to opt out? Because pumping can lead to re-entrancy in in unexpected places.
+ // If a client decides to opt out, they are required to cleanup RCWs themselves by
+ // calling Marshal.CleanupUnusedObjectsInCurrentContext periodically. The best place
+ // to make that call is within their own message pump.
+ if (!disableEagerCleanup
+#ifdef FEATURE_REMOTING
+ && !InSendMessage()
+#endif
+ )
+ {
+ _ASSERTE(g_pRCWCleanupList != NULL);
+ g_pRCWCleanupList->CleanupWrappersInCurrentCtxThread();
+ }
+}
+
+VOID RCW::MarkURTAggregated()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_Flags.m_fURTContained == 0);
+ }
+ CONTRACTL_END;
+
+ if (!m_Flags.m_fURTAggregated && m_Flags.m_fIsJupiterObject)
+ {
+ // Notify Jupiter that we are about to release IJupiterObject
+ RCWWalker::BeforeInterfaceRelease(this);
+
+ // If we mark this RCW as aggregated and we've done a QI for IJupiterObject,
+ // release it to account for the extra ref
+ // Note that this is a quick fix for PDC-2 and eventually we should replace
+ // this with a better fix
+ SafeRelease(GetJupiterObject());
+ }
+
+ m_Flags.m_fURTAggregated = 1;
+}
+
+RCW::MarshalingType RCW::GetMarshalingType(IUnknown* pUnk, MethodTable *pClassMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(CheckPointer(pClassMT));
+ }
+ CONTRACTL_END;
+
+ PTR_EEClass pClass = pClassMT->GetClass();
+
+ // Skip attributes on interfaces as any object could implement those interface
+ if (!pClass->IsInterface() && pClass->IsMarshalingTypeSet())
+ {
+ MarshalingType mType;
+ ( pClass ->IsMarshalingTypeFreeThreaded() ) ? mType = MarshalingType_FreeThreaded
+ : (pClass->IsMarshalingTypeInhibit() ? mType = MarshalingType_Inhibit
+ : mType = MarshalingType_Standard);
+ return mType;
+ }
+ // MarshalingBehavior is not set and hence we will have to find the behavior using the QI
+ else
+ {
+ // Check whether the COM object can be marshaled. Hence we query for INoMarshal
+ SafeComHolderPreemp<INoMarshal> pNoMarshal;
+ HRESULT hr = SafeQueryInterfacePreemp(pUnk, IID_INoMarshal, (IUnknown**)&pNoMarshal);
+ LogInteropQI(pUnk, IID_INoMarshal, hr, "RCW::GetMarshalingType: QI for INoMarshal");
+
+ if (SUCCEEDED(hr))
+ return MarshalingType_Inhibit;
+ if (IUnkEntry::IsComponentFreeThreaded(pUnk))
+ return MarshalingType_FreeThreaded;
+ }
+ return MarshalingType_Unknown;
+}
+
+void RCW::AddMemoryPressure(GCPressureSize pressureSize)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ int pressure = s_rGCPressureTable[pressureSize];
+
+ if (pressureSize >= GCPressureSize_WinRT_Base)
+ {
+ // use the new implementation for WinRT RCWs
+ GCInterface::NewAddMemoryPressure(pressure);
+ }
+ else
+ {
+ // use the old implementation for classic COM interop
+ GCInterface::AddMemoryPressure(pressure);
+ }
+
+ // Remember the pressure we set.
+ m_Flags.m_GCPressure = pressureSize;
+}
+
+
+void RCW::RemoveMemoryPressure()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION((GetThread()->m_StateNC & Thread::TSNC_UnsafeSkipEnterCooperative) == 0);
+ }
+ CONTRACTL_END;
+
+ if (GCPressureSize_None == m_Flags.m_GCPressure)
+ return;
+
+ int pressure = s_rGCPressureTable[m_Flags.m_GCPressure];
+
+ if (m_Flags.m_GCPressure >= GCPressureSize_WinRT_Base)
+ {
+ // use the new implementation for WinRT RCWs
+ GCInterface::NewRemoveMemoryPressure(pressure);
+ }
+ else
+ {
+ // use the old implementation for classic COM interop
+ GCInterface::RemoveMemoryPressure(pressure);
+ }
+
+ m_Flags.m_GCPressure = GCPressureSize_None;
+}
+
+
+//--------------------------------------------------------------------------------
+// Addref is called only from within the runtime, when we lookup a wrapper in our hash
+// table
+LONG RCW::AddRef(RCWCache* pWrapCache)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pWrapCache));
+ PRECONDITION(pWrapCache->LOCKHELD());
+ }
+ CONTRACTL_END;
+
+ LONG cbRef = ++m_cbRefCount;
+ return cbRef;
+}
+
+AppDomain* RCW::GetDomain()
+{
+ CONTRACT (AppDomain*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+ RETURN m_pRCWCache->GetDomain();
+}
+
+//--------------------------------------------------------------------------------
+// Used to facilitate the ReleaseComObject API.
+// Ensures that the RCW is not in use before attempting to release it.
+//
+INT32 RCW::ExternalRelease(OBJECTREF* pObjPROTECTED)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pObjPROTECTED != NULL);
+ PRECONDITION(*pObjPROTECTED != NULL);
+ }
+ CONTRACTL_END;
+
+ COMOBJECTREF* cref = (COMOBJECTREF*)pObjPROTECTED;
+
+ INT32 cbRef = -1;
+ BOOL fCleanupWrapper = FALSE;
+ RCW* pRCW = NULL;
+
+ // Lock
+ RCWCache* pCache = RCWCache::GetRCWCache();
+ _ASSERTE(pCache);
+
+ {
+ RCWCache::LockHolder lh(pCache);
+
+ // now to see if the wrapper is valid
+ // if there is another ReleaseComObject on this object
+ // of if an STA thread death decides to cleanup this wrapper
+ // then the object will be disconnected from the wrapper
+ pRCW = (*cref)->GetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW();
+
+ if (pRCW)
+ {
+ // check for invalid case
+ if ((LONG)pRCW->m_cbRefCount > 0)
+ {
+ cbRef = (INT32) (--(pRCW->m_cbRefCount));
+ if (cbRef == 0)
+ {
+ pCache->RemoveWrapper(pRCW);
+ fCleanupWrapper = TRUE;
+ }
+ }
+ }
+ }
+
+ // do cleanup after releasing the lock
+ if (fCleanupWrapper)
+ {
+#ifdef MDA_SUPPORTED
+ MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
+ if (mda)
+ {
+ BOOL fIsInUse = FALSE;
+
+ // Walk the thread tables, looking for this RCW in use.
+ {
+ // Take the threadstore lock
+ ThreadStoreLockHolder tslh;
+
+ Thread* pThread = NULL;
+
+ // walk each thread's table
+ while (NULL != (pThread = ThreadStore::GetThreadList(pThread)) )
+ {
+ if (pThread->RCWIsInUse(pRCW))
+ {
+ // found a match!
+ fIsInUse = TRUE;
+ break;
+ }
+ }
+ }
+
+ // If we found one, bail.
+ if (fIsInUse)
+ {
+ // Cannot decrement the counter if it's in use.
+ ++(pRCW->m_cbRefCount);
+ mda->ReportViolation();
+ }
+ }
+#endif // MDA_SUPPORTED
+
+ // Release all the data associated with the __ComObject.
+ ComObject::ReleaseAllData(pRCW->GetExposedObject());
+
+ pRCW->DecoupleFromObject();
+ pRCW->Cleanup();
+ }
+
+ return cbRef;
+}
+
+
+//--------------------------------------------------------------------------------
+// Used to facilitate the FinalReleaseComObject API.
+// Ensures that the RCW is not in use before attempting to release it.
+//
+void RCW::FinalExternalRelease(OBJECTREF* pObjPROTECTED)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pObjPROTECTED != NULL);
+ PRECONDITION(*pObjPROTECTED != NULL);
+ }
+ CONTRACTL_END;
+
+ COMOBJECTREF* cref = (COMOBJECTREF*)pObjPROTECTED;
+ BOOL fCleanupWrapper = FALSE;
+ RCW* pRCW = NULL;
+
+ // Lock
+ RCWCache* pCache = RCWCache::GetRCWCache();
+ _ASSERTE(pCache);
+
+ {
+ RCWCache::LockHolder lh(pCache);
+
+ pRCW = (*cref)->GetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW();
+
+ if (pRCW && pRCW->m_cbRefCount > 0)
+ {
+ pRCW->m_cbRefCount = 0;
+ pCache->RemoveWrapper(pRCW);
+ fCleanupWrapper = TRUE;
+ }
+ }
+
+ // do cleanup after releasing the lock
+ if (fCleanupWrapper)
+ {
+#ifdef MDA_SUPPORTED
+ MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup);
+ if (mda)
+ {
+ BOOL fIsInUse = FALSE;
+
+ // Walk the thread tables, looking for this RCW in use.
+ {
+ // Take the threadstore lock
+ ThreadStoreLockHolder tslh;
+
+ Thread* pThread = NULL;
+
+ // walk each thread's table
+ while (NULL != (pThread = ThreadStore::GetThreadList(pThread)) )
+ {
+ if (pThread->RCWIsInUse(pRCW))
+ {
+ // found a match!
+ fIsInUse = TRUE;
+ break;
+ }
+ }
+ }
+
+ // If we found one, bail.
+ if (fIsInUse)
+ {
+ // Cannot zero the counter if it's in use.
+ pRCW->m_cbRefCount = 1;
+ mda->ReportViolation();
+ }
+ }
+#endif // MDA_SUPPORTED
+
+ // Release all the data associated with the __ComObject.
+ ComObject::ReleaseAllData(pRCW->GetExposedObject());
+
+ pRCW->DecoupleFromObject();
+ pRCW->Cleanup();
+ }
+}
+
+
+//--------------------------------------------------------------------------------
+// schedule to free all interface pointers, called during GC to
+// do minimal work
+void RCW::MinorCleanup()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(GCHeap::IsGCInProgress() || ( (g_fEEShutDown & ShutDown_SyncBlock) && g_fProcessDetach ));
+ }
+ CONTRACTL_END;
+
+ // Log the wrapper minor cleanup.
+ LogRCWMinorCleanup(this);
+
+ // remove the wrapper from the cache, so that
+ // other threads won't find this invalid wrapper
+ // NOTE: we don't need to LOCK because we make sure
+ // the rest of the folks touch this hash table
+ // with thier GC mode pre-emptiveGCDisabled
+ RCWCache* pCache = m_pRCWCache;
+ _ASSERTE(pCache);
+
+ // On server build, multiple threads will be removing
+ // wrappers from wrapper cache,
+ pCache->RemoveWrapper(this);
+
+ if (IsJupiterObject() && !IsDetached())
+ RCWWalker::BeforeJupiterRCWDestroyed(this);
+
+ // Clear the SyncBlockIndex as the object is being GC'd and the index will become
+ // invalid as soon as the object is collected.
+ m_SyncBlockIndex = 0;
+}
+
+//--------------------------------------------------------------------------------
+// Cleanup free all interface pointers
+void RCW::Cleanup()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Log the destruction of the RCW.
+ LogRCWDestroy(this);
+
+ // If we can't switch to cooperative mode, then we need to skip the check to
+ // if the wrapper is still in the cache. Also, if we can't switch to coop mode,
+ // we're guaranteed to have already decoupled the RCW from its object.
+#ifdef _DEBUG
+ if (!(GetThread()->m_StateNC & Thread::TSNC_UnsafeSkipEnterCooperative))
+ {
+ GCX_COOP();
+
+ // make sure this wrapper is not in the hash table
+ RCWCache::LockHolder lh(m_pRCWCache);
+ _ASSERTE(m_pRCWCache->LookupWrapperUnsafe(m_pIdentity) != this);
+ }
+#endif
+
+ // Switch to preemptive GC mode before we release the interfaces.
+ {
+ GCX_PREEMP();
+
+ // Release the IUnkEntry and the InterfaceEntries.
+ ReleaseAllInterfacesCallBack(this);
+
+ // Remove the memory pressure caused by this RCW (if present)
+ // If we're in a shutdown situation, we can ignore the memory pressure.
+ if ((GetThread()->m_StateNC & Thread::TSNC_UnsafeSkipEnterCooperative) == 0 && !g_fForbidEnterEE)
+ RemoveMemoryPressure();
+ }
+
+ if (m_pAuxiliaryData != NULL)
+ {
+ delete m_pAuxiliaryData;
+ }
+
+#ifdef _DEBUG
+ m_cbRefCount = 0;
+ m_SyncBlockIndex = 0;
+#endif
+
+ // If there's no thread currently working with the RCW, this call will release helper fields on IUnkEntry
+ // and recycle the entire RCW structure, i.e. insert it in the standby list to be reused or free the memory.
+ // If a thread still keeps a ref-count on the RCW, it will release it when it's done. Keeping the structure
+ // and the helper fields alive reduces the chances of memory corruption in race scenarios.
+ DecrementUseCount();
+}
+
+
+//--------------------------------------------------------------------------------
+// Create a new wrapper for a different method table that represents the same
+// COM object as the original wrapper.
+void RCW::CreateDuplicateWrapper(MethodTable *pNewMT, RCWHolder* pNewRCW)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pNewMT));
+ PRECONDITION(pNewMT->IsComObjectType());
+ PRECONDITION(CheckPointer(pNewRCW));
+ //POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACTL_END;
+
+ NewRCWHolder pNewWrap;
+
+ // Validate that there exists a default constructor for the new wrapper class.
+ if (!pNewMT->HasDefaultConstructor())
+ COMPlusThrow(kArgumentException, IDS_EE_WRAPPER_MUST_HAVE_DEF_CONS);
+
+ // Allocate the wrapper COM object.
+ COMOBJECTREF NewWrapperObj = (COMOBJECTREF)ComObject::CreateComObjectRef(pNewMT);
+ GCPROTECT_BEGIN(NewWrapperObj)
+ {
+ SafeComHolder<IUnknown> pAutoUnk = NULL;
+
+ // Retrieve the RCWCache to use.
+ RCWCache* pCache = RCWCache::GetRCWCache();
+
+ // Create the new RCW associated with the COM object. We need
+ // to set the identity to some default value so we don't remove the original
+ // wrapper from the hash table when this wrapper goes away.
+ pAutoUnk = GetIUnknown();
+
+ DWORD flags = 0;
+ if (SupportsIInspectable())
+ flags |= CF_SupportsIInspectable;
+
+ // make sure we "pin" the syncblock before switching to preemptive mode
+ SyncBlock *pSB = NewWrapperObj->GetSyncBlock();
+ pSB->SetPrecious();
+ DWORD dwSyncBlockIndex = pSB->GetSyncBlockIndex();
+
+ pNewWrap = RCW::CreateRCW((IUnknown *)pAutoUnk, dwSyncBlockIndex, flags, pNewMT);
+
+ // Reset the Identity to be the RCW* as we don't want to create a duplicate entry
+ pNewWrap->m_pIdentity = (LPVOID)pNewWrap;
+
+ // Run the class constructor if it has not run yet.
+ pNewMT->CheckRunClassInitThrowing();
+
+ CallDefaultConstructor(ObjectToOBJECTREF(NewWrapperObj));
+
+ pNewRCW->InitNoCheck(NewWrapperObj);
+
+ // Insert the wrapper into the hashtable. The wrapper will be a duplicate however we
+ // we fix the identity to ensure there is no collison in the hash table & it is required
+ // since the hashtable is used on appdomain unload to determine what RCW's need to released.
+ {
+ RCWCache::LockHolder lh(pCache);
+ pCache->InsertWrapper(pNewRCW);
+ }
+ }
+ GCPROTECT_END();
+
+ pNewWrap.SuppressRelease();
+}
+
+//--------------------------------------------------------------------------------
+// Calling this is relatively slow since it can't take advantage of the cache
+// since there is no longer any way to go from an IID to a MethodTable.
+// If at all possible you should use the version that takes a MethodTable.
+// This usually means calling GetComIPFromObjectRef passing in a MethodTable
+// instead of an IID.
+IUnknown* RCW::GetComIPFromRCW(REFIID iid)
+{
+ CONTRACT(IUnknown *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ SafeComHolder<IUnknown> pRet = NULL;
+ HRESULT hr = S_OK;
+
+ hr = SafeQueryInterfaceRemoteAware(iid, (IUnknown**)&pRet);
+ if (hr != E_NOINTERFACE)
+ {
+ // We simply return NULL on E_NOINTERFACE which is much better for perf than throwing exceptions. Note
+ // that we can hit this code path often in aggregation scenarios where we forward QI's to the COM base class.
+ IfFailThrow(hr);
+ }
+ else
+ {
+ // Clear the return value in case we got E_NOINTERFACE but a non-NULL pUnk.
+ pRet.Clear();
+ }
+
+ pRet.SuppressRelease();
+ RETURN pRet;
+}
+
+//--------------------------------------------------------------------------------
+// check the local cache, out of line cache
+// if not found QI for the interface and store it
+IUnknown* RCW::GetComIPFromRCW(MethodTable* pMT)
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ if (pMT == NULL || pMT->IsObjectClass())
+ {
+ // give out the IUnknown or IDispatch
+ IUnknown *result = GetIUnknown();
+ _ASSERTE(result != NULL);
+ RETURN result;
+ }
+
+ //
+ // Collectible types do not support com interop
+ //
+ if (pMT->Collectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM"));
+ }
+
+ // returns an AddRef'ed IP
+ RETURN GetComIPForMethodTableFromCache(pMT);
+}
+
+
+//-----------------------------------------------------------------
+// Get the IUnknown pointer for the wrapper
+// make sure it is on the right thread
+IUnknown* RCW::GetIUnknown()
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ // Try to retrieve the IUnknown in the current context.
+ RETURN m_UnkEntry.GetIUnknownForCurrContext(false);
+}
+
+//-----------------------------------------------------------------
+// Get the IUnknown pointer for the wrapper, non-AddRef'ed.
+// Generally this will work only if we are on the right thread,
+// otherwise NULL will be returned.
+IUnknown* RCW::GetIUnknown_NoAddRef()
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ // Retrieve the IUnknown in the current context.
+ RETURN m_UnkEntry.GetIUnknownForCurrContext(true);
+}
+
+IUnknown *RCW::GetWellKnownInterface(REFIID riid)
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ IUnknown *pUnk = NULL;
+
+ // QI for riid.
+ HRESULT hr = SafeQueryInterfaceRemoteAware(riid, &pUnk);
+ if ( S_OK != hr )
+ {
+ // If anything goes wrong simply set pUnk to NULL to indicate that
+ // the wrapper does not support given riid.
+ pUnk = NULL;
+ }
+
+ // Return the IDispatch that is guaranteed to be valid on the current thread.
+ RETURN pUnk;
+}
+
+//-----------------------------------------------------------------
+// Get the IUnknown pointer for the wrapper
+// make sure it is on the right thread
+IDispatch *RCW::GetIDispatch()
+{
+#ifdef FEATURE_CORECLR
+ if (AppX::IsAppXProcess())
+ {
+ COMPlusThrow(kPlatformNotSupportedException, IDS_EE_ERROR_IDISPATCH);
+ }
+#endif // FEATURE_CORECLR
+
+ WRAPPER_NO_CONTRACT;
+ return (IDispatch *)GetWellKnownInterface(IID_IDispatch);
+}
+
+//-----------------------------------------------------------------
+// Get the IInspectable pointer for the wrapper
+IInspectable *RCW::GetIInspectable()
+{
+ WRAPPER_NO_CONTRACT;
+ return (IInspectable *)GetWellKnownInterface(IID_IInspectable);
+}
+
+//-----------------------------------------------
+// Free GC handle and remove SyncBlock entry
+void RCW::DecoupleFromObject()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ if (m_SyncBlockIndex != 0)
+ {
+ if (IsJupiterObject() && !IsDetached())
+ RCWWalker::BeforeJupiterRCWDestroyed(this);
+
+ // remove reference to wrapper from sync block
+ SyncBlock* pSB = GetSyncBlock();
+ _ASSERTE(pSB);
+
+ InteropSyncBlockInfo* pInteropInfo = pSB->GetInteropInfoNoCreate();
+ _ASSERTE(pInteropInfo);
+
+ pInteropInfo->SetRawRCW(NULL);
+
+ m_SyncBlockIndex = 0;
+ }
+}
+
+HRESULT RCW::SafeQueryInterfaceRemoteAware(REFIID iid, IUnknown** ppResUnk)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ SafeComHolder<IUnknown> pUnk(GetIUnknown_NoAddRef(), /*takeOwnership =*/ FALSE);
+ if (pUnk == NULL)
+ {
+ // if we are not on the right thread we get a proxy which we need to keep AddRef'ed
+ pUnk = GetIUnknown();
+ }
+
+ RCW_VTABLEPTR(this);
+
+ HRESULT hr = SafeQueryInterface(pUnk, iid, ppResUnk);
+ LogInteropQI(pUnk, iid, hr, "QI for interface in SafeQueryInterfaceRemoteAware");
+
+ if (hr == CO_E_OBJNOTCONNECTED || hr == RPC_E_INVALID_OBJECT || hr == RPC_E_INVALID_OBJREF || hr == CO_E_OBJNOTREG)
+ {
+ // set apartment state
+ GetThread()->SetApartment(Thread::AS_InMTA, FALSE);
+
+ // Release the stream of the IUnkEntry to force UnmarshalIUnknownForCurrContext
+ // to remarshal to the stream.
+ m_UnkEntry.ReleaseStream();
+
+ // Unmarshal again to the current context to get a valid proxy.
+ IUnknown *pTmpUnk = m_UnkEntry.UnmarshalIUnknownForCurrContext();
+
+ // Try to QI for the interface again.
+ hr = SafeQueryInterface(pTmpUnk, iid, ppResUnk);
+ LogInteropQI(pTmpUnk, iid, hr, "SafeQIRemoteAware - QI for Interface after lost");
+
+ // release our ref-count on pTmpUnk
+ int cbRef = SafeRelease(pTmpUnk);
+ LogInteropRelease(pTmpUnk, cbRef, "SafeQIRemoteAware - Release for Interface after lost");
+ }
+
+ return hr;
+}
+
+#endif //#ifndef CROSSGEN_COMPILE
+
+//-----------------------------------------------------------------
+// Returns a redirected collection interface corresponding to a given ICollection/ICollection<T> or NULL
+// if the given interface is not ICollection/ICollection<T>. This also works for IReadOnlyCollection<T>.
+// The BOOL parameters help resolve the ambiguity around ICollection<KeyValuePair<K, V>>.
+// static
+MethodTable *RCW::ResolveICollectionInterface(MethodTable *pItfMT, BOOL fPreferIDictionary, BOOL *pfChosenIDictionary)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pItfMT));
+ PRECONDITION(CheckPointer(pfChosenIDictionary, NULL_OK));
+ }
+ CONTRACTL_END;
+
+ if (pfChosenIDictionary != NULL)
+ *pfChosenIDictionary = FALSE;
+
+ // Casting/calling via ICollection<T> means QI/calling through IVector<T>, casting/calling via ICollection<KeyValuePair<K, V>> means
+ // QI/calling via IMap<K, V> OR IVector<IKeyValuePair<K, V>>. See which case it is.
+ if (pItfMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__ICOLLECTIONGENERIC)))
+ {
+ Instantiation inst = pItfMT->GetInstantiation();
+ TypeHandle arg = inst[0];
+
+ if (fPreferIDictionary)
+ {
+ if (!arg.IsTypeDesc() && arg.GetMethodTable()->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC)))
+ {
+ // ICollection<KeyValuePair<K, V>> -> IDictionary<K, V>
+ if (pfChosenIDictionary != NULL)
+ *pfChosenIDictionary = TRUE;
+
+ pItfMT = GetAppDomain()->GetRedirectedType(WinMDAdapter::RedirectedTypeIndex_System_Collections_Generic_IDictionary);
+ return TypeHandle(pItfMT).Instantiate(arg.GetInstantiation()).GetMethodTable();
+ }
+ }
+
+ // ICollection<T> -> IList<T>
+ pItfMT = GetAppDomain()->GetRedirectedType(WinMDAdapter::RedirectedTypeIndex_System_Collections_Generic_IList);
+ return TypeHandle(pItfMT).Instantiate(inst).GetMethodTable();
+ }
+
+ // Casting/calling via IReadOnlyCollection<T> means QI/calling through IVectorView<T>, casting/calling via IReadOnlyCollection<KeyValuePair<K, V>> means
+ // QI/calling via IMapView<K, V> OR IVectorView<IKeyValuePair<K, V>>. See which case it is.
+ if (pItfMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IREADONLYCOLLECTIONGENERIC)))
+ {
+ Instantiation inst = pItfMT->GetInstantiation();
+ TypeHandle arg = inst[0];
+
+ if (fPreferIDictionary)
+ {
+ if (!arg.IsTypeDesc() && arg.GetMethodTable()->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC)))
+ {
+ // IReadOnlyCollection<KeyValuePair<K, V>> -> IReadOnlyDictionary<K, V>
+ if (pfChosenIDictionary != NULL)
+ *pfChosenIDictionary = TRUE;
+
+ pItfMT = GetAppDomain()->GetRedirectedType(WinMDAdapter::RedirectedTypeIndex_System_Collections_Generic_IReadOnlyDictionary);
+ return TypeHandle(pItfMT).Instantiate(arg.GetInstantiation()).GetMethodTable();
+ }
+ }
+
+ // IReadOnlyCollection<T> -> IReadOnlyList<T>
+ pItfMT = GetAppDomain()->GetRedirectedType(WinMDAdapter::RedirectedTypeIndex_System_Collections_Generic_IReadOnlyList);
+ return TypeHandle(pItfMT).Instantiate(inst).GetMethodTable();
+ }
+
+ // Casting/calling via ICollection means QI/calling through IBindableVector (projected to IList).
+ if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTION))
+ {
+ return MscorlibBinder::GetExistingClass(CLASS__ILIST);
+ }
+
+ // none of the above
+ return NULL;
+}
+
+// Helper method to allow us to compare a MethodTable against a known method table
+// from mscorlib. If the mscorlib type isn't loaded, we don't load it because we
+// know that it can't be the MethodTable we're curious about.
+static bool MethodTableHasSameTypeDefAsMscorlibClass(MethodTable* pMT, BinderClassID classId)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodTable* pMT_MscorlibClass = MscorlibBinder::GetClassIfExist(classId);
+ if (pMT_MscorlibClass == NULL)
+ return false;
+
+ return (pMT->HasSameTypeDefAs(pMT_MscorlibClass) != FALSE);
+}
+
+// Returns an interface with variance corresponding to pMT or NULL if pMT does not support variance.
+// The reason why we don't just call HasVariance() is that we also deal with the WinRT interfaces
+// like IIterable<T> which do not (and cannot) have variance from .NET type system point of view.
+// static
+MethodTable *RCW::GetVariantMethodTable(MethodTable *pMT)
+{
+ CONTRACT(MethodTable *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT));
+ POSTCONDITION(RETVAL == NULL || RETVAL->HasVariance());
+ }
+ CONTRACT_END;
+
+ RCWPerTypeData *pData = pMT->GetRCWPerTypeData();
+ if (pData == NULL)
+ {
+ // if this type has no RCW data allocated, we know for sure that pMT has no
+ // corresponding MethodTable with variance
+ _ASSERTE(ComputeVariantMethodTable(pMT) == NULL);
+ RETURN NULL;
+ }
+
+ if ((pData->m_dwFlags & RCWPerTypeData::VariantTypeInited) == 0)
+ {
+ pData->m_pVariantMT = ComputeVariantMethodTable(pMT);
+ FastInterlockOr(&pData->m_dwFlags, RCWPerTypeData::VariantTypeInited);
+ }
+ RETURN pData->m_pVariantMT;
+}
+
+// static
+MethodTable *RCW::ComputeVariantMethodTable(MethodTable *pMT)
+{
+ CONTRACT(MethodTable *)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT));
+ POSTCONDITION(RETVAL == NULL || RETVAL->HasVariance());
+ }
+ CONTRACT_END;
+
+ if (!pMT->IsProjectedFromWinRT() && !WinRTTypeNameConverter::ResolveRedirectedType(pMT, NULL))
+ {
+ RETURN NULL;
+ }
+
+ if (pMT->HasVariance())
+ {
+ RETURN pMT;
+ }
+
+ // IIterable and IVectorView are not marked as covariant. Check them explicitly and
+ // return the corresponding IEnumerable / IReadOnlyList instantiation.
+ if (MethodTableHasSameTypeDefAsMscorlibClass(pMT, CLASS__IITERABLE))
+ {
+ RETURN TypeHandle(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)).
+ Instantiate(pMT->GetInstantiation()).AsMethodTable();
+ }
+ if (MethodTableHasSameTypeDefAsMscorlibClass(pMT, CLASS__IVECTORVIEW))
+ {
+ RETURN TypeHandle(MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC)).
+ Instantiate(pMT->GetInstantiation()).AsMethodTable();
+ }
+
+ // IIterator is not marked as covariant either. Return the covariant IEnumerator.
+ DefineFullyQualifiedNameForClassW();
+ if (MethodTableHasSameTypeDefAsMscorlibClass(pMT, CLASS__IITERATOR) ||
+ wcscmp(GetFullyQualifiedNameForClassW_WinRT(pMT), g_WinRTIIteratorClassNameW) == 0)
+ {
+ RETURN TypeHandle(MscorlibBinder::GetClass(CLASS__IENUMERATORGENERIC)).
+ Instantiate(pMT->GetInstantiation()).AsMethodTable();
+ }
+
+ RETURN NULL;
+}
+
+#ifndef CROSSGEN_COMPILE
+//-----------------------------------------------------------------
+// Determines the interface that should be QI'ed for when the RCW is cast to pItfMT.
+RCW::InterfaceRedirectionKind RCW::GetInterfaceForQI(MethodTable *pItfMT, MethodTable **pNewItfMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pItfMT));
+ PRECONDITION(CheckPointer(pNewItfMT));
+ }
+ CONTRACTL_END;
+
+ // We don't want to be redirecting interfaces if the underlying COM object is not a WinRT type
+ if (SupportsIInspectable() || pItfMT->IsWinRTRedirectedDelegate())
+ {
+ MethodTable *pNewItfMT1;
+ MethodTable *pNewItfMT2;
+ InterfaceRedirectionKind redirectionKind = GetInterfacesForQI(pItfMT, &pNewItfMT1, &pNewItfMT2);
+
+ //
+ // IEnumerable may need three QI attempts:
+ // 1. IEnumerable/IDispatch+DISPID_NEWENUM
+ // 2. IBindableIterable
+ // 3. IIterable<T> for a T
+ //
+ // Is this 3rd attempt on IEnumerable (non-generic)?
+ if (redirectionKind == InterfaceRedirection_Other_RetryOnFailure &&
+ pItfMT != *pNewItfMT && *pNewItfMT != NULL &&
+ pItfMT == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE))
+ {
+ // Yes - we are at 3rd attempt;
+ // QI for IEnumerable/IDispatch+DISPID_NEWENUM and for IBindableIterable failed
+ // and we are about to see if we know of an IIterable<T> to use.
+
+ MethodDesc *pMD = GetGetEnumeratorMethod();
+ if (pMD != NULL)
+ {
+ // we have already determined what casting to IEnumerable means for this RCW
+ TypeHandle th = TypeHandle(MscorlibBinder::GetClass(CLASS__IITERABLE));
+ *pNewItfMT = th.Instantiate(pMD->GetClassInstantiation()).GetMethodTable();
+ return InterfaceRedirection_IEnumerable;
+ }
+
+ // The last attempt failed, this is an error.
+ return InterfaceRedirection_UnresolvedIEnumerable;
+ }
+
+ if ((redirectionKind != InterfaceRedirection_IEnumerable_RetryOnFailure &&
+ redirectionKind != InterfaceRedirection_Other_RetryOnFailure) || *pNewItfMT == NULL)
+ {
+ // First attempt - use pNewItfMT1
+ *pNewItfMT = pNewItfMT1;
+ return redirectionKind;
+ }
+ else
+ {
+ // Second attempt - use pNewItfMT2
+ *pNewItfMT = pNewItfMT2;
+
+ if (redirectionKind == InterfaceRedirection_IEnumerable_RetryOnFailure)
+ return InterfaceRedirection_IEnumerable;
+
+ // Get ready for the 3rd attmpt if 2nd attempt fails
+ // This only happens for non-generic IEnumerable
+ if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE))
+ return InterfaceRedirection_IEnumerable_RetryOnFailure;
+
+ return InterfaceRedirection_IEnumerable;
+ }
+ }
+
+ *pNewItfMT = pItfMT;
+ return InterfaceRedirection_None;
+}
+#endif // !CROSSGEN_COMPILE
+
+// static
+RCW::InterfaceRedirectionKind RCW::GetInterfacesForQI(MethodTable *pItfMT, MethodTable **ppNewItfMT1, MethodTable **ppNewItfMT2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pItfMT));
+ PRECONDITION(CheckPointer(ppNewItfMT1));
+ PRECONDITION(CheckPointer(ppNewItfMT2));
+ }
+ CONTRACTL_END;
+
+ RCWPerTypeData *pData = pItfMT->GetRCWPerTypeData();
+ if (pData == NULL)
+ {
+#ifdef _DEBUG
+ // verify that if the per-type data is NULL, the type has indeed no redirection
+ MethodTable *pNewItfMT1;
+ MethodTable *pNewItfMT2;
+ _ASSERTE(ComputeInterfacesForQI(pItfMT, &pNewItfMT1, &pNewItfMT2) == InterfaceRedirection_None);
+#endif // _DEBUG
+
+ *ppNewItfMT1 = pItfMT;
+ *ppNewItfMT2 = NULL;
+ return InterfaceRedirection_None;
+ }
+ else
+ {
+ if ((pData->m_dwFlags & RCWPerTypeData::RedirectionInfoInited) == 0)
+ {
+ pData->m_RedirectionKind = ComputeInterfacesForQI(pItfMT, &pData->m_pMTForQI1, &pData->m_pMTForQI2);
+ FastInterlockOr(&pData->m_dwFlags, RCWPerTypeData::RedirectionInfoInited);
+ }
+
+ *ppNewItfMT1 = pData->m_pMTForQI1;
+ *ppNewItfMT2 = pData->m_pMTForQI2;
+ return pData->m_RedirectionKind;
+ }
+}
+
+// static
+RCW::InterfaceRedirectionKind RCW::ComputeInterfacesForQI(MethodTable *pItfMT, MethodTable **ppNewItfMT1, MethodTable **ppNewItfMT2)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pItfMT));
+ PRECONDITION(CheckPointer(ppNewItfMT1));
+ PRECONDITION(CheckPointer(ppNewItfMT2));
+ }
+ CONTRACTL_END;
+
+ if (pItfMT->IsProjectedFromWinRT())
+ {
+ // If we're casting to IIterable<T> directly, then while we do want to QI IIterable<T>, also
+ // make a note that it is redirected from IEnumerable<T>
+ if (pItfMT->HasInstantiation() && pItfMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__IITERABLE)))
+ {
+ *ppNewItfMT1 = pItfMT;
+ return InterfaceRedirection_IEnumerable;
+ }
+ }
+ else
+ {
+ WinMDAdapter::RedirectedTypeIndex redirectedInterfaceIndex;
+ RCW::InterfaceRedirectionKind redirectionKind = InterfaceRedirection_None;
+
+ BOOL fChosenIDictionary;
+ MethodTable *pResolvedItfMT = ResolveICollectionInterface(pItfMT, TRUE, &fChosenIDictionary);
+ if (pResolvedItfMT == NULL)
+ {
+ pResolvedItfMT = pItfMT;
+ // Let ResolveRedirectedType convert IDictionary/IList to the corresponding WinRT type as usual
+ }
+
+ if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pResolvedItfMT, &redirectedInterfaceIndex))
+ {
+ TypeHandle th = WinRTInterfaceRedirector::GetWinRTTypeForRedirectedInterfaceIndex(redirectedInterfaceIndex);
+
+ if (th.HasInstantiation())
+ {
+ *ppNewItfMT1 = th.Instantiate(pResolvedItfMT->GetInstantiation()).GetMethodTable();
+ if (pItfMT->CanCastToInterface(MscorlibBinder::GetClass(CLASS__IENUMERABLE)))
+ {
+ redirectionKind = InterfaceRedirection_IEnumerable;
+ }
+ else
+ {
+ _ASSERTE(!fChosenIDictionary);
+ redirectionKind = InterfaceRedirection_Other;
+ }
+ }
+ else
+ {
+ // pItfMT is a non-generic redirected interface - for compat reasons do QI for the interface first,
+ // and if it fails, use redirection
+ *ppNewItfMT1 = pItfMT;
+ *ppNewItfMT2 = th.GetMethodTable();
+ redirectionKind = InterfaceRedirection_Other_RetryOnFailure;
+ }
+ }
+
+ if (fChosenIDictionary)
+ {
+ // pItfMT is the ambiguous ICollection<KeyValuePair<K, V>> and *ppNewItfMT1 at this point is the
+ // corresponding IMap<K, V>, now we are going to assign IVector<IKeyValuePair<K, V>> to *ppNewItfMT2
+ pResolvedItfMT = ResolveICollectionInterface(pItfMT, FALSE, NULL);
+
+ VERIFY(WinRTInterfaceRedirector::ResolveRedirectedInterface(pResolvedItfMT, &redirectedInterfaceIndex));
+ TypeHandle th = WinRTInterfaceRedirector::GetWinRTTypeForRedirectedInterfaceIndex(redirectedInterfaceIndex);
+
+ *ppNewItfMT2 = th.Instantiate(pItfMT->GetInstantiation()).GetMethodTable();
+ redirectionKind = InterfaceRedirection_IEnumerable_RetryOnFailure;
+ }
+
+ if (redirectionKind != InterfaceRedirection_None)
+ {
+ return redirectionKind;
+ }
+
+ if (WinRTDelegateRedirector::ResolveRedirectedDelegate(pItfMT, &redirectedInterfaceIndex))
+ {
+ TypeHandle th = TypeHandle(WinRTDelegateRedirector::GetWinRTTypeForRedirectedDelegateIndex(redirectedInterfaceIndex));
+
+ if (pItfMT->HasInstantiation())
+ {
+ th = th.Instantiate(pItfMT->GetInstantiation());
+ }
+
+ *ppNewItfMT1 = th.GetMethodTable();
+ return InterfaceRedirection_Other;
+ }
+ }
+
+ *ppNewItfMT1 = pItfMT;
+ return InterfaceRedirection_None;
+}
+
+#ifndef CROSSGEN_COMPILE
+//-----------------------------------------------------------------
+// Returns a known working IEnumerable<T>::GetEnumerator to be used in lieu of the non-generic
+// IEnumerable::GetEnumerator.
+MethodDesc *RCW::GetGetEnumeratorMethod()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pAuxiliaryData == NULL || m_pAuxiliaryData->m_pGetEnumeratorMethod == NULL)
+ {
+ MethodTable *pClsMT;
+ {
+ GCX_COOP();
+ pClsMT = GetExposedObject()->GetTrueMethodTable();
+ }
+
+ SetGetEnumeratorMethod(pClsMT);
+ }
+
+ return (m_pAuxiliaryData == NULL ? NULL : m_pAuxiliaryData->m_pGetEnumeratorMethod);
+}
+
+//-----------------------------------------------------------------
+// Sets the first "known" GetEnumerator method on the RCW if not set already.
+void RCW::SetGetEnumeratorMethod(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pAuxiliaryData != NULL && m_pAuxiliaryData->m_pGetEnumeratorMethod != NULL)
+ return;
+
+ // Retrieve cached GetEnumerator method or compute the right one for this pMT
+ MethodDesc *pMD = GetOrComputeGetEnumeratorMethodForType(pMT);
+
+ if (pMD != NULL)
+ {
+ // We successfully got a GetEnumerator method - cache it in the RCW
+ // We can have multiple casts going on concurrently, make sure that
+ // the result of this method is stable.
+ InterlockedCompareExchangeT(&GetOrCreateAuxiliaryData()->m_pGetEnumeratorMethod, pMD, NULL);
+ }
+}
+
+// Retrieve cached GetEnumerator method or compute the right one for a specific type
+MethodDesc *RCW::GetOrComputeGetEnumeratorMethodForType(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodDesc *pMD = NULL;
+
+ RCWPerTypeData *pData = pMT->GetRCWPerTypeData();
+ if (pData != NULL)
+ {
+ if ((pData->m_dwFlags & RCWPerTypeData::GetEnumeratorInited) == 0)
+ {
+ pData->m_pGetEnumeratorMethod = ComputeGetEnumeratorMethodForType(pMT);
+ FastInterlockOr(&pData->m_dwFlags, RCWPerTypeData::GetEnumeratorInited);
+ }
+
+ pMD = pData->m_pGetEnumeratorMethod;
+ }
+ else
+ {
+ pMD = ComputeGetEnumeratorMethodForType(pMT);
+ }
+
+ return pMD;
+}
+
+// Compute the first GetEnumerator for a specific type
+MethodDesc *RCW::ComputeGetEnumeratorMethodForType(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ MethodDesc *pMD = ComputeGetEnumeratorMethodForTypeInternal(pMT);
+
+ // Walk the interface impl and use these interfaces to compute
+ MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
+ while (pMD == NULL && it.Next())
+ {
+ pMT = it.GetInterface();
+ pMD = GetOrComputeGetEnumeratorMethodForType(pMT);
+ }
+
+ return pMD;
+}
+
+// Get the GetEnumerator method for IEnumerable<T> or IIterable<T>
+MethodDesc *RCW::ComputeGetEnumeratorMethodForTypeInternal(MethodTable *pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (!pMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)))
+ {
+ // If we have an IIterable<T>, we want to get the enumerator for the equivalent
+ // instantiation of IEnumerable<T>
+ if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__IITERABLE)))
+ {
+ TypeHandle thEnumerable = TypeHandle(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC));
+ pMT = thEnumerable.Instantiate(pMT->GetInstantiation()).GetMethodTable();
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ MethodDesc *pMD = pMT->GetMethodDescForSlot(0);
+ _ASSERTE(strcmp(pMD->GetName(), "GetEnumerator") == 0);
+
+ if (pMD->IsSharedByGenericInstantiations())
+ {
+ pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pMD,
+ pMT,
+ FALSE, // forceBoxedEntryPoint
+ Instantiation(), // methodInst
+ FALSE, // allowInstParam
+ TRUE); // forceRemotableMethod
+ }
+
+ return pMD;
+}
+
+
+//-----------------------------------------------------------------
+// Notifies the RCW of an interface that is known to be supported by the COM object.
+// pItfMT is the type which the object directly supports, originalInst is the instantiation
+// that we asked for. I.e. we know that the object supports pItfMT<originalInst> via
+// variance because the QI for IID(pItfMT) succeeded.
+void RCW::SetSupportedInterface(MethodTable *pItfMT, Instantiation originalInst)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ BOOL fIsEnumerable = (pItfMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)) ||
+ pItfMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__IITERABLE)));
+
+ if (fIsEnumerable || pItfMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC)) ||
+ pItfMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__IVECTORVIEW)))
+ {
+ WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType;
+ if (!originalInst.IsEmpty())
+ {
+ // use the original instantiation if available
+ baseType = WinRTInterfaceRedirector::GetStructureBaseType(originalInst);
+ }
+ else
+ {
+ baseType = WinRTInterfaceRedirector::GetStructureBaseType(pItfMT->GetInstantiation());
+ }
+
+ switch (baseType)
+ {
+ case WinRTInterfaceRedirector::BaseType_Object:
+ {
+ OBJECTHANDLE *pohHandleField = fIsEnumerable ?
+ &GetOrCreateAuxiliaryData()->m_ohObjectVariantCallTarget_IEnumerable :
+ &GetOrCreateAuxiliaryData()->m_ohObjectVariantCallTarget_IReadOnlyList;
+
+ if (*pohHandleField != NULL)
+ {
+ // we've already established the behavior so we can skip the code below
+ break;
+ }
+
+ if (!originalInst.IsEmpty())
+ {
+ MethodTable *pInstArgMT = pItfMT->GetInstantiation()[0].GetMethodTable();
+
+ if (pInstArgMT == g_pStringClass)
+ {
+ // We are casting the RCW to IEnumerable<string> or IReadOnlyList<string> - we special-case this common case
+ // so we don't have to create the delegate.
+ FastInterlockCompareExchangePointer<OBJECTHANDLE>(pohHandleField, VARIANCE_STUB_TARGET_USE_STRING, NULL);
+ }
+ else if (pInstArgMT == g_pExceptionClass ||
+ pInstArgMT == MscorlibBinder::GetClass(CLASS__TYPE) ||
+ pInstArgMT->IsArray() ||
+ pInstArgMT->IsDelegate())
+ {
+ // We are casting the RCW to IEnumerable<T> or IReadOnlyList<T> where T is Type/Exception/an array/a delegate
+ // i.e. an unbounded set of types. We'll create a delegate pointing to the right stub and cache it on the RCW
+ // so we can handle the calls via GetEnumerator/Indexer_Get as fast as possible.
+
+ MethodDesc *pTargetMD = MscorlibBinder::GetMethod(fIsEnumerable ?
+ METHOD__ITERABLE_TO_ENUMERABLE_ADAPTER__GET_ENUMERATOR_STUB :
+ METHOD__IVECTORVIEW_TO_IREADONLYLIST_ADAPTER__INDEXER_GET);
+
+ pTargetMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pTargetMD,
+ pTargetMD->GetMethodTable(),
+ FALSE, // forceBoxedEntryPoint
+ pItfMT->GetInstantiation(), // methodInst
+ FALSE, // allowInstParam
+ TRUE); // forceRemotableMethod
+
+ MethodTable *pMT = MscorlibBinder::GetClass(fIsEnumerable ?
+ CLASS__GET_ENUMERATOR_DELEGATE :
+ CLASS__INDEXER_GET_DELEGATE);
+
+ pMT = TypeHandle(pMT).Instantiate(pItfMT->GetInstantiation()).AsMethodTable();
+
+ GCX_COOP();
+
+ DELEGATEREF pDelObj = NULL;
+ GCPROTECT_BEGIN(pDelObj);
+
+ pDelObj = (DELEGATEREF)AllocateObject(pMT);
+ pDelObj->SetTarget(GetExposedObject());
+ pDelObj->SetMethodPtr(pTargetMD->GetMultiCallableAddrOfCode());
+
+ OBJECTHANDLEHolder oh = GetAppDomain()->CreateHandle(pDelObj);
+ if (FastInterlockCompareExchangePointer<OBJECTHANDLE>(pohHandleField, oh, NULL) == NULL)
+ {
+ oh.SuppressRelease();
+ }
+
+ GCPROTECT_END();
+ }
+ }
+
+ // the default is "use T", i.e. handle the call as normal
+ if (*pohHandleField == NULL)
+ {
+ FastInterlockCompareExchangePointer<OBJECTHANDLE>(pohHandleField, VARIANCE_STUB_TARGET_USE_T, NULL);
+ }
+ break;
+ }
+
+ case WinRTInterfaceRedirector::BaseType_IEnumerable:
+ case WinRTInterfaceRedirector::BaseType_IEnumerableOfChar:
+ {
+ // The only WinRT-legal type that implements IEnumerable<IEnumerable> or IEnumerable<IEnumerable<char>> or
+ // IReadOnlyList<IEnumerable> or IReadOnlyList<IEnumerable<char>> AND is not an IInspectable on the WinRT
+ // side is string. We'll use a couple of flags here since the number of options is small.
+
+ InterfaceVarianceBehavior varianceBehavior = (fIsEnumerable ? IEnumerableSupported : IReadOnlyListSupported);
+
+ if (!originalInst.IsEmpty())
+ {
+ MethodTable *pInstArgMT = pItfMT->GetInstantiation()[0].GetMethodTable();
+ if (pInstArgMT == g_pStringClass)
+ {
+ varianceBehavior = (InterfaceVarianceBehavior)
+ (varianceBehavior | (fIsEnumerable ? IEnumerableSupportedViaStringInstantiation : IReadOnlyListSupportedViaStringInstantiation));
+ }
+
+ RCWAuxiliaryData::RCWAuxFlags newAuxFlags = { 0 };
+
+ if (baseType == WinRTInterfaceRedirector::BaseType_IEnumerable)
+ {
+ newAuxFlags.m_InterfaceVarianceBehavior_OfIEnumerable = varianceBehavior;
+ }
+ else
+ {
+ _ASSERTE(baseType == WinRTInterfaceRedirector::BaseType_IEnumerableOfChar);
+ newAuxFlags.m_InterfaceVarianceBehavior_OfIEnumerableOfChar = varianceBehavior;
+ }
+
+ RCWAuxiliaryData *pAuxData = GetOrCreateAuxiliaryData();
+ FastInterlockOr(&pAuxData->m_AuxFlags.m_dwFlags, newAuxFlags.m_dwFlags);
+ }
+ }
+ }
+ }
+}
+
+// Performs QI for the given interface, optionally instantiating it with the given generic args.
+HRESULT RCW::CallQueryInterface(MethodTable *pMT, Instantiation inst, IID *piid, IUnknown **ppUnk)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr;
+ MethodTable *pCastToMT = pMT;
+ MethodTable *pCOMItfMT = NULL;
+ InterfaceRedirectionKind redirection = InterfaceRedirection_None;
+
+ if (!inst.IsEmpty())
+ {
+ pMT = TypeHandle(pMT).Instantiate(inst).GetMethodTable();
+ }
+
+ do
+ {
+ redirection = GetInterfaceForQI(pMT, &pCOMItfMT);
+
+ if (redirection == InterfaceRedirection_UnresolvedIEnumerable)
+ {
+ // We just say no in this case. If we threw an exception, we would make the "as" operator
+ // throwing which would be ECMA violation.
+ return E_NOINTERFACE;
+ }
+
+ // To avoid throwing BadImageFormatException later in ComputeGuidForGenericTypes we must fail early if this is a generic type and not a legal WinRT type.
+ if (pCOMItfMT->SupportsGenericInterop(TypeHandle::Interop_NativeToManaged, MethodTable::modeProjected) && !pCOMItfMT->IsLegalNonArrayWinRTType())
+ {
+ return E_NOINTERFACE;
+ }
+ else
+ {
+ // Retrieve the IID of the interface.
+ pCOMItfMT->GetGuid(piid, TRUE);
+ }
+
+ // QI for the interface.
+ hr = SafeQueryInterfaceRemoteAware(*piid, ppUnk);
+ }
+ while (hr == E_NOINTERFACE && // Terminate the loop if the QI failed for some other reasons (for example, context transition failure)
+ (redirection == InterfaceRedirection_IEnumerable_RetryOnFailure || redirection == InterfaceRedirection_Other_RetryOnFailure));
+
+ if (SUCCEEDED(hr))
+ {
+ if (redirection == InterfaceRedirection_IEnumerable)
+ {
+ // remember the first IEnumerable<T> interface we successfully QI'ed for
+ SetGetEnumeratorMethod(pMT);
+ }
+
+ // remember successful QI's for interesting interfaces passing the original instantiation so we know that variance was involved
+ SetSupportedInterface(pCOMItfMT, pCastToMT->GetInstantiation());
+ }
+
+ return hr;
+}
+
+// Performs QI for interfaces that are castable to pMT using co-/contra-variance.
+HRESULT RCW::CallQueryInterfaceUsingVariance(MethodTable *pMT, IUnknown **ppUnk)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_NOINTERFACE;
+
+ // see if pMT is an interface with variance, if not we return NULL
+ MethodTable *pVariantMT = GetVariantMethodTable(pMT);
+
+ if (pVariantMT != NULL)
+ {
+ MethodTable *pItfMT = NULL;
+ IID variantIid;
+
+ MethodTable *pClassMT;
+
+ {
+ GCX_COOP();
+ pClassMT = GetExposedObject()->GetTrueMethodTable();
+ }
+
+ // Try interfaces that we know about from metadata
+ if (pClassMT != NULL && pClassMT != g_pBaseCOMObject)
+ {
+ MethodTable::InterfaceMapIterator it = pClassMT->IterateInterfaceMap();
+ while (FAILED(hr) && it.Next())
+ {
+ pItfMT = GetVariantMethodTable(it.GetInterface());
+ if (pItfMT != NULL && pItfMT->CanCastByVarianceToInterfaceOrDelegate(pVariantMT, NULL))
+ {
+ hr = CallQueryInterface(pMT, pItfMT->GetInstantiation(), &variantIid, ppUnk);
+ }
+ }
+ }
+
+ // Then try the interface pointer cache
+ CachedInterfaceEntryIterator it = IterateCachedInterfacePointers();
+ while (FAILED(hr) && it.Next())
+ {
+ MethodTable *pCachedItfMT = (MethodTable *)it.GetEntry()->m_pMT;
+ if (pCachedItfMT != NULL)
+ {
+ pItfMT = GetVariantMethodTable(pCachedItfMT);
+ if (pItfMT != NULL && pItfMT->CanCastByVarianceToInterfaceOrDelegate(pVariantMT, NULL))
+ {
+ hr = CallQueryInterface(pMT, pItfMT->GetInstantiation(), &variantIid, ppUnk);
+ }
+
+ // The cached interface may not support variance, but one of its base interfaces can
+ if (FAILED(hr))
+ {
+ MethodTable::InterfaceMapIterator it = pCachedItfMT->IterateInterfaceMap();
+ while (FAILED(hr) && it.Next())
+ {
+ pItfMT = GetVariantMethodTable(it.GetInterface());
+ if (pItfMT != NULL && pItfMT->CanCastByVarianceToInterfaceOrDelegate(pVariantMT, NULL))
+ {
+ hr = CallQueryInterface(pMT, pItfMT->GetInstantiation(), &variantIid, ppUnk);
+ }
+ }
+ }
+ }
+ }
+
+ // If we still haven't succeeded, enumerate the variant interface cache
+ if (FAILED(hr) && m_pAuxiliaryData != NULL && m_pAuxiliaryData->m_prVariantInterfaces != NULL)
+ {
+ // make a copy of the cache under the lock
+ ArrayList rVariantInterfacesCopy;
+ {
+ CrstHolder ch(&m_pAuxiliaryData->m_VarianceCacheCrst);
+
+ ArrayList::Iterator it = m_pAuxiliaryData->m_prVariantInterfaces->Iterate();
+ while (it.Next())
+ {
+ rVariantInterfacesCopy.Append(it.GetElement());
+ }
+ }
+
+ ArrayList::Iterator it = rVariantInterfacesCopy.Iterate();
+ while (FAILED(hr) && it.Next())
+ {
+ pItfMT = (MethodTable *)it.GetElement();
+ if (pItfMT->CanCastByVarianceToInterfaceOrDelegate(pVariantMT, NULL))
+ {
+ hr = CallQueryInterface(pMT, pItfMT->GetInstantiation(), &variantIid, ppUnk);
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+//-----------------------------------------------------------------
+// Retrieve correct COM IP for the method table
+// for the current apartment, use the cache and update the cache on miss
+IUnknown* RCW::GetComIPForMethodTableFromCache(MethodTable* pMT)
+{
+ CONTRACT(IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pMT));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ ULONG cbRef;
+ IUnknown* pUnk = 0;
+ IID iid;
+ HRESULT hr;
+ int i;
+
+ LPVOID pCtxCookie = GetCurrentCtxCookie();
+ _ASSERTE(pCtxCookie != NULL);
+
+ RCW_VTABLEPTR(this);
+
+ // Check whether we can satisfy this request from our cache.
+ if (pCtxCookie == GetWrapperCtxCookie() || IsFreeThreaded())
+ {
+ for (i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++)
+ {
+ if (m_aInterfaceEntries[i].m_pMT == (IE_METHODTABLE_PTR)pMT)
+ {
+ _ASSERTE(!m_aInterfaceEntries[i].IsFree());
+
+ pUnk = m_aInterfaceEntries[i].m_pUnknown;
+ _ASSERTE(pUnk != NULL);
+
+ cbRef = SafeAddRef(pUnk);
+ LogInteropAddRef(pUnk, cbRef, "RCW::GetComIPForMethodTableFromCache: Addref because returning pUnk fetched from InterfaceEntry cache");
+ RETURN pUnk;
+ }
+ }
+ }
+
+ if (m_pAuxiliaryData != NULL)
+ {
+ pUnk = m_pAuxiliaryData->FindInterfacePointer(pMT, (IsFreeThreaded() ? NULL : pCtxCookie));
+ if (pUnk != NULL)
+ {
+ cbRef = SafeAddRef(pUnk);
+ LogInteropAddRef(pUnk, cbRef, "RCW::GetComIPForMethodTableFromCache: Addref because returning pUnk fetched from auxiliary interface pointer cache");
+ RETURN pUnk;
+ }
+ }
+
+ // We're going to be making some COM calls, better initialize COM.
+ EnsureComStarted();
+
+ // First, try to QI for the interface that we were asked for
+ hr = CallQueryInterface(pMT, Instantiation(), &iid, &pUnk);
+
+ // If that failed and the interface has variance, we'll try to find another instantiation
+ bool fVarianceUsed = false;
+ if (FAILED(hr))
+ {
+ hr = CallQueryInterfaceUsingVariance(pMT, &pUnk);
+ if (pUnk != NULL)
+ {
+ fVarianceUsed = true;
+ }
+ }
+
+#ifdef MDA_SUPPORTED
+ if (FAILED(hr))
+ {
+ MDA_TRIGGER_ASSISTANT(FailedQI, ReportAdditionalInfo(hr, this, iid, pMT));
+ }
+#endif
+
+ if (pUnk == NULL)
+ RETURN NULL;
+
+ // See if we should cache the result in the fast inline cache. This cache can only store interface pointers
+ // returned from QI's in the same context where we created the RCW.
+ bool fAllowCache = true;
+ bool fAllowOutOfContextCache = true;
+
+ if (!pMT->IsProjectedFromWinRT() && !pMT->IsWinRTRedirectedInterface(TypeHandle::Interop_ManagedToNative) && !pMT->IsWinRTRedirectedDelegate())
+ {
+ AppDomain *pAppDomain = GetAppDomain();
+ if (pAppDomain && pAppDomain->GetDisableInterfaceCache())
+ {
+ // Caching is disabled in this AD
+ fAllowCache = false;
+ }
+ else
+ {
+ // This is not a WinRT interface and we could in theory use the out-of-context auxiliary cache,
+ // at worst we would just do
+ // fAllowOutOfContextCache = !IsURTAggregated()
+ // however such a change has some breaking potential (COM proxies would live much longer) and is
+ // considered to risky for an in-place release.
+
+ fAllowOutOfContextCache = false;
+ }
+ }
+
+ // try to cache the interface pointer in the inline cache
+ bool fInterfaceCached = false;
+ if (fAllowCache)
+ {
+ if (GetWrapperCtxCookie() == pCtxCookie || IsFreeThreaded())
+ {
+ for (i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++)
+ {
+ if (m_aInterfaceEntries[i].IsFree() && m_aInterfaceEntries[i].Init(pMT, pUnk))
+ {
+ // If the component is not aggregated then we need to ref-count
+ if (!IsURTAggregated())
+ {
+ // Get an extra addref to hold this reference alive in our cache
+ cbRef = SafeAddRef(pUnk);
+ LogInteropAddRef(pUnk, cbRef, "RCW::GetComIPForMethodTableFromCache: Addref because storing pUnk in InterfaceEntry cache");
+
+ // Notify Jupiter we have done a AddRef
+ // We should do this *after* we made a AddRef because we should never
+ // be in a state where report refs > actual refs
+ RCWWalker::AfterInterfaceAddRef(this);
+ }
+
+ fInterfaceCached = true;
+ break;
+ }
+ }
+ }
+
+ if (!fInterfaceCached && fAllowOutOfContextCache)
+ {
+ // We couldn't insert into the inline cache, either because it didn't fit, or because
+ // we are in a wrong COM context. We'll use the RCWAuxiliaryData structure.
+ GetOrCreateAuxiliaryData()->CacheInterfacePointer(pMT, pUnk, (IsFreeThreaded() ? NULL : pCtxCookie));
+
+ // If the component is not aggregated then we need to ref-count
+ if (!IsURTAggregated())
+ {
+ // Get an extra addref to hold this reference alive in our cache
+ cbRef = SafeAddRef(pUnk);
+ LogInteropAddRef(pUnk, cbRef, "RCW::GetComIPForMethodTableFromCache: Addref because storing pUnk in the auxiliary interface pointer cache");
+
+ // Notify Jupiter we have done a AddRef
+ // We should do this *after* we made a AddRef because we should never
+ // be in a state where report refs > actual refs
+ RCWWalker::AfterInterfaceAddRef(this);
+ }
+
+ fInterfaceCached = true;
+ }
+ }
+
+ // Make sure we cache successful QI's for variant interfaces. This is so we can cast an RCW for
+ // example to IEnumerable<object> if we previously successfully QI'ed for IEnumerable<IFoo>. We
+ // only need to do this if we actually didn't use variance for this QI.
+ if (!fVarianceUsed)
+ {
+ MethodTable *pVariantMT = GetVariantMethodTable(pMT);
+
+ // We can also skip the potentially expensive CacheVariantInterface call if we already inserted
+ // the variant interface into our interface pointer cache.
+ if (pVariantMT != NULL && (!fInterfaceCached || pVariantMT != pMT))
+ {
+ GetOrCreateAuxiliaryData()->CacheVariantInterface(pVariantMT);
+ }
+ }
+
+ RETURN pUnk;
+}
+
+//----------------------------------------------------------
+// Determine if the COM object supports IProvideClassInfo.
+BOOL RCW::SupportsIProvideClassInfo()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ BOOL bSupportsIProvideClassInfo = FALSE;
+ SafeComHolder<IUnknown> pProvClassInfo = NULL;
+
+ // QI for IProvideClassInfo on the COM object.
+ HRESULT hr = SafeQueryInterfaceRemoteAware(IID_IProvideClassInfo, &pProvClassInfo);
+
+ // Check to see if the QI for IProvideClassInfo succeeded.
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(pProvClassInfo);
+ bSupportsIProvideClassInfo = TRUE;
+ }
+
+ return bSupportsIProvideClassInfo;
+}
+
+BOOL RCW::AllowEagerSTACleanup()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // We only consider STA threads. MTA threads should have been dealt
+ // with before calling this.
+ _ASSERTE(GetSTAThread() != NULL);
+
+ // If the client has called CoEEShutdownCOM, then we should always try to
+ // clean up RCWs, even if they have previously opted out by calling
+ // DisableComObjectEagerCleanup. There's no way for clients to re-enable
+ // eager cleanup so, if we don't clean up now, they will be leaked. After
+ // shutting down COM, clients would not expect any RCWs to be left over.
+ if( g_fShutDownCOM )
+ {
+ return TRUE;
+ }
+
+ return m_Flags.m_fAllowEagerSTACleanup;
+}
+
+HRESULT RCW::EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(!IsFreeThreaded());
+ PRECONDITION(GetWrapperCtxEntryNoRef() != NULL);
+ }
+ CONTRACTL_END;
+
+ CtxEntryHolder pCtxEntry = GetWrapperCtxEntry();
+ return pCtxEntry->EnterContext(pCallbackFunc, pData);
+}
+
+//---------------------------------------------------------------------
+// Callback called to release the interfaces in the auxiliary cache.
+HRESULT __stdcall RCW::ReleaseAuxInterfacesCallBack(LPVOID pData)
+{
+ CONTRACT(HRESULT)
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(CheckPointer(pData));
+ POSTCONDITION(SUCCEEDED(RETVAL));
+ }
+ CONTRACT_END;
+
+ RCW* pWrap = (RCW*)pData;
+
+ LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
+ _ASSERTE(pCurrentCtxCookie != NULL);
+
+ RCW_VTABLEPTR(pWrap);
+
+ // we don't come here for free-threaded RCWs
+ _ASSERTE(!pWrap->IsFreeThreaded());
+
+ // we don't come here if there are no interfaces in the aux cache
+ _ASSERTE(pWrap->m_pAuxiliaryData != NULL);
+
+ RCWAuxiliaryData::InterfaceEntryIterator it = pWrap->m_pAuxiliaryData->IterateInterfacePointers();
+ while (it.Next())
+ {
+ InterfaceEntry *pEntry = it.GetEntry();
+ if (!pEntry->IsFree())
+ {
+ if (pCurrentCtxCookie == it.GetCtxCookie())
+ {
+ IUnknown *pUnk = it.GetEntry()->m_pUnknown;
+
+ // make sure we never try to clean this up again
+ pEntry->Free();
+ SafeReleasePreemp(pUnk, pWrap);
+ }
+ }
+ }
+
+ RETURN S_OK;
+}
+
+//---------------------------------------------------------------------
+// Callback called to release the IUnkEntry and the Interface entries.
+HRESULT __stdcall RCW::ReleaseAllInterfacesCallBack(LPVOID pData)
+{
+ CONTRACT(HRESULT)
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(CheckPointer(pData));
+ POSTCONDITION(SUCCEEDED(RETVAL));
+ }
+ CONTRACT_END;
+
+ RCW* pWrap = (RCW*)pData;
+
+ RCW_VTABLEPTR(pWrap);
+
+ LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
+ if (pCurrentCtxCookie == NULL || pCurrentCtxCookie == pWrap->GetWrapperCtxCookie() || pWrap->IsFreeThreaded())
+ {
+ pWrap->ReleaseAllInterfaces();
+ }
+ else
+ {
+ // Transition into the context to release the interfaces.
+ HRESULT hr = pWrap->EnterContext(ReleaseAllInterfacesCallBack, pWrap);
+ if (FAILED(hr))
+ {
+ // The context is disconnected so we cannot transition into it to clean up.
+ // The only option we have left is to try and release the interfaces from
+ // the current context. This will work for context agile object's since we have
+ // a pointer to them directly. It will however fail for others since we only
+ // have a pointer to a proxy which is no longer attached to the object.
+
+#ifdef MDA_SUPPORTED
+ MDA_TRIGGER_ASSISTANT(DisconnectedContext, ReportViolationCleanup(pWrap->GetWrapperCtxCookie(), pCurrentCtxCookie, hr));
+#endif
+
+ pWrap->ReleaseAllInterfaces();
+ }
+ }
+
+ // Free auxiliary interface entries if this is not an extensible RCW
+ if (!pWrap->IsURTAggregated() && pWrap->m_pAuxiliaryData != NULL)
+ {
+ RCWAuxiliaryData::InterfaceEntryIterator it = pWrap->m_pAuxiliaryData->IterateInterfacePointers();
+ while (it.Next())
+ {
+ InterfaceEntry *pEntry = it.GetEntry();
+ if (!pEntry->IsFree())
+ {
+ IUnknown *pUnk = it.GetEntry()->m_pUnknown;
+
+ if (pCurrentCtxCookie == NULL || pCurrentCtxCookie == it.GetCtxCookie() || pWrap->IsFreeThreaded())
+ {
+ // Notify Jupiter we are about to do a Release() for every cached interface pointer
+ // This needs to be made before call Release because we should never be in a
+ // state that we report more than the actual ref
+ RCWWalker::BeforeInterfaceRelease(pWrap);
+
+ // make sure we never try to clean this up again
+ pEntry->Free();
+ SafeReleasePreemp(pUnk, pWrap);
+ }
+ else
+ {
+ _ASSERTE(!pWrap->IsJupiterObject());
+
+ // Retrieve the addref'ed context entry that the wrapper lives in.
+ CtxEntryHolder pCtxEntry = it.GetCtxEntry();
+
+ // Transition into the context to release the interfaces.
+ HRESULT hr = pCtxEntry->EnterContext(ReleaseAuxInterfacesCallBack, pWrap);
+ if (FAILED(hr))
+ {
+ // The context is disconnected so we cannot transition into it to clean up.
+ // The only option we have left is to try and release the interfaces from
+ // the current context. This will work for context agile object's since we have
+ // a pointer to them directly. It will however fail for others since we only
+ // have a pointer to a proxy which is no longer attached to the object.
+
+#ifdef MDA_SUPPORTED
+ MDA_TRIGGER_ASSISTANT(DisconnectedContext, ReportViolationCleanup(it.GetCtxCookie(), pCurrentCtxCookie, hr));
+#endif
+
+ // make sure we never try to clean this up again
+ pEntry->Free();
+ SafeReleasePreemp(pUnk, pWrap);
+ }
+ }
+ }
+ }
+ }
+
+ RETURN S_OK;
+}
+
+//---------------------------------------------------------------------
+// Helper function called from ReleaseAllInterfacesCallBack do do the
+// actual releases.
+void RCW::ReleaseAllInterfaces()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ RCW_VTABLEPTR(this);
+
+ // Notify Jupiter we are about to do a Release() for IUnknown
+ // This needs to be made before call Release because we should never be in a
+ // state that we report more than the actual ref
+ RCWWalker::BeforeInterfaceRelease(this);
+
+ // Release the pUnk held by IUnkEntry
+ m_UnkEntry.ReleaseInterface(this);
+
+ // If this wrapper is not an Extensible RCW, free all the interface entries that have been allocated.
+ if (!IsURTAggregated())
+ {
+ for (int i = m_Flags.m_iEntryToRelease; i < INTERFACE_ENTRY_CACHE_SIZE; i++)
+ {
+ // Make sure we never try to clean this up again (so if we bail, we'll leak it).
+ m_Flags.m_iEntryToRelease++;
+
+ if (!m_aInterfaceEntries[i].IsFree())
+ {
+ // Notify Jupiter we are about to do a Release() for every cached interface pointer
+ // This needs to be made before call Release because we should never be in a
+ // state that we report more than the actual ref
+ RCWWalker::BeforeInterfaceRelease(this);
+
+ DWORD cbRef = SafeReleasePreemp(m_aInterfaceEntries[i].m_pUnknown, this);
+ LogInteropRelease(m_aInterfaceEntries[i].m_pUnknown, cbRef, "RCW::ReleaseAllInterfaces: Releasing ref from InterfaceEntry table");
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Returns RCWAuxiliaryData associated with this RCW.
+PTR_RCWAuxiliaryData RCW::GetOrCreateAuxiliaryData()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pAuxiliaryData == NULL)
+ {
+ NewHolder<RCWAuxiliaryData> pData = new RCWAuxiliaryData();
+ if (InterlockedCompareExchangeT(&m_pAuxiliaryData, pData.GetValue(), NULL) == NULL)
+ {
+ pData.SuppressRelease();
+ }
+ }
+ return m_pAuxiliaryData;
+}
+
+//---------------------------------------------------------------------
+// Returns true if the RCW supports given "standard managed" interface.
+bool RCW::SupportsMngStdInterface(MethodTable *pItfMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pItfMT));
+ }
+ CONTRACTL_END;
+
+ //
+ // Handle casts to normal managed standard interfaces.
+ //
+
+ // Check to see if the interface is a managed standard interface.
+ IID *pNativeIID = MngStdInterfaceMap::GetNativeIIDForType(pItfMT);
+ if (pNativeIID != NULL)
+ {
+ // It is a managed standard interface so we need to check to see if the COM component
+ // implements the native interface associated with it.
+ SafeComHolder<IUnknown> pNativeItf = NULL;
+
+ // QI for the native interface.
+ SafeQueryInterfaceRemoteAware(*pNativeIID, &pNativeItf);
+
+ // If the component supports the native interface then we can say it implements the
+ // standard interface.
+ if (pNativeItf)
+ return true;
+ }
+ else
+ {
+ //
+ // Handle casts to IEnumerable.
+ //
+
+ // If the requested interface is IEnumerable then we need to check to see if the
+ // COM object implements IDispatch and has a member with DISPID_NEWENUM.
+ if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE))
+ {
+ SafeComHolder<IDispatch> pDisp = NULL;
+#ifdef FEATURE_CORECLR
+ if (!AppX::IsAppXProcess())
+#endif // FEATURE_CORECLR
+ {
+ // Get the IDispatch on the current thread.
+ pDisp = GetIDispatch();
+ }
+ if (pDisp)
+ {
+ DISPPARAMS DispParams = {0, 0, NULL, NULL};
+ VariantHolder VarResult;
+
+ // Initialize the return variant.
+ SafeVariantInit(&VarResult);
+
+ HRESULT hr = E_FAIL;
+ {
+ // We are about to make a call to COM so switch to preemptive GC.
+ GCX_PREEMP();
+
+ // Can not get the IP for pDisp->Invoke, instead using the first IP in vtable.
+ LeaveRuntimeHolder holder (**(size_t**)((IDispatch*)pDisp));
+
+ // Call invoke with DISPID_NEWENUM to see if such a member exists.
+ hr = pDisp->Invoke(
+ DISPID_NEWENUM,
+ IID_NULL,
+ LOCALE_USER_DEFAULT,
+ DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+ &DispParams,
+ &VarResult,
+ NULL,
+ NULL
+ );
+ }
+
+ // If the invoke succeeded then the component has a member DISPID_NEWENUM
+ // so we can expose it as an IEnumerable.
+ if (SUCCEEDED(hr))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//---------------------------------------------------------------------
+// Determines whether a call through the given interface should use new
+// WinRT interop (as opposed to classic COM).
+TypeHandle::CastResult RCW::SupportsWinRTInteropInterfaceNoGC(MethodTable *pItfMT)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ WinMDAdapter::RedirectedTypeIndex index;
+
+ // @TODO: Make this nicer?
+ RedirectionBehavior redirectionBehavior;
+ if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE))
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_IEnumerable;
+ else if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTION))
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_ICollection;
+ else if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__ILIST))
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_IList;
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_INotifyCollectionChanged)
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_INotifyCollectionChanged;
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_ComponentModel_INotifyPropertyChanged)
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_INotifyPropertyChanged;
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_Windows_Input_ICommand)
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_ICommand;
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_IDisposable)
+ redirectionBehavior = (RedirectionBehavior)m_Flags.m_RedirectionBehavior_IDisposable;
+ else
+ {
+ UNREACHABLE_MSG("Unknown redirected interface");
+ }
+
+ if ((redirectionBehavior & RedirectionBehaviorComputed) == 0)
+ {
+ // we don't know yet what the behavior should be
+ return TypeHandle::MaybeCast;
+ }
+
+ return ((redirectionBehavior & RedirectionBehaviorEnabled) == 0 ?
+ TypeHandle::CannotCast :
+ TypeHandle::CanCast);
+}
+
+//---------------------------------------------------------------------
+// This is a GC-triggering variant of code:SupportsWinRTInteropInterfaceNoGC.
+bool RCW::SupportsWinRTInteropInterface(MethodTable *pItfMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ TypeHandle::CastResult result = SupportsWinRTInteropInterfaceNoGC(pItfMT);
+ switch (result)
+ {
+ case TypeHandle::CanCast: return true;
+ case TypeHandle::CannotCast: return false;
+ }
+
+ WinMDAdapter::RedirectedTypeIndex index;
+ bool fLegacySupported;
+
+ // @TODO: Make this nicer?
+ RedirectionBehavior redirectionBehavior;
+ RCWFlags newFlags = { 0 };
+
+ if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__IENUMERABLE))
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_IEnumerable = redirectionBehavior;
+ newFlags.m_RedirectionBehavior_IEnumerable_LegacySupported = fLegacySupported;
+ }
+ else if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__ICOLLECTION))
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_ICollection = redirectionBehavior;
+ }
+ else if (pItfMT == MscorlibBinder::GetExistingClass(CLASS__ILIST))
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_IList = redirectionBehavior;
+ }
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_INotifyCollectionChanged)
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_INotifyCollectionChanged = redirectionBehavior;
+ }
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_ComponentModel_INotifyPropertyChanged)
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_INotifyPropertyChanged = redirectionBehavior;
+ }
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_Windows_Input_ICommand)
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_ICommand = redirectionBehavior;
+ }
+ else if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pItfMT, &index) && index == WinMDAdapter::RedirectedTypeIndex_System_IDisposable)
+ {
+ redirectionBehavior = ComputeRedirectionBehavior(pItfMT, &fLegacySupported);
+ newFlags.m_RedirectionBehavior_IDisposable = redirectionBehavior;
+ }
+ else
+ {
+ UNREACHABLE_MSG("Unknown redirected interface");
+ }
+
+ // Use interlocked operation so we don't race with other threads trying to set some other flags on the RCW.
+ // Note that since we are in cooperative mode, we don't race with RCWCache::DetachWrappersWorker here.
+ FastInterlockOr(&m_Flags.m_dwFlags, newFlags.m_dwFlags);
+
+ _ASSERTE((redirectionBehavior & RedirectionBehaviorComputed) != 0);
+ return ((redirectionBehavior & RedirectionBehaviorEnabled) != 0);
+}
+
+//---------------------------------------------------------------------
+// Computes the result of code:SupportsWinRTInteropInterface.
+RCW::RedirectionBehavior RCW::ComputeRedirectionBehavior(MethodTable *pItfMT, bool *pfLegacySupported)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ *pfLegacySupported = false;
+
+ // @TODO: It may be possible to take advantage of metadata (e.g. non-WinRT ComImport class says it implements ICollection -> use classic COM)
+ // and/or the interface cache but for now we'll just QI.
+
+ IID iid;
+ pItfMT->GetGuid(&iid, TRUE, TRUE);
+
+ SafeComHolder<IUnknown> pUnk;
+ if (SUCCEEDED(SafeQueryInterfaceRemoteAware(iid, &pUnk)))
+ {
+ // if the object supports the legacy COM interface we don't use redirection
+ *pfLegacySupported = true;
+ return RedirectionBehaviorComputed;
+ }
+
+ if (SupportsMngStdInterface(pItfMT))
+ {
+ // if the object supports the corresponding "managed std" interface we don't use redirection
+ *pfLegacySupported = true;
+ return RedirectionBehaviorComputed;
+ }
+
+ COMOBJECTREF oref = GetExposedObject();
+ if (ComObject::SupportsInterface(oref, pItfMT))
+ {
+ // the cast succeeded but we know that the legacy COM interface is not implemented
+ // -> we know for sure that the object supports the WinRT redirected interface
+ return (RedirectionBehavior)(RedirectionBehaviorComputed | RedirectionBehaviorEnabled);
+ }
+
+ // The object does not support anything which means that we are in a failure case and an
+ // exception will be thrown. For back compat we want the exception message to include the
+ // classic COM IID so we'll return the "no redirection" result.
+ return RedirectionBehaviorComputed;
+}
+
+//--------------------------------------------------------------------------------
+// OBJECTREF ComObject::CreateComObjectRef(MethodTable* pMT)
+// returns NULL for out of memory scenarios
+OBJECTREF ComObject::CreateComObjectRef(MethodTable* pMT)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(pMT->IsComObjectType());
+ }
+ CONTRACTL_END;
+
+ if (pMT != g_pBaseCOMObject)
+ {
+ pMT->CheckRestore();
+ pMT->EnsureInstanceActive();
+
+ //
+ // Collectible types do not support com interop
+ //
+ if (pMT->Collectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM"));
+ }
+
+ pMT->CheckRunClassInitThrowing();
+ }
+
+ return AllocateObject(pMT, false);
+}
+
+
+//--------------------------------------------------------------------------------
+// SupportsInterface
+BOOL ComObject::SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ INJECT_FAULT(COMPlusThrowOM());
+ PRECONDITION(oref != NULL);
+ PRECONDITION(CheckPointer(pIntfTable));
+ }
+ CONTRACTL_END
+
+ SafeComHolder<IUnknown> pUnk = NULL;
+ HRESULT hr;
+ BOOL bSupportsItf = FALSE;
+
+ GCPROTECT_BEGIN(oref);
+
+ // Make sure the interface method table has been restored.
+ pIntfTable->CheckRestore();
+
+ // Check to see if the static class definition indicates we implement the interface.
+ MethodTable *pMT = oref->GetMethodTable();
+ if (pMT->CanCastToInterface(pIntfTable))
+ {
+ bSupportsItf = TRUE;
+ }
+ else
+ {
+ RCWHolder pRCW(GetThread());
+ RCWPROTECT_BEGIN(pRCW, oref);
+
+ // This should not be called for interfaces that are in the normal portion of the
+ // interface map for this class. The only interfaces that are in the interface map
+ // but are not in the normal portion are the dynamic interfaces on extensible RCW's.
+ _ASSERTE(!oref->GetMethodTable()->ImplementsInterface(pIntfTable));
+
+
+ //
+ // First QI the object to see if it implements the specified interface.
+ //
+
+ pUnk = pRCW->GetComIPFromRCW(pIntfTable);
+ if (pUnk)
+ {
+ bSupportsItf = true;
+ }
+ else if (pIntfTable->IsComEventItfType())
+ {
+ MethodTable *pSrcItfClass = NULL;
+ MethodTable *pEvProvClass = NULL;
+ GUID SrcItfIID;
+ SafeComHolder<IConnectionPointContainer> pCPC = NULL;
+ SafeComHolder<IConnectionPoint> pCP = NULL;
+
+ // Retrieve the IID of the source interface associated with this
+ // event interface.
+ pIntfTable->GetEventInterfaceInfo(&pSrcItfClass, &pEvProvClass);
+ pSrcItfClass->GetGuid(&SrcItfIID, TRUE);
+
+ // QI for IConnectionPointContainer.
+ hr = pRCW->SafeQueryInterfaceRemoteAware(IID_IConnectionPointContainer, (IUnknown**)&pCPC);
+
+ // If the component implements IConnectionPointContainer, then check
+ // to see if it handles the source interface.
+ if (SUCCEEDED(hr))
+ {
+ GCX_PREEMP(); // make sure we switch to preemptive mode before calling the external COM object
+ LeaveRuntimeHolder lrh(*((*(size_t**)(IConnectionPointContainer*)pCPC)+4));
+ hr = pCPC->FindConnectionPoint(SrcItfIID, &pCP);
+ if (SUCCEEDED(hr))
+ {
+ // The component handles the source interface so we can succeed the QI call.
+ bSupportsItf = true;
+ }
+ }
+ }
+ else if (pRCW->SupportsMngStdInterface(pIntfTable))
+ {
+ bSupportsItf = true;
+ }
+
+ if (bSupportsItf)
+ {
+ // If the object has a dynamic interface map then we have extra work to do.
+ MethodTable *pMT = oref->GetMethodTable();
+ if (pMT->HasDynamicInterfaceMap())
+ {
+ // First, make sure we haven't already added this.
+ if (!pMT->FindDynamicallyAddedInterface(pIntfTable))
+ {
+ // It's not there.
+ if (!pMT->IsWinRTObjectType())
+ {
+ // Check if the object supports all of these interfaces only if this is a classic COM interop
+ // scenario. This is a perf optimization (no need to QI for base interfaces if we don't really
+ // need them just yet) and also has a usability aspect. If this SupportsInterface call failed
+ // because one of the base interfaces is not supported, the exception we'd throw would contain
+ // only the name of the "top level" interface which would confuse the developer.
+ MethodTable::InterfaceMapIterator it = pIntfTable->IterateInterfaceMap();
+ while (it.Next())
+ {
+ bSupportsItf = Object::SupportsInterface(oref, it.GetInterface());
+ if (!bSupportsItf)
+ break;
+ }
+ }
+
+ // If the object supports all these interfaces, attempt to add the interface table
+ // to the cache.
+ if (bSupportsItf)
+ {
+ {
+ // Take the wrapper cache lock before we start playing with the interface map.
+ RCWCache::LockHolder lh(RCWCache::GetRCWCache());
+
+ // Check again with the lock.
+ if (!pMT->FindDynamicallyAddedInterface(pIntfTable))
+ {
+ // Add it to the dynamic interface table.
+ pMT->AddDynamicInterface(pIntfTable);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RCWPROTECT_END(pRCW);
+ }
+
+ GCPROTECT_END();
+
+ return bSupportsItf;
+
+}
+
+//--------------------------------------------------------------------
+// ThrowInvalidCastException
+void ComObject::ThrowInvalidCastException(OBJECTREF *pObj, MethodTable *pCastToMT)
+{
+ CONTRACT_VOID
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(pObj != NULL);
+ PRECONDITION(*pObj != NULL);
+ PRECONDITION(IsProtectedByGCFrame (pObj));
+ POSTCONDITION(!"This function should never return!");
+ }
+ CONTRACT_END;
+
+ SafeComHolder<IUnknown> pItf = NULL;
+ HRESULT hr = S_OK;
+ IID *pNativeIID = NULL;
+ GUID iid;
+
+ // Use an InlineSString with a size of MAX_CLASSNAME_LENGTH + 1 to prevent
+ // TypeHandle::GetName from having to allocate a new block of memory. This
+ // significantly improves the performance of throwing an InvalidCastException.
+ InlineSString<MAX_CLASSNAME_LENGTH + 1> strComObjClassName;
+ InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastToName;
+
+ TypeHandle thClass = (*pObj)->GetTypeHandle();
+ TypeHandle thCastTo = TypeHandle(pCastToMT);
+
+ thClass.GetName(strComObjClassName);
+ thCastTo.GetName(strCastToName);
+
+ if (thCastTo.IsInterface())
+ {
+ RCWHolder pRCW(GetThread());
+ pRCW.Init(*pObj);
+
+ // Retrieve the IID of the interface.
+ MethodTable *pCOMItfMT = NULL;
+ if (pRCW->GetInterfaceForQI(thCastTo.GetMethodTable(), &pCOMItfMT) == RCW::InterfaceRedirection_UnresolvedIEnumerable)
+ {
+ // A special exception message for the case where we are unable to figure out the
+ // redirected interface because we haven't seen a cast to a generic IEnumerable yet.
+ COMPlusThrow(kInvalidCastException, IDS_EE_WINRT_IENUMERABLE_BAD_CAST);
+ }
+
+ if (pCOMItfMT->IsProjectedFromWinRT())
+ {
+ // pCOMItfMT could be a generic WinRT-illegal interface in which case GetGuid would throw a confusing BadImageFormatException
+ // so we swallow the exception and throw the generic InvalidCastException instead
+ if (FAILED(pCOMItfMT->GetGuidNoThrow(&iid, FALSE)))
+ {
+ COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+ }
+ else
+ {
+ // keep calling the throwing GetGuid for non-WinRT interfaces (back compat)
+ pCOMItfMT->GetGuid(&iid, TRUE);
+ }
+
+ // Query for the interface to determine the failure HRESULT.
+ hr = pRCW->SafeQueryInterfaceRemoteAware(iid, (IUnknown**)&pItf);
+
+ // If this function was called, it means the QI call failed in the past. If it
+ // no longer fails now, we still need to throw, so throw a generic invalid cast exception.
+ if (SUCCEEDED(hr) ||
+ // Also throw the generic exception if the QI failed with E_NOINTERFACE and this is
+ // a WinRT scenario - the user is very likely not interested in details like IID and
+ // HRESULT, they just want to get the "managed" experience.
+ (hr == E_NOINTERFACE && (thClass.GetMethodTable()->IsWinRTObjectType() || pCOMItfMT->IsProjectedFromWinRT())))
+ {
+ COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+
+ // Convert the IID to a string.
+ WCHAR strIID[39];
+ StringFromGUID2(iid, strIID, sizeof(strIID) / sizeof(WCHAR));
+
+ // Obtain the textual description of the HRESULT.
+ SString strHRDescription;
+ GetHRMsg(hr, strHRDescription);
+
+ if (thCastTo.IsComEventItfType())
+ {
+ GUID SrcItfIID;
+ MethodTable *pSrcItfClass = NULL;
+ MethodTable *pEvProvClass = NULL;
+
+ // Retrieve the IID of the source interface associated with this event interface.
+ thCastTo.GetMethodTable()->GetEventInterfaceInfo(&pSrcItfClass, &pEvProvClass);
+ pSrcItfClass->GetGuid(&SrcItfIID, TRUE);
+
+ // Convert the source interface IID to a string.
+ WCHAR strSrcItfIID[39];
+ StringFromGUID2(SrcItfIID, strSrcItfIID, sizeof(strSrcItfIID) / sizeof(WCHAR));
+
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_EVENTITF, strHRDescription.GetUnicode(), strComObjClassName.GetUnicode(),
+ strCastToName.GetUnicode(), strIID, strSrcItfIID);
+ }
+ else if (thCastTo == TypeHandle(MscorlibBinder::GetClass(CLASS__IENUMERABLE)))
+ {
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_IENUMERABLE,
+ strHRDescription.GetUnicode(), strComObjClassName.GetUnicode(), strCastToName.GetUnicode(), strIID);
+ }
+ else if ((pNativeIID = MngStdInterfaceMap::GetNativeIIDForType(thCastTo)) != NULL)
+ {
+ // Convert the source interface IID to a string.
+ WCHAR strNativeItfIID[39];
+ StringFromGUID2(*pNativeIID, strNativeItfIID, sizeof(strNativeItfIID) / sizeof(WCHAR));
+
+ // Query for the interface to determine the failure HRESULT.
+ HRESULT hr2 = pRCW->SafeQueryInterfaceRemoteAware(iid, (IUnknown**)&pItf);
+
+ // If this function was called, it means the QI call failed in the past. If it
+ // no longer fails now, we still need to throw, so throw a generic invalid cast exception.
+ if (SUCCEEDED(hr2))
+ COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+
+ // Obtain the textual description of the 2nd HRESULT.
+ SString strHR2Description;
+ GetHRMsg(hr2, strHR2Description);
+
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_MNGSTDITF, strHRDescription.GetUnicode(), strComObjClassName.GetUnicode(),
+ strCastToName.GetUnicode(), strIID, strNativeItfIID, strHR2Description.GetUnicode());
+ }
+ else
+ {
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_ITF,
+ strHRDescription.GetUnicode(), strComObjClassName.GetUnicode(), strCastToName.GetUnicode(), strIID);
+ }
+ }
+ else
+ {
+ // Validate that this function wasn't erroneously called.
+ _ASSERTE(!thClass.CanCastTo(thCastTo));
+
+ if (thClass.GetMethodTable()->IsWinRTObjectType() || thCastTo.IsProjectedFromWinRT() || thCastTo.GetMethodTable()->IsWinRTObjectType())
+ {
+ // don't mention any "COM components" in the exception if we failed to cast a WinRT object or
+ // to a WinRT object, throw the simple generic InvalidCastException instead
+ COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+
+ if (thCastTo.IsComObjectType())
+ {
+ if (IsComObjectClass(thClass))
+ {
+ // An attempt was made to cast an __ComObject to ComImport metadata defined type.
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_COMOBJ_TO_MD,
+ strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+ else
+ {
+ // An attempt was made to cast an instance of a ComImport metadata defined type to
+ // a different non ComImport metadata defined type.
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_MD_TO_MD,
+ strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+ }
+ else
+ {
+ // An attempt was made to cast this RCW to a non ComObjectType class.
+ COMPlusThrow(kInvalidCastException, IDS_EE_RCW_INVALIDCAST_TO_NON_COMOBJTYPE,
+ strComObjClassName.GetUnicode(), strCastToName.GetUnicode());
+ }
+ }
+
+ RETURN;
+}
+
+//--------------------------------------------------------------------------------
+// Release all the data associated with the __ComObject.
+void ComObject::ReleaseAllData(OBJECTREF oref)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(oref != NULL);
+ PRECONDITION(oref->GetMethodTable()->IsComObjectType());
+ }
+ CONTRACTL_END;
+
+ GCPROTECT_BEGIN(oref)
+ {
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__COM_OBJECT__RELEASE_ALL_DATA);
+
+ DECLARE_ARGHOLDER_ARRAY(ReleaseAllDataArgs, 1);
+ ReleaseAllDataArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(oref);
+
+ CALL_MANAGED_METHOD_NORET(ReleaseAllDataArgs);
+ }
+ GCPROTECT_END();
+}
+
+#ifndef DACCESS_COMPILE
+//--------------------------------------------------------------------------
+// Wrapper around code:RCW.GetComIPFromRCW
+// static
+IUnknown *ComObject::GetComIPFromRCW(OBJECTREF *pObj, MethodTable* pIntfTable)
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(IsProtectedByGCFrame(pObj));
+ PRECONDITION(CheckPointer(pIntfTable, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // NULL if we couldn't find match
+ }
+ CONTRACT_END;
+
+ SafeComHolder<IUnknown> pIUnk;
+
+ RCWHolder pRCW(GetThread());
+ RCWPROTECT_BEGIN(pRCW, *pObj);
+
+ pIUnk = pRCW->GetComIPFromRCW(pIntfTable);
+
+ RCWPROTECT_END(pRCW);
+ RETURN pIUnk.Extract();
+}
+
+//--------------------------------------------------------------------------
+// Wrapper around code:ComObject.GetComIPFromRCW that throws InvalidCastException
+// static
+IUnknown *ComObject::GetComIPFromRCWThrowing(OBJECTREF *pObj, MethodTable* pIntfTable)
+{
+ CONTRACT (IUnknown*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(IsProtectedByGCFrame(pObj));
+ PRECONDITION(CheckPointer(pIntfTable, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IUnknown* pIUnk = GetComIPFromRCW(pObj, pIntfTable);
+
+ if (pIUnk == NULL)
+ ThrowInvalidCastException(pObj, pIntfTable);
+
+ RETURN pIUnk;
+}
+
+//
+// Create override information based on interface lookup
+//
+WinRTOverrideInfo::WinRTOverrideInfo(EEClass *pClass)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ PRECONDITION(CheckPointer(pClass));
+ }
+ CONTRACTL_END;
+
+ ::ZeroMemory(this, sizeof(WinRTOverrideInfo));
+
+ MethodTable *pMT = pClass->GetMethodTable();
+
+ _ASSERTE(IsTdClass(pClass->GetAttrClass()));
+ //
+ // Iterate through each implemented interface
+ // Note that the interface map is laid from parent to child
+ // So we start from the most derived class, and climb our way up to parent, instead of
+ // inspecting interface map directly
+ //
+ while (pMT != g_pBaseCOMObject)
+ {
+ MethodTable *pParentMT = pMT->GetParentMethodTable();
+ unsigned dwParentInterfaces = 0;
+ if (pParentMT)
+ dwParentInterfaces = pParentMT->GetNumInterfaces();
+
+ DWORD dwFound = 0;
+
+ //
+ // Scanning only current class only if the current class have more interface than parent
+ //
+ if (pMT->GetNumInterfaces() > dwParentInterfaces)
+ {
+ MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMapFrom(dwParentInterfaces);
+ while (!it.Finished())
+ {
+ MethodTable *pImplementedIntfMT = it.GetInterface();
+
+ // Only check private interfaces as they are exclusive
+ if (IsTdNotPublic(pImplementedIntfMT->GetAttrClass()) && pImplementedIntfMT->IsProjectedFromWinRT())
+ {
+ if (m_pToStringMD == NULL)
+ {
+ m_pToStringMD = MemberLoader::FindMethod(
+ pImplementedIntfMT,
+ "ToString",
+ &gsig_IM_RetStr);
+ if (m_pToStringMD != NULL)
+ dwFound++;
+ }
+
+ if (m_pGetHashCodeMD == NULL)
+ {
+ m_pGetHashCodeMD = MemberLoader::FindMethod(
+ pImplementedIntfMT,
+ "GetHashCode",
+ &gsig_IM_RetInt);
+ if (m_pGetHashCodeMD != NULL)
+ dwFound++;
+ }
+
+ if (m_pEqualsMD == NULL)
+ {
+ m_pEqualsMD = MemberLoader::FindMethod(
+ pImplementedIntfMT,
+ "Equals",
+ &gsig_IM_Obj_RetBool);
+ if (m_pEqualsMD != NULL)
+ dwFound++;
+ }
+
+ if (dwFound == 3)
+ return;
+ }
+
+ it.Next();
+ }
+ }
+
+ //
+ // Parent has no more interfaces (including parents of parent). We are done
+ //
+ if (dwParentInterfaces == 0)
+ break;
+
+ pMT = pParentMT;
+ }
+}
+
+//
+// If WinRTOverrideInfo is not created, create one. Otherwise return existing one
+//
+WinRTOverrideInfo *WinRTOverrideInfo::GetOrCreateWinRTOverrideInfo(MethodTable *pMT)
+{
+ CONTRACT (WinRTOverrideInfo*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ PRECONDITION(CheckPointer(pMT));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ _ASSERTE(pMT != NULL);
+
+ EEClass *pClass = pMT->GetClass();
+
+ //
+ // Retrieve the WinRTOverrideInfo from WinRT class factory
+ // It is kind of sub-optimal but saves a EEClass field
+ //
+ WinRTClassFactory *pClassFactory = GetComClassFactory(pMT)->AsWinRTClassFactory();
+
+ WinRTOverrideInfo *pOverrideInfo = pClassFactory->GetWinRTOverrideInfo();
+ if (pOverrideInfo == NULL)
+ {
+ //
+ // Create the override information
+ //
+ NewHolder<WinRTOverrideInfo> pNewOverrideInfo = new WinRTOverrideInfo(pClass);
+
+ if (pNewOverrideInfo->m_pEqualsMD == NULL &&
+ pNewOverrideInfo->m_pGetHashCodeMD == NULL &&
+ pNewOverrideInfo->m_pToStringMD == NULL)
+ {
+ // Special optimization for where there is no override found
+ pMT->SetSkipWinRTOverride();
+
+ RETURN NULL;
+ }
+ else
+ {
+ if (pClassFactory->SetWinRTOverrideInfo(pNewOverrideInfo))
+ {
+ // We win the race
+ pNewOverrideInfo.SuppressRelease();
+ RETURN pNewOverrideInfo;
+ }
+ else
+ {
+ // Lost the race - retrieve again
+ RETURN pClassFactory->GetWinRTOverrideInfo();
+ }
+ }
+ }
+
+ RETURN pOverrideInfo;
+}
+
+//
+// Redirection for ToString
+//
+NOINLINE static MethodDesc *GetRedirectedToStringMDHelper(Object *pThisUNSAFE, MethodTable *pMT)
+{
+ FC_INNER_PROLOG(ComObject::GetRedirectedToStringMD);
+
+ MethodDesc *pRetMD = NULL;
+
+ // Creates helper frame for GetOrCreateWinRTOverrideInfo (which throws)
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
+
+ WinRTOverrideInfo *pOverrideInfo = WinRTOverrideInfo::GetOrCreateWinRTOverrideInfo(pMT);
+ if (pOverrideInfo && pOverrideInfo->m_pToStringMD != NULL)
+ {
+ pRetMD = pOverrideInfo->m_pToStringMD;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_INNER_EPILOG();
+
+ return pRetMD;
+}
+
+FCIMPL1(MethodDesc *, ComObject::GetRedirectedToStringMD, Object *pThisUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ MethodTable *pMT = pThisUNSAFE->GetMethodTable();
+ if (pMT->IsSkipWinRTOverride())
+ return NULL;
+
+ FC_INNER_RETURN(MethodDesc*, ::GetRedirectedToStringMDHelper(pThisUNSAFE, pMT));
+}
+FCIMPLEND
+
+FCIMPL2(StringObject *, ComObject::RedirectToString, Object *pThisUNSAFE, MethodDesc *pToStringMD)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ OBJECTREF refThis = ObjectToOBJECTREF(pThisUNSAFE);
+ STRINGREF refString = NULL;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_2(refThis, refString);
+
+ // Note that this has to be virtual. Consider this case
+ //
+ // interface INativeA
+ // {
+
+ // string ToString();
+ // }
+ //
+ // class NativeA : INativeA
+ // {
+ // protected override ToString()
+ // {
+ // .override IA.ToString()
+ // }
+ // }
+ //
+ // class Managed : NativeA
+ // {
+ // override ToString();
+ // }
+ //
+ // If we call IA.ToString virtually, we'll land on INativeA.ToString() which is not correct.
+ // Calling it virtually will solve this problem
+ PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pToStringMD, refThis);
+
+ DECLARE_ARGHOLDER_ARRAY(ToStringArgs, 1);
+ ToStringArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(refThis);
+
+ CALL_MANAGED_METHOD_RETREF(refString, STRINGREF, ToStringArgs);
+
+ HELPER_METHOD_FRAME_END();
+
+ return STRINGREFToObject(refString);
+}
+FCIMPLEND
+
+//
+// Redirection for GetHashCode
+//
+NOINLINE static MethodDesc *GetRedirectedGetHashCodeMDHelper(Object *pThisUNSAFE, MethodTable *pMT)
+{
+ FC_INNER_PROLOG(ComObject::GetRedirectedGetHashCodeMD);
+
+ MethodDesc *pRetMD = NULL;
+
+ // Creates helper frame for GetOrCreateWinRTOverrideInfo (which throws)
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
+
+ WinRTOverrideInfo *pOverrideInfo = WinRTOverrideInfo::GetOrCreateWinRTOverrideInfo(pMT);
+ if (pOverrideInfo && pOverrideInfo->m_pGetHashCodeMD != NULL)
+ {
+ pRetMD = pOverrideInfo->m_pGetHashCodeMD;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_INNER_EPILOG();
+
+ return pRetMD;
+}
+
+FCIMPL1(MethodDesc *, ComObject::GetRedirectedGetHashCodeMD, Object *pThisUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ MethodTable *pMT = pThisUNSAFE->GetMethodTable();
+ if (pMT->IsSkipWinRTOverride())
+ return NULL;
+
+ FC_INNER_RETURN(MethodDesc*, ::GetRedirectedGetHashCodeMDHelper(pThisUNSAFE, pMT));
+}
+FCIMPLEND
+
+FCIMPL2(int, ComObject::RedirectGetHashCode, Object *pThisUNSAFE, MethodDesc *pGetHashCodeMD)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ OBJECTREF refThis = ObjectToOBJECTREF(pThisUNSAFE);
+ int hash = 0;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
+
+ // Note that this has to be virtual. See RedirectToString for more details
+ PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pGetHashCodeMD, refThis);
+
+ DECLARE_ARGHOLDER_ARRAY(GetHashCodeArgs, 1);
+ GetHashCodeArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(refThis);
+
+ CALL_MANAGED_METHOD(hash, int, GetHashCodeArgs);
+
+ HELPER_METHOD_FRAME_END();
+
+ return hash;
+}
+FCIMPLEND
+
+NOINLINE static MethodDesc *GetRedirectedEqualsMDHelper(Object *pThisUNSAFE, MethodTable *pMT)
+{
+ FC_INNER_PROLOG(ComObject::GetRedirectedEqualsMD);
+
+ MethodDesc *pRetMD = NULL;
+
+ // Creates helper frame for GetOrCreateWinRTOverrideInfo (which throws)
+ HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
+
+ WinRTOverrideInfo *pOverrideInfo = WinRTOverrideInfo::GetOrCreateWinRTOverrideInfo(pMT);
+ if (pOverrideInfo && pOverrideInfo->m_pEqualsMD!= NULL)
+ {
+ pRetMD = pOverrideInfo->m_pEqualsMD;
+ }
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_INNER_EPILOG();
+
+ return pRetMD;
+}
+
+FCIMPL1(MethodDesc *, ComObject::GetRedirectedEqualsMD, Object *pThisUNSAFE)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ MethodTable *pMT = pThisUNSAFE->GetMethodTable();
+ if (pMT->IsSkipWinRTOverride())
+ return NULL;
+
+ FC_INNER_RETURN(MethodDesc*, ::GetRedirectedEqualsMDHelper(pThisUNSAFE, pMT));
+}
+FCIMPLEND
+
+FCIMPL3(FC_BOOL_RET, ComObject::RedirectEquals, Object *pThisUNSAFE, Object *pOtherUNSAFE, MethodDesc *pEqualsMD)
+{
+ CONTRACTL
+ {
+ FCALL_CHECK;
+ PRECONDITION(CheckPointer(pThisUNSAFE));
+ }
+ CONTRACTL_END;
+
+ OBJECTREF refThis = ObjectToOBJECTREF(pThisUNSAFE);
+ OBJECTREF refOther = ObjectToOBJECTREF(pOtherUNSAFE);
+
+ CLR_BOOL ret = FALSE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_2(refThis, refOther);
+
+ // Note that this has to be virtual. See RedirectToString for more details
+ PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pEqualsMD, refThis);
+
+ DECLARE_ARGHOLDER_ARRAY(EqualArgs, 2);
+ EqualArgs[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(refThis);
+ EqualArgs[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(refOther);
+
+ CALL_MANAGED_METHOD(ret, CLR_BOOL, EqualArgs);
+
+ HELPER_METHOD_FRAME_END();
+
+ FC_RETURN_BOOL(ret);
+}
+FCIMPLEND
+
+#endif // #ifndef DACCESS_COMPILE
+
+#endif //#ifndef CROSSGEN_COMPILE
+