1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
11 #include "runtimecallablewrapper.h"
15 #include "win32threadpool.h"
17 #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
18 #include "olecontexthelpers.h"
19 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
22 //================================================================
24 const GUID IID_IEnterActivityWithNoLock = { 0xd7174f82, 0x36b8, 0x4aa8, { 0x80, 0x0a, 0xe9, 0x63, 0xab, 0x2d, 0xfa, 0xb9 } };
25 const GUID IID_IFuncEvalAbort = { 0xde6844f6, 0x95ac, 0x4e83, { 0x90, 0x8d, 0x9b, 0x1b, 0xea, 0x2f, 0xe0, 0x8c } };
27 // sanity check., to find stress bug #82137
28 VOID IUnkEntry::CheckValidIUnkEntry()
40 COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER);
44 // Version that returns an HR instead of throwing.
45 HRESULT IUnkEntry::HRCheckValidIUnkEntry()
57 return COR_E_INVALIDCOMOBJECT;
63 // Returns IErrorInfo corresponding to the exception injected by the debugger to abort a func eval,
64 // or NULL if there is no such exception.
65 static IErrorInfo *CheckForFuncEvalAbortNoThrow(HRESULT hr)
75 // the managed exception thrown by the debugger is translated to EXCEPTION_COMPLUS by COM
76 if (hr == EXCEPTION_COMPLUS)
80 // we recognize the ones thrown by the debugger by QI'ing the IErrorInfo for a special IID
81 ReleaseHolder<IErrorInfo> pErrorInfo;
82 if (SafeGetErrorInfo(&pErrorInfo) == S_OK)
84 ReleaseHolder<IUnknown> pUnk;
85 if (SafeQueryInterface(pErrorInfo, IID_IFuncEvalAbort, &pUnk) == S_OK)
87 // QI succeeded, this is a func eval abort
88 return pErrorInfo.Extract();
92 // QI failed, put the IErrorInfo back
93 SetErrorInfo(0, pErrorInfo);
101 // Rethrows the exception injected by the debugger to abort a func eval, or does nothing if there is no such exception.
102 static void CheckForFuncEvalAbort(HRESULT hr)
112 IErrorInfo *pErrorInfo = CheckForFuncEvalAbortNoThrow(hr);
113 if (pErrorInfo != NULL)
115 // COMPlusThrowHR internally releases the pErrorInfo
116 COMPlusThrowHR(hr, pErrorInfo);
120 //+-------------------------------------------------------------------------
122 // Function: STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf))
123 // Create a stream in the memory
125 STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf)
132 INJECT_FAULT(CONTRACT_RETURN NULL);
133 PRECONDITION(CheckPointer(ppBuf, NULL_OK));
134 PRECONDITION(CheckPointer(ppBuf, NULL_OK));
135 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
139 LPSTREAM pstm = NULL;
141 BYTE* pMem = new(nothrow) BYTE[cb];
144 HRESULT hr = CInMemoryStream::CreateStreamOnMemory(pMem, cb, &pstm, TRUE);
145 _ASSERTE(hr == S_OK || pstm == NULL);
154 //=====================================================================
155 // HRESULT wCoMarshalInterThreadInterfaceInStream
156 HRESULT wCoMarshalInterThreadInterfaceInStream(REFIID riid,
162 #endif // !PLATFORM_CE
169 PRECONDITION(CheckPointer(pUnk));
170 PRECONDITION(CheckPointer(ppStm));
175 LPSTREAM pStm = NULL;
177 DWORD mshlFlgs = MSHLFLAGS_NORMAL;
180 hr = CoGetMarshalSizeMax(&lSize, IID_IUnknown, pUnk, MSHCTX_INPROC, NULL, mshlFlgs);
185 pStm = CreateMemStm(lSize, NULL);
189 // Marshal the interface into the stream TABLE STRONG
190 hr = CoMarshalInterface(pStm, riid, pUnk, MSHCTX_INPROC, NULL, mshlFlgs);
200 // Reset the stream to the begining
204 pStm->Seek(li, STREAM_SEEK_SET, &li2);
206 // Set the return value
211 // Cleanup if failure
212 SafeReleasePreemp(pStm);
220 //================================================================
221 // Struct passed in to DoCallback.
222 struct CtxEntryEnterContextCallbackData
224 PFNCTXCALLBACK m_pUserCallbackFunc;
227 HRESULT m_UserCallbackHR;
230 //================================================================
232 CtxEntryCache* CtxEntryCache::s_pCtxEntryCache = NULL;
234 CtxEntryCache::CtxEntryCache()
244 m_Lock.Init(LOCK_COMCTXENTRYCACHE);
245 LockOwner lock = {&m_Lock, IsOwnerOfSpinLock};
248 CtxEntryCache::~CtxEntryCache()
258 for (SHash<CtxEntryCacheTraits>::Iterator it = m_CtxEntryHash.Begin(); it != m_CtxEntryHash.End(); it++)
260 CtxEntry *pCtxEntry = (CtxEntry *)*it;
262 LPVOID CtxCookie = pCtxEntry->GetCtxCookie();
263 m_CtxEntryHash.Remove(CtxCookie);
265 LOG((LF_INTEROP, LL_INFO100, "Leaked CtxEntry %8.8x with CtxCookie %8.8x, ref count %d\n", pCtxEntry, pCtxEntry->GetCtxCookie(), pCtxEntry->m_dwRefCount));
266 pCtxEntry->m_dwRefCount = 0;
272 void CtxEntryCache::Init()
280 // This should never be called more than once.
281 PRECONDITION(NULL == s_pCtxEntryCache);
285 // Allocate the one and only instance of the context entry cache.
286 s_pCtxEntryCache = new CtxEntryCache();
289 CtxEntryCache* CtxEntryCache::GetCtxEntryCache()
296 PRECONDITION(CheckPointer(s_pCtxEntryCache));
300 return s_pCtxEntryCache;
303 CtxEntry* CtxEntryCache::CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread)
313 CtxEntry * pCtxEntry = NULL;
314 Thread * pThread = GetThread();
316 // If we don't already have a context entry for the context cookie,
317 // we need to create one.
318 NewHolder<CtxEntry> pNewCtxEntry = new CtxEntry(pCtxCookie, pSTAThread);
319 // tiggers GC, can't happen when we hold spin lock
320 pNewCtxEntry->Init();
323 TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(&m_Lock);
325 // double check for race
326 pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
327 if (pCtxEntry == NULL)
329 // We successfully allocated and initialized the entry.
330 m_CtxEntryHash.Add(pNewCtxEntry);
331 pCtxEntry = pNewCtxEntry.Extract();
333 // We must have an entry now; we need to addref it before
334 // we leave the lock.
335 pCtxEntry->AddRef ();
341 CtxEntry* CtxEntryCache::FindCtxEntry(LPVOID pCtxCookie, Thread *pThread)
343 CtxEntry *pCtxEntry = NULL;
344 Thread *pSTAThread = NULL;
351 INJECT_FAULT(COMPlusThrowOM());
352 PRECONDITION(CheckPointer(pCtxCookie));
353 POSTCONDITION(CheckPointer(RETVAL));
354 POSTCONDITION(pCtxCookie == pCtxEntry->GetCtxCookie());
355 POSTCONDITION(pSTAThread == pCtxEntry->GetSTAThread());
359 // Find our STA (if any)
360 if (pThread->GetApartment() == Thread::AS_InSTA)
362 // We are in an STA thread. But we may be in a NA context, so do an extra
363 // check for that case.
366 // try the simple cache on Thread first
367 if (pCtxCookie != pThread->GetLastSTACtxCookie(&fNAContext))
370 fNAContext = (SUCCEEDED(GetCurrentApartmentTypeNT5((IObjectContext *)pCtxCookie, &type)) && type == APTTYPE_NA);
371 pThread->SetLastSTACtxCookie(pCtxCookie, fNAContext);
375 pSTAThread = pThread;
378 ASSERT (GetThread ());
381 ACQUIRE_SPINLOCK_NO_HOLDER(&m_Lock, pThread);
383 // Try to find a context entry for the context cookie.
384 pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
387 // We must have an entry now; we need to addref it before
388 // we leave the lock.
389 pCtxEntry->AddRef ();
393 RELEASE_SPINLOCK_NO_HOLDER(&m_Lock, pThread);
397 pCtxEntry = CreateCtxEntry(pCtxCookie, pSTAThread);
400 // Returned the found or allocated entry.
405 void CtxEntryCache::TryDeleteCtxEntry(LPVOID pCtxCookie)
412 PRECONDITION(CheckPointer(pCtxCookie));
416 BOOL bDelete = FALSE;
417 CtxEntry *pCtxEntry = NULL;
420 TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(&m_Lock);
422 // Try to find a context entry for the context cookie.
423 pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
426 // If the ref count of the context entry is still 0, then we can
427 // remove the ctx entry and delete it.
428 if (pCtxEntry->m_dwRefCount == 0)
430 // First remove the context entry from the list.
431 m_CtxEntryHash.Remove(pCtxCookie);
433 // We need to unlock the context entry cache before we delete the
434 // context entry since this can cause release to be called on
435 // an IP which can cause us to re-enter the runtime thus causing a
437 // We can now safely delete the context entry.
449 //================================================================
450 // Get the RCW associated with this IUnkEntry
451 // We assert inside Init that this IUnkEntry is indeed within a RCW
452 RCW *IUnkEntry::GetRCW()
454 LIMITED_METHOD_CONTRACT;
456 return (RCW *) (((LPBYTE) this) - offsetof(RCW, m_UnkEntry));
459 //================================================================
460 // Initialize the entry
461 void IUnkEntry::Init(
463 BOOL bIsFreeThreaded,
473 PRECONDITION(CheckPointer(pUnk));
474 INDEBUG(PRECONDITION(CheckPointer(pRCW));)
478 // Make sure this IUnkEntry is part of a RCW so that we can get back to the RCW through offset
480 _ASSERTE(((LPBYTE)pRCW) + offsetof(RCW, m_UnkEntry) == (LPBYTE) this);
482 // Find our context cookie
483 LPVOID pCtxCookie = GetCurrentCtxCookie();
484 _ASSERTE(pCtxCookie);
486 // Set up IUnkEntry's state.
490 m_pCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(pCtxCookie, pThread);
493 m_pCtxCookie = pCtxCookie;
496 // Sanity check this IUnkEntry.
497 CheckValidIUnkEntry();
500 //================================================================
501 // Release the interface pointer held by the IUnkEntry.
502 VOID IUnkEntry::ReleaseInterface(RCW *pRCW)
512 if (g_fProcessDetach)
514 // The Release call is unsafe if the process is going away (calls into
515 // DLLs we don't know are even mapped).
516 LogInteropLeak(this);
520 // now release the IUnknown that we hold
521 if ((m_pUnknown != 0) && (m_pUnknown != (IUnknown *)0xBADF00D))
523 ULONG cbRef = SafeReleasePreemp(m_pUnknown, pRCW);
524 LogInteropRelease(m_pUnknown, cbRef, "IUnkEntry::Free: Releasing the held ref");
527 // mark the entry as dead
528 m_pUnknown = (IUnknown *)0xBADF00D;
532 //================================================================
533 // Free the IUnknown entry. ReleaseInterface must have been called.
534 VOID IUnkEntry::Free()
541 PRECONDITION(g_fProcessDetach || m_pUnknown == (IUnknown *)0xBADF00D);
545 // Log the de-allocation of the IUnknown entry.
546 LOG((LF_INTEROP, LL_INFO10000, "IUnkEntry::Free called for context 0x%08X, to release entry with m_pUnknown %p, on thread %p\n", m_pCtxCookie, m_pUnknown, GetThread()));
548 if (g_fProcessDetach)
550 IStream *pOldStream = m_pStream;
551 if (InterlockedExchangeT(&m_pStream, NULL) == pOldStream)
552 SafeReleasePreemp(pOldStream);
556 IStream *pStream = m_pStream;
559 // This will release the stream, object in the stream and the memory on which the stream was created
561 SafeReleaseStream(pStream);
565 // Release the ref count we have on the CtxEntry.
566 CtxEntry *pEntry = GetCtxEntry();
574 //================================================================
575 // Get IUnknown for the current context from IUnkEntry
576 IUnknown* IUnkEntry::GetIUnknownForCurrContext(bool fNoAddRef)
583 POSTCONDITION(CheckPointer(RETVAL, (fNoAddRef ? NULL_OK : NULL_NOT_OK)));
587 IUnknown* pUnk = NULL;
588 LPVOID pCtxCookie = GetCurrentCtxCookie();
589 _ASSERTE(pCtxCookie);
591 CheckValidIUnkEntry();
593 if (m_pCtxCookie == pCtxCookie || IsFreeThreaded())
595 pUnk = GetRawIUnknown_NoAddRef();
599 RCW_VTABLEPTR(GetRCW());
600 ULONG cbRef = SafeAddRef(pUnk);
601 LogInteropAddRef(pUnk, cbRef, "IUnkEntry::GetIUnknownForCurrContext: Addref pUnk, passing ref to caller");
605 if (pUnk == NULL && !fNoAddRef)
606 pUnk = UnmarshalIUnknownForCurrContext();
611 //================================================================
612 // Unmarshal IUnknown for the current context from IUnkEntry
613 IUnknown* IUnkEntry::UnmarshalIUnknownForCurrContext()
620 PRECONDITION(!IsFreeThreaded());
621 POSTCONDITION(CheckPointer(RETVAL));
625 HRESULT hrCDH = S_OK;
626 IUnknown* pUnk = NULL;
628 BOOL fUnmarshalFailed = FALSE;
629 BOOL fCallHelper = FALSE;
631 CheckValidIUnkEntry();
633 _ASSERTE(GetCtxEntry() != NULL);
636 if(IsMarshalingInhibited() && (m_pCtxCookie != GetCurrentCtxCookie()))
638 // We want to use an interface in a different context but it can't be marshalled.
639 LOG((LF_INTEROP, LL_INFO100, "IUnkEntry::GetIUnknownForCurrContext failed as the COM object has inhibited marshaling"));
640 COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST_NOMARSHAL);
643 // Make sure we are in preemptive GC mode before we call out to COM.
646 // Need to synchronize
649 // Marshal the interface to the stream if it hasn't been done yet.
650 if (m_pStream == NULL)
652 // If context transition failed, we'll return a failure HRESULT
653 // Otherwise, we return S_OK but m_pStream will stay being NULL
654 hrCDH = MarshalIUnknownToStreamCallback(this);
655 CheckForFuncEvalAbort(hrCDH);
658 if (TryUpdateEntry())
660 // If the interface is not marshalable or if we failed to
661 // enter the context, then we don't have any choice but to
663 if (m_pStream == NULL)
665 // We retrieved an IP so stop retrying.
668 // Give out this IUnknown we are holding
669 pUnk = GetRawIUnknown_NoAddRef();
671 RCW_VTABLEPTR(GetRCW());
672 ULONG cbRef = SafeAddRefPreemp(pUnk);
674 LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
678 // we got control for this entry
679 // GetInterface for the current context
681 hr = CoUnmarshalInterface(m_pStream, IID_IUnknown, (void **)&pUnk);
683 // If the objref in the stream times out, we need to go an marshal into the
684 // stream once again.
689 CheckForFuncEvalAbort(hr);
691 // This should release the stream, object in the stream and the memory on which the stream was created
692 SafeReleaseStream(m_pStream);
695 // If unmarshal failed twice, then bail out.
696 if (fUnmarshalFailed)
700 // Handing out m_pUnknown in this case would be incorrect. We should fix other places that are doing the same thing in Dev10
701 // To minimize code changes, throwing E_NOINTERFACE instead
702 COMPlusThrowHR(E_NOINTERFACE);
705 // Remember we failed to unmarshal.
706 fUnmarshalFailed = TRUE;
710 // Reset the stream to the begining
714 m_pStream->Seek(li, STREAM_SEEK_SET, &li2);
716 // Marshal the interface into the stream with appropriate flags
717 hr = CoMarshalInterface(m_pStream,
718 IID_IUnknown, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
722 CheckForFuncEvalAbort(hr);
724 // The proxy is no longer valid. This sometimes manifests itself by
725 // a failure during re-marshaling it to the stream. When this happens,
726 // we need to release the the pUnk we extracted and the stream and try to
727 // re-create the stream. We don't want to release the stream data since
728 // we already extracted the proxy from the stream and released it.
729 RCW_VTABLEPTR(GetRCW());
730 SafeReleasePreemp(pUnk);
732 SafeReleasePreemp(m_pStream);
737 // Reset the stream to the begining
739 m_pStream->Seek(li, STREAM_SEEK_SET, &li2);
741 // We managed to unmarshal the IP from the stream, stop retrying.
747 // Done with the entry.
752 //================================================================
753 // We can potentially collide with the COM+ activity lock so spawn off
754 // another call that does its stream marshalling on the stack without
755 // the need to do locking.
763 // If we hit a collision earlier, spawn off helper that repeats this operation without locking.
764 pUnk = UnmarshalIUnknownForCurrContextHelper();
770 //================================================================
771 // Release the stream. This will force UnmarshalIUnknownForCurrContext to transition
772 // into the context that owns the IP and re-marshal it to the stream.
773 void IUnkEntry::ReleaseStream()
783 // This should release the stream, object in the stream and the memory on which the stream was created
786 SafeReleaseStream(m_pStream);
791 // Indicates if the COM component being wrapped by the IUnkEntry aggregates the FTM
792 bool IUnkEntry::IsFreeThreaded()
794 LIMITED_METHOD_CONTRACT;
795 return GetRCW()->IsFreeThreaded();
798 // Indicates if the COM component being wrapped by the IUnkEntry implements INoMashal.
799 bool IUnkEntry::IsMarshalingInhibited()
801 LIMITED_METHOD_CONTRACT;
802 return GetRCW()->IsMarshalingInhibited();
805 // Helper function to marshal the IUnknown pointer to the stream.
806 static HRESULT MarshalIUnknownToStreamHelper(IUnknown * pUnknown, IStream ** ppStream)
816 IStream *pStream = NULL;
820 // ensure we register this cookie
821 HRESULT hr = wCoMarshalInterThreadInterfaceInStream(IID_IUnknown, pUnknown, &pStream);
823 if ((hr == REGDB_E_IIDNOTREG) ||
825 (hr == E_NOINTERFACE) ||
826 (hr == E_INVALIDARG) ||
827 (hr == E_UNEXPECTED))
829 // Interface is not marshallable.
839 //================================================================
840 struct StreamMarshalData
842 IUnkEntry * m_pUnkEntry;
845 // Fix for if the lock is held
846 HRESULT IUnkEntry::MarshalIUnknownToStreamCallback2(LPVOID pData)
853 PRECONDITION(CheckPointer(pData));
854 PRECONDITION(g_fProcessDetach == FALSE);
859 StreamMarshalData *psmd = reinterpret_cast<StreamMarshalData *>(pData);
861 // This should never be called during process detach.
863 hr = psmd->m_pUnkEntry->HRCheckValidIUnkEntry();
866 // Interface not marshallable
867 // We'll know marshaling failed because m_pStream == NULL
871 LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
872 _ASSERTE(pCurrentCtxCookie);
874 if (pCurrentCtxCookie == psmd->m_pUnkEntry->m_pCtxCookie)
876 // We are in the right context marshal the IUnknown to the
878 hr = MarshalIUnknownToStreamHelper(psmd->m_pUnkEntry->m_pUnknown, &psmd->m_pStream);
882 // Transition into the context to marshal the IUnknown to
884 _ASSERTE(psmd->m_pUnkEntry->GetCtxEntry() != NULL);
885 hr = psmd->m_pUnkEntry->GetCtxEntry()->EnterContext(MarshalIUnknownToStreamCallback2, psmd);
891 //================================================================
892 // Unmarshal IUnknown for the current context if the lock is held
893 IUnknown* IUnkEntry::UnmarshalIUnknownForCurrContextHelper()
900 PRECONDITION(!IsFreeThreaded());
901 POSTCONDITION(CheckPointer(RETVAL));
905 HRESULT hrCDH = S_OK;
906 IUnknown * pUnk = NULL;
907 SafeComHolder<IStream> spStream;
909 CheckValidIUnkEntry();
911 // Make sure we are in preemptive GC mode before we call out to COM.
914 // Marshal the interface to the stream. Any call to this function
915 // would be from another apartment so marshalling is needed.
916 StreamMarshalData smd = {this, NULL};
918 // If context transition failed, we'll return a failure HRESULT
919 // Otherwise, we return S_OK but m_pStream will stay being NULL
920 hrCDH = MarshalIUnknownToStreamCallback2(&smd);
922 spStream = smd.m_pStream;
923 smd.m_pStream = NULL;
925 CheckForFuncEvalAbort(hrCDH);
927 // If the interface is not marshalable or if we failed to
928 // enter the context, then we don't have any choice but to
930 if (spStream == NULL)
932 // Give out this IUnknown we are holding
933 pUnk = GetRawIUnknown_NoAddRef();
935 RCW_VTABLEPTR(GetRCW());
936 ULONG cbRef = SafeAddRefPreemp(pUnk);
938 LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
942 // we got control for this entry
943 // GetInterface for the current context
945 hr = CoUnmarshalInterface(spStream, IID_IUnknown, reinterpret_cast<void**>(&pUnk));
950 CheckForFuncEvalAbort(hr);
952 // Give out this IUnknown we are holding
953 pUnk = GetRawIUnknown_NoAddRef();
955 RCW_VTABLEPTR(GetRCW());
956 ULONG cbRef = SafeAddRefPreemp(pUnk);
958 LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
965 //================================================================
966 // Callback called to marshal the IUnknown into a stream lazily.
967 HRESULT IUnkEntry::MarshalIUnknownToStreamCallback(LPVOID pData)
974 PRECONDITION(CheckPointer(pData));
975 PRECONDITION(g_fProcessDetach == FALSE);
980 IUnkEntry *pUnkEntry = (IUnkEntry *)pData;
982 // This should never be called during process detach.
984 hr = pUnkEntry->HRCheckValidIUnkEntry();
987 // Interface not marshallable
988 // We'll know marshaling failed because m_pStream == NULL
992 LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
993 _ASSERTE(pCurrentCtxCookie);
995 if (pCurrentCtxCookie == pUnkEntry->m_pCtxCookie)
997 // We are in the right context marshal the IUnknown to the
999 hr = pUnkEntry->MarshalIUnknownToStream();
1003 _ASSERTE(pUnkEntry->GetCtxEntry() != NULL);
1005 // Transition into the context to marshal the IUnknown to
1007 hr = pUnkEntry->GetCtxEntry()->EnterContext(MarshalIUnknownToStreamCallback, pUnkEntry);
1013 //================================================================
1014 // Helper function to determine if a COM component aggregates the
1016 bool IUnkEntry::IsComponentFreeThreaded(IUnknown *pUnk)
1023 PRECONDITION(CheckPointer(pUnk));
1027 // First see if the object implements the IAgileObject marker interface
1028 SafeComHolderPreemp<IAgileObject> pAgileObject;
1029 HRESULT hr = SafeQueryInterfacePreemp(pUnk, IID_IAgileObject, (IUnknown**)&pAgileObject);
1030 LogInteropQI(pUnk, IID_IAgileObject, hr, "IUnkEntry::IsComponentFreeThreaded: QI for IAgileObject");
1038 SafeComHolderPreemp<IMarshal> pMarshal = NULL;
1040 // If not, then we can try to determine if the component aggregates the FTM via IMarshal.
1041 hr = SafeQueryInterfacePreemp(pUnk, IID_IMarshal, (IUnknown **)&pMarshal);
1042 LogInteropQI(pUnk, IID_IMarshal, hr, "IUnkEntry::IsComponentFreeThreaded: QI for IMarshal");
1047 // The COM component implements IMarshal so we now check to see if the un-marshal class
1048 // for this IMarshal is the FTM's un-marshaler.
1049 hr = pMarshal->GetUnmarshalClass(IID_IUnknown, NULL, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL, &clsid);
1050 if (SUCCEEDED(hr) && clsid == CLSID_InProcFreeMarshaler)
1052 // The un-marshaler is indeed the unmarshaler for the FTM so this object
1053 // is free threaded.
1062 //================================================================
1063 // Helper function to marshal the IUnknown pointer to the stream.
1064 HRESULT IUnkEntry::MarshalIUnknownToStream()
1072 // This must always be called in the right context.
1073 PRECONDITION(m_pCtxCookie == GetCurrentCtxCookie());
1077 IStream *pStream = NULL;
1083 // ensure we register this cookie
1084 IUnknown *pUnk = m_pUnknown;
1085 if (pUnk == (IUnknown *)0xBADF00D)
1087 hr = COR_E_INVALIDCOMOBJECT;
1091 hr = wCoMarshalInterThreadInterfaceInStream(IID_IUnknown, pUnk, &pStream);
1093 if ((hr == REGDB_E_IIDNOTREG) ||
1095 (hr == E_NOINTERFACE) ||
1096 (hr == E_INVALIDARG) ||
1097 (hr == E_UNEXPECTED))
1099 // Interface is not marshallable.
1105 // Try to set the stream in the IUnkEntry. If another thread already set it,
1106 // then we need to release the stream we just set up.
1107 if (FastInterlockCompareExchangePointer(&m_pStream, pStream, NULL) != NULL)
1108 SafeReleaseStream(pStream);
1114 // Method to try to start updating the entry.
1115 bool IUnkEntry::TryUpdateEntry()
1117 WRAPPER_NO_CONTRACT;
1119 CtxEntry *pOldEntry = m_pCtxEntry;
1120 if (((DWORD_PTR)pOldEntry & 1) == 0)
1122 CtxEntry *pNewEntry = (CtxEntry *)((DWORD_PTR)pOldEntry | 1);
1123 return (InterlockedExchangeT(&m_pCtxEntry, pNewEntry) == pOldEntry);
1128 // Method to end updating the entry.
1129 VOID IUnkEntry::EndUpdateEntry()
1131 LIMITED_METHOD_CONTRACT;
1133 CtxEntry *pOldEntry = m_pCtxEntry;
1135 // we should hold the lock
1136 _ASSERTE(((DWORD_PTR)pOldEntry & 1) == 1);
1138 CtxEntry *pNewEntry = (CtxEntry *)((DWORD_PTR)pOldEntry & ~1);
1140 // and it's us who resets the bit
1141 VERIFY(InterlockedExchangeT(&m_pCtxEntry, pNewEntry) == pOldEntry);
1145 // Initialize the entry, returns true on success (i.e. the entry was free).
1146 bool InterfaceEntry::Init(MethodTable* pMT, IUnknown* pUnk)
1156 // It is important the fields be set in this order.
1157 if (InterlockedCompareExchangeT(&m_pUnknown, pUnk, NULL) == NULL)
1159 m_pMT = (IE_METHODTABLE_PTR)pMT;
1165 // Helper to determine if the entry is free.
1166 BOOL InterfaceEntry::IsFree()
1168 LIMITED_METHOD_CONTRACT;
1169 return m_pUnknown.Load() == NULL;
1172 void InterfaceEntry::Free()
1174 LIMITED_METHOD_CONTRACT;
1176 // We use the m_pUnknown field to synchronize access to the entry so that's the only
1177 // one we need to reset. After all, the set of interfaces that the object is known to
1178 // support is one of the most important debugging cues so let's keep m_pMT intact.
1179 m_pUnknown.Store(NULL);
1182 //================================================================
1183 // Constructor for the context entry.
1184 CtxEntry::CtxEntry(LPVOID pCtxCookie, Thread *pSTAThread)
1185 : m_pCtxCookie(pCtxCookie)
1188 , m_pSTAThread(pSTAThread)
1190 WRAPPER_NO_CONTRACT;
1193 //================================================================
1194 // Destructor for the context entry.
1195 CtxEntry::~CtxEntry()
1202 PRECONDITION(m_dwRefCount == 0);
1206 // If the context is a valid context then release it.
1207 if (m_pObjCtx && !g_fProcessDetach)
1209 SafeRelease(m_pObjCtx);
1213 // Set the context cookie to 0xBADF00D to indicate the current context
1214 // has been deleted.
1215 m_pCtxCookie = (LPVOID)0xBADF00D;
1218 //================================================================
1219 // Initialization method for the context entry.
1220 VOID CtxEntry::Init()
1227 INJECT_FAULT(COMPlusThrowOM());
1229 // Make sure COM has been started
1230 PRECONDITION(g_fComStarted == TRUE);
1234 // Retrieve the IObjectContext.
1235 HRESULT hr = GetCurrentObjCtx(&m_pObjCtx);
1237 // In case the call to GetCurrentObjCtx fails (which should never really happen)
1238 // we will throw an exception.
1244 // Add a reference to the CtxEntry.
1245 DWORD CtxEntry::AddRef()
1255 ULONG cbRef = FastInterlockIncrement((LONG*)&m_dwRefCount);
1256 LOG((LF_INTEROP, LL_INFO100, "CtxEntry::Addref %8.8x with %d\n", this, cbRef));
1261 //================================================================
1262 // Method to decrement the ref count of the context entry.
1263 DWORD CtxEntry::Release()
1270 PRECONDITION(m_dwRefCount > 0);
1274 LPVOID pCtxCookie = m_pCtxCookie;
1276 LONG cbRef = FastInterlockDecrement((LONG*)&m_dwRefCount);
1277 LOG((LF_INTEROP, LL_INFO100, "CtxEntry::Release %8.8x with %d\n", this, cbRef));
1279 // If the ref count falls to 0, try and delete the ctx entry.
1280 // This might not end up deleting it if another thread tries to
1281 // retrieve this ctx entry at the same time this one tries
1284 CtxEntryCache::GetCtxEntryCache()->TryDeleteCtxEntry(pCtxCookie);
1286 // WARNING: The this pointer cannot be used at this point.
1290 //================================================================
1291 // Method to transition into the context and call the callback
1292 // from within the context.
1293 HRESULT CtxEntry::EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData)
1301 PRECONDITION(CheckPointer(pCallbackFunc));
1302 PRECONDITION(CheckPointer(pData));
1303 // This should not be called if the this context is the current context.
1304 PRECONDITION(m_pCtxCookie != GetCurrentCtxCookie());
1310 // If we are in process detach, we cannot safely try to enter another context
1311 // since we don't know if OLE32 is still loaded.
1312 if (g_fProcessDetach)
1314 LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08%x has failed since we are in process detach\n", m_pCtxCookie));
1315 return RPC_E_DISCONNECTED;
1318 // Make sure we are in preemptive GC mode before we call out to COM.
1321 // Prepare the information struct passed into the callback.
1322 CtxEntryEnterContextCallbackData CallbackInfo;
1323 CallbackInfo.m_pUserCallbackFunc = pCallbackFunc;
1324 CallbackInfo.m_pUserData = pData;
1325 CallbackInfo.m_pCtxCookie = m_pCtxCookie;
1326 CallbackInfo.m_UserCallbackHR = E_FAIL;
1328 // Retrieve the IContextCallback interface from the IObjectContext.
1329 SafeComHolderPreemp<IContextCallback> pCallback;
1330 hr = SafeQueryInterfacePreemp(m_pObjCtx, IID_IContextCallback, (IUnknown**)&pCallback);
1331 LogInteropQI(m_pObjCtx, IID_IContextCallback, hr, "QI for IID_IContextCallback");
1332 _ASSERTE(SUCCEEDED(hr) && pCallback);
1334 // Setup the callback data structure with the callback Args
1335 ComCallData callBackData;
1336 callBackData.dwDispid = 0;
1337 callBackData.dwReserved = 0;
1338 callBackData.pUserDefined = &CallbackInfo;
1342 hr = ((IContextCallback*)pCallback)->ContextCallback(EnterContextCallback, &callBackData, IID_IEnterActivityWithNoLock, 2, NULL);
1346 hr = GET_EXCEPTION()->GetHR();
1348 EX_END_CATCH(SwallowAllExceptions);
1352 // If the transition failed because of an aborted func eval, simply propagate
1353 // the HRESULT/IErrorInfo back to the caller as we cannot throw here.
1354 SafeComHolder<IErrorInfo> pErrorInfo = CheckForFuncEvalAbortNoThrow(hr);
1355 if (pErrorInfo != NULL)
1357 LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08X has failed since the debugger is blocking it\n", m_pCtxCookie));
1359 // put the IErrorInfo back
1360 SetErrorInfo(0, pErrorInfo);
1364 // The context is disconnected so we cannot transition into it.
1365 LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08X has failed since the context has disconnected\n", m_pCtxCookie));
1373 //================================================================
1374 // Callback function called by DoCallback.
1375 HRESULT __stdcall CtxEntry::EnterContextCallback(ComCallData* pComCallData)
1382 PRECONDITION(CheckPointer(pComCallData));
1386 // Retrieve the callback data.
1387 CtxEntryEnterContextCallbackData *pData = (CtxEntryEnterContextCallbackData*)pComCallData->pUserDefined;
1390 Thread *pThread = GetThread();
1392 // Make sure the thread has been set before we call the user callback function.
1395 // huh! we are in the middle of shutdown
1396 // and there is no way we can add a new thread
1397 // so let us just return RPC_E_DISCONNECTED
1398 // look at the pCallBack->DoCallback above
1399 // to see why we are returning this SCODE
1401 return RPC_E_DISCONNECTED;
1403 // Otherwise, we need to create a managed thread object for this new thread
1407 pThread = SetupThreadNoThrow(&hr);
1408 if (pThread == NULL)
1413 // at this point we should be in the right context on NT4,
1414 // if not then it is possible that the actual apartment state for this
1415 // thread has changed and we have stale info in our thread or the CtxEntry
1416 LPVOID pCtxCookie = GetCurrentCtxCookie();
1417 _ASSERTE(pCtxCookie);
1418 if (pData->m_pCtxCookie != pCtxCookie)
1419 return RPC_E_DISCONNECTED;
1421 // Call the user callback function and store the return value the
1423 pData->m_UserCallbackHR = pData->m_pUserCallbackFunc(pData->m_pUserData);
1425 // Return S_OK to indicate the context transition was successfull.