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.
8 // Classes/Structures used to represent and store info on COM interfaces and contexts.
14 #ifndef FEATURE_COMINTEROP
15 #error FEATURE_COMINTEROP is required for this file
16 #endif // FEATURE_COMINTEROP
21 //================================================================
22 // Forward declarations.
27 //================================================================
29 HRESULT wCoMarshalInterThreadInterfaceInStream(REFIID riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);
30 STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf);
33 typedef DPTR(CtxEntry) PTR_CtxEntry;
35 //==============================================================
36 // An entry representing a COM+ 1.0 context or an appartment.
39 // The CtxEntryCache needs to be able to see the internals
43 // NewHolder<CtxEntry> needs to be able to call the destructor of CtxEntry.
44 // DISABLE Warning C4396, the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
45 #pragma warning(push) // store original warning levels
46 #pragma warning(disable: 4396)
47 friend void Delete<CtxEntry>(CtxEntry *);
48 #pragma warning(pop) // restore original warning levels
52 // Disallow creation and deletion of the CtxEntries.
53 CtxEntry(LPVOID pCtxCookie, Thread* pSTAThread);
56 // Initialization method called from the CtxEntryCache.
60 // Add a reference to the CtxEntry.
63 // Release a reference to the CtxEntry.
66 // Function to enter the context. The specified callback function will
67 // be called from within the context.
68 HRESULT EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData);
70 // Accessor for the context cookie.
73 LIMITED_METHOD_CONTRACT;
77 // Accessor for the STA thread.
78 Thread* GetSTAThread()
80 LIMITED_METHOD_CONTRACT;
85 // Callback function called by DoCallback.
86 static HRESULT __stdcall EnterContextCallback(ComCallData* pData);
88 LPVOID m_pCtxCookie; // The OPAQUE context cookie.
89 IUnknown* m_pObjCtx; // The object context interface.
90 DWORD m_dwRefCount; // The ref count.
91 Thread* m_pSTAThread; // STA thread associated with the context, if any
94 //==============================================================
95 // IUnkEntry: represent a single COM component
98 // The context entry needs to be a friend to be able to call InitSpecial.
100 // RCW need to access IUnkEntry
104 // Does not throw if m_pUnknown is no longer valid, debug only.
105 IUnknown *GetRawIUnknown_NoAddRef_NoThrow()
107 LIMITED_METHOD_CONTRACT;
108 _ASSERTE(m_pUnknown != NULL && m_pUnknown != (IUnknown*)0xBADF00D);
114 IUnknown *GetRawIUnknown_NoAddRef()
123 IUnknown *pUnk = m_pUnknown;
124 #ifndef DACCESS_COMPILE
125 if (pUnk == (IUnknown *)0xBADF00D)
127 // All callers of this method had checked the pUnk before so this must be a race.
128 COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_RELEASE_RACE);
130 #endif // !DACCESS_COMPILE
135 LPVOID GetCtxCookie()
137 LIMITED_METHOD_CONTRACT;
142 // Is the RCW disconnected from its COM object?
143 inline bool IsDisconnected()
145 LIMITED_METHOD_CONTRACT;
146 return (m_pUnknown == (IUnknown*)0xBADF00D ||
147 (GetCtxEntry() != NULL && m_pCtxCookie != GetCtxEntry()->GetCtxCookie()));
152 // Initialize the entry, returns true if we are in an STA.
153 // We assert inside Init that this IUnkEntry is indeed within a RCW
154 void Init(IUnknown* pUnk, BOOL bIsFreeThreaded, Thread *pThread DEBUGARG(RCW *pRCW));
156 // Release the interface pointer held by the IUnkEntry.
157 VOID ReleaseInterface(RCW *pRCW);
159 // Free the IUnknown entry. ReleaseInterface must have been called.
162 // Get the RCW associated with this IUnkEntry
163 // We assert inside Init that this IUnkEntry is indeed within a RCW
166 // Get IUnknown for the current context from IUnkEntry
167 IUnknown* GetIUnknownForCurrContext(bool fNoAddRef);
169 // Unmarshal IUnknown for the current context from IUnkEntry
170 IUnknown* UnmarshalIUnknownForCurrContext();
172 // Release the stream. This will force UnmarshalIUnknownForCurrContext to transition
173 // into the context that owns the IP and re-marshal it to the stream.
174 void ReleaseStream();
176 // Indicates if the COM component being wrapped by the IUnkEntry aggregates the FTM
177 inline bool IsFreeThreaded();
179 // Indicates if the COM component being wrapped by the IUnkEntry implements INoMashal.
180 inline bool IsMarshalingInhibited();
182 VOID CheckValidIUnkEntry();
184 HRESULT HRCheckValidIUnkEntry();
186 // Unmarshal IUnknown for the current context if the lock is held
187 IUnknown* UnmarshalIUnknownForCurrContextHelper();
189 // Fix for if the lock is held that works on a stack allocated stream
190 // instead of the member variable stream
191 static HRESULT MarshalIUnknownToStreamCallback2(LPVOID pData);
193 // Callback called to marshal the IUnknown into a stream lazily.
194 static HRESULT MarshalIUnknownToStreamCallback(LPVOID pData);
196 // Helper function called from MarshalIUnknownToStreamCallback.
197 HRESULT MarshalIUnknownToStream();
199 // Method to try and start updating the the entry.
200 bool TryUpdateEntry();
202 // Method to end updating the entry.
203 VOID EndUpdateEntry();
205 // Helper function to determine if a COM component aggregates the FTM.
206 static bool IsComponentFreeThreaded(IUnknown *pUnk);
208 inline PTR_CtxEntry GetCtxEntry()
210 LIMITED_METHOD_DAC_CONTRACT;
212 PTR_CtxEntry pCtxEntry = dac_cast<PTR_CtxEntry>(dac_cast<TADDR>(m_pCtxEntry) & ~1);
217 // Context cookie at the point where we acquired the interface pointer
220 // Context entry representing the context where we acquired the interface pointer.
221 // We use the lowest bit for synchronization and we rely on the fact that the
222 // context itself (the rest of the bits) does not change throughout the lifetime
224 PTR_CtxEntry m_pCtxEntry;
226 // IUnknown interface
227 IUnknown* m_pUnknown;
229 // IStream used for marshalling
233 // Don't use this directly as the methodtable could have been released
235 typedef MethodTable* IE_METHODTABLE_PTR;
237 //==============================================================
238 // Interface Entry represents a single COM IP
239 struct InterfaceEntry
241 // Initialize the entry, returns true on success (i.e. the entry was free).
242 bool Init(MethodTable* pMT, IUnknown* pUnk);
244 // Helper to determine if the entry is free.
247 // Mark the entry as free.
250 // Member of the entry. These must be volatile so the compiler
251 // will not try and optimize reads and writes to them.
252 Volatile<IE_METHODTABLE_PTR> m_pMT; // Interface asked for
253 Volatile<IUnknown*> m_pUnknown; // Result of query
256 class CtxEntryCacheTraits : public DefaultSHashTraits<CtxEntry *>
259 typedef LPVOID key_t;
260 static CtxEntry *Null() { LIMITED_METHOD_CONTRACT; return NULL; }
261 static bool IsNull(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return (e == NULL); }
262 static const LPVOID GetKey(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return e->GetCtxCookie(); }
263 static count_t Hash(LPVOID key_t) { LIMITED_METHOD_CONTRACT; return (count_t) key_t; }
264 static BOOL Equals(LPVOID lhs, LPVOID rhs) { LIMITED_METHOD_CONTRACT; return (lhs == rhs); }
265 static CtxEntry *Deleted() { LIMITED_METHOD_CONTRACT; return (CtxEntry *)-1; }
266 static bool IsDeleted(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return e == (CtxEntry *)-1; }
269 //==============================================================
270 // The cache of context entries.
273 // The CtxEntry needs to be able to call some of the private
274 // method of the CtxEntryCache.
278 // Disallow creation and deletion of the CtxEntryCache.
283 // Static initialization routine for the CtxEntryCache.
286 // Static accessor for the one and only instance of the CtxEntryCache.
287 static CtxEntryCache *GetCtxEntryCache();
289 // Method to retrieve/create a CtxEntry for the specified context cookie.
290 CtxEntry *FindCtxEntry(LPVOID pCtxCookie, Thread *pSTAThread);
293 CtxEntry * CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread);
295 // Helper function called from the CtxEntry.
296 void TryDeleteCtxEntry(LPVOID pCtxCookie);
298 SHash<CtxEntryCacheTraits> m_CtxEntryHash;
300 // spin lock for fast synchronization
303 // The one and only instance for the context entry cache.
304 static CtxEntryCache* s_pCtxEntryCache;