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.
4
5
6#include "common.h"
7#include "vars.hpp"
8#include "excep.h"
9#include "interoputil.h"
10#include "interopconverter.h"
11#include "olevariant.h"
12#include "comcallablewrapper.h"
13
14#ifdef FEATURE_COMINTEROP
15
16#include "stdinterfaces.h"
17#include "runtimecallablewrapper.h"
18#include "cominterfacemarshaler.h"
19#include "mdaassistants.h"
20#include "binder.h"
21#include "winrttypenameconverter.h"
22#include "typestring.h"
23
24//--------------------------------------------------------------------------------
25// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, MethodTable *pMT, ...)
26// Convert ObjectRef to a COM IP, based on MethodTable* pMT.
27IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, MethodTable *pMT, BOOL bSecurityCheck, BOOL bEnableCustomizedQueryInterface)
28{
29 CONTRACT (IUnknown*)
30 {
31 THROWS;
32 GC_TRIGGERS;
33 MODE_COOPERATIVE;
34 PRECONDITION(CheckPointer(poref));
35 PRECONDITION(CheckPointer(pMT));
36 PRECONDITION(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!");
37 POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL) : CheckPointer(RETVAL, NULL_OK));
38 }
39 CONTRACT_END;
40
41 BOOL fReleaseWrapper = false;
42 HRESULT hr = E_NOINTERFACE;
43 SafeComHolder<IUnknown> pUnk = NULL;
44 size_t ul = 0;
45
46 if (*poref == NULL)
47 RETURN NULL;
48
49
50 SyncBlock* pBlock = (*poref)->GetSyncBlock();
51
52 InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo();
53
54 // If we have a CCW, or a NULL CCW but the RCW field was never used,
55 // get the pUnk from the ComCallWrapper, otherwise from the RCW
56 if ((NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed()))
57 {
58 CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref);
59
60 GetComIPFromCCW::flags flags = GetComIPFromCCW::None;
61 if (!bSecurityCheck) { flags |= GetComIPFromCCW::SuppressSecurityCheck; }
62 if (!bEnableCustomizedQueryInterface) { flags |= GetComIPFromCCW::SuppressCustomizedQueryInterface; }
63
64 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, GUID_NULL, pMT, flags);
65 }
66 else
67 {
68 RCWHolder pRCW(GetThread());
69 RCWPROTECT_BEGIN(pRCW, pBlock);
70
71 // The interface will be returned addref'ed.
72 pUnk = pRCW->GetComIPFromRCW(pMT);
73
74 RCWPROTECT_END(pRCW);
75 }
76
77 // If we failed to retrieve an IP then throw an exception.
78 if (pUnk == NULL)
79 COMPlusThrowHR(hr);
80
81 pUnk.SuppressRelease();
82 RETURN pUnk;
83}
84
85
86//--------------------------------------------------------------------------------
87// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, ComIpType ReqIpType, ComIpType *pFetchedIpType);
88// Convert ObjectRef to a COM IP of the requested type.
89IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, ComIpType ReqIpType, ComIpType *pFetchedIpType)
90{
91 CONTRACT (IUnknown*)
92 {
93 THROWS;
94 GC_TRIGGERS;
95 MODE_COOPERATIVE;
96 PRECONDITION((ReqIpType & (ComIpType_Dispatch | ComIpType_Unknown | ComIpType_Inspectable)) != 0);
97 PRECONDITION(CheckPointer(poref));
98 PRECONDITION(ReqIpType != 0);
99 POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL) : CheckPointer(RETVAL, NULL_OK));
100 }
101 CONTRACT_END;
102
103 // COM had better be started up at this point.
104 _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!");
105
106 BOOL fReleaseWrapper = false;
107 HRESULT hr = E_NOINTERFACE;
108 IUnknown* pUnk = NULL;
109 size_t ul = 0;
110 ComIpType FetchedIpType = ComIpType_None;
111
112 if (*poref == NULL)
113 RETURN NULL;
114
115 MethodTable *pMT = (*poref)->GetMethodTable();
116
117 SyncBlock* pBlock = (*poref)->GetSyncBlock();
118
119 InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo();
120
121 if ( (NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed()) )
122 {
123 CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref);
124
125 // If the user requested IDispatch, then check for IDispatch first.
126 if (ReqIpType & ComIpType_Dispatch)
127 {
128 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IDispatch, NULL);
129 if (pUnk)
130 FetchedIpType = ComIpType_Dispatch;
131 }
132
133 if (ReqIpType & ComIpType_Inspectable)
134 {
135 WinMDAdapter::RedirectedTypeIndex redirectedTypeIndex;
136
137 MethodTable * pMT = (*poref)->GetMethodTable();
138
139 //
140 // Check whether this object is of a legal WinRT type (including array)
141 //
142 // Note that System.RuntimeType is a weird case - we only redirect System.Type at type
143 // level, but when we boxing the actual instance, we expect it to be a System.RuntimeType
144 // instance, which is not redirected and not a legal WinRT type
145 //
146 // Therefore, special case for System.RuntimeType and treat it as a legal WinRT type
147 // only for boxing
148 //
149 if (pMT->IsLegalWinRTType(poref) ||
150 MscorlibBinder::IsClass(pMT, CLASS__CLASS))
151 {
152 // The managed signature contains Object, and native signature is IInspectable.
153 // "Box" value types by allocating an IReference<T> and storing them inside it.
154 // Similarly, String must be an IReference<HSTRING>. Delegates get wrapped too.
155 // Arrays must be stored in an IReferenceArray<T>.
156 // System.Type is in fact internal type System.RuntimeType (CLASS__CLASS) that inherits from it.
157 // Note: We do not allow System.ReflectionOnlyType that inherits from System.RuntimeType.
158 // KeyValuePair`2 must be exposed as CLRIKeyValuePair.
159 if (pMT->HasInstantiation() && pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC)))
160 {
161 TypeHandle th = TypeHandle(MscorlibBinder::GetClass(CLASS__CLRIKEYVALUEPAIRIMPL)).Instantiate(pMT->GetInstantiation());
162
163 MethodDesc *method = MethodDesc::FindOrCreateAssociatedMethodDesc(
164 MscorlibBinder::GetMethod(METHOD__CLRIKEYVALUEPAIRIMPL__BOXHELPER),
165 th.GetMethodTable(),
166 FALSE,
167 Instantiation(),
168 FALSE);
169 _ASSERTE(method != NULL);
170
171 MethodDescCallSite boxHelper(method);
172
173 ARG_SLOT Args[] =
174 {
175 ObjToArgSlot(*poref),
176 };
177 OBJECTREF orCLRKeyValuePair = boxHelper.Call_RetOBJECTREF(Args);
178
179 GCPROTECT_BEGIN(orCLRKeyValuePair);
180 CCWHolder pCCWHoldBoxed = ComCallWrapper::InlineGetWrapper(&orCLRKeyValuePair);
181 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHoldBoxed, IID_IInspectable, NULL);
182 GCPROTECT_END();
183 }
184 else if ((pMT->IsValueType() ||
185 pMT->IsStringOrArray() ||
186 pMT->IsDelegate() ||
187 MscorlibBinder::IsClass(pMT, CLASS__CLASS)))
188 {
189 OBJECTREF orBoxedIReference = NULL;
190 MethodDescCallSite createIReference(METHOD__FACTORYFORIREFERENCE__CREATE_IREFERENCE);
191
192 ARG_SLOT Args[] =
193 {
194 ObjToArgSlot(*poref),
195 };
196
197 // Call FactoryForIReference::CreateIReference(Object) for an IReference<T> or IReferenceArray<T>.
198 orBoxedIReference = createIReference.Call_RetOBJECTREF(Args);
199
200 GCPROTECT_BEGIN(orBoxedIReference);
201 CCWHolder pCCWHoldBoxed = ComCallWrapper::InlineGetWrapper(&orBoxedIReference);
202 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHoldBoxed, IID_IInspectable, NULL);
203 GCPROTECT_END();
204 }
205 else if (WinRTTypeNameConverter::ResolveRedirectedType(pMT, &redirectedTypeIndex))
206 {
207 // This is a redirected type - see if we need to manually marshal it
208 if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Uri)
209 {
210 UriMarshalingInfo *pUriMarshalInfo = GetAppDomain()->GetMarshalingData()->GetUriMarshalingInfo();
211 struct
212 {
213 OBJECTREF ref;
214 STRINGREF refRawUri;
215 }
216 gc;
217 ZeroMemory(&gc, sizeof(gc));
218 GCPROTECT_BEGIN(gc);
219
220 gc.ref = *poref;
221
222 MethodDescCallSite getRawURI(pUriMarshalInfo->GetSystemUriOriginalStringMD());
223 ARG_SLOT getRawURIArgs[] =
224 {
225 ObjToArgSlot(gc.ref)
226 };
227
228 gc.refRawUri = (STRINGREF)getRawURI.Call_RetOBJECTREF(getRawURIArgs);
229
230 DWORD cchRawUri = gc.refRawUri->GetStringLength();
231 LPCWSTR wszRawUri = gc.refRawUri->GetBuffer();
232
233 {
234 GCX_PREEMP();
235 pUnk = CreateWinRTUri(wszRawUri, static_cast<INT32>(cchRawUri));
236 }
237
238 GCPROTECT_END();
239 }
240 else if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_NotifyCollectionChangedEventArgs ||
241 redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_ComponentModel_PropertyChangedEventArgs)
242 {
243 MethodDesc *pMD;
244 EventArgsMarshalingInfo *pInfo = GetAppDomain()->GetMarshalingData()->GetEventArgsMarshalingInfo();
245
246 if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_NotifyCollectionChangedEventArgs)
247 pMD = pInfo->GetSystemNCCEventArgsToWinRTNCCEventArgsMD();
248 else
249 pMD = pInfo->GetSystemPCEventArgsToWinRTPCEventArgsMD();
250
251 MethodDescCallSite marshalMethod(pMD);
252 ARG_SLOT methodArgs[] =
253 {
254 ObjToArgSlot(*poref)
255 };
256 pUnk = (IUnknown *)marshalMethod.Call_RetLPVOID(methodArgs);
257 }
258 else
259 {
260 _ASSERTE(!W("Unexpected redirected type seen in GetComIPFromObjectRef"));
261 }
262 }
263 else
264 {
265 //
266 // WinRT reference type - marshal as IInspectable
267 //
268 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IInspectable, /* pIntfMT = */ NULL);
269 }
270 }
271 else
272 {
273 //
274 // Marshal non-WinRT types as IInspectable* to enable round-tripping (for example, TextBox.Tag property)
275 // By default, this returns ICustomPropertyProvider;
276 //
277 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IInspectable, /* pIntfMT = */ NULL);
278 }
279
280 if (pUnk)
281 FetchedIpType = ComIpType_Inspectable;
282 }
283
284 // If the ObjectRef doesn't support IDispatch and the caller also accepts
285 // an IUnknown pointer, then check for IUnknown.
286 if (!pUnk && (ReqIpType & ComIpType_Unknown))
287 {
288 if (ReqIpType & ComIpType_OuterUnknown)
289 {
290 // check if the object is aggregated
291 SimpleComCallWrapper* pSimpleWrap = pCCWHold->GetSimpleWrapper();
292 if (pSimpleWrap)
293 {
294 pUnk = pSimpleWrap->GetOuter();
295 if (pUnk)
296 SafeAddRef(pUnk);
297 }
298 }
299 if (!pUnk)
300 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IUnknown, NULL);
301 if (pUnk)
302 FetchedIpType = ComIpType_Unknown;
303 }
304 }
305 else
306 {
307 RCWHolder pRCW(GetThread());
308
309 // This code is hot, use a simple RCWHolder check (i.e. don't increment the use count on the RCW).
310 // @TODO: Cache IInspectable & IDispatch so we don't have to QI every time we come here.
311 pRCW.InitFastCheck(pBlock);
312
313 // If the user requested IDispatch, then check for IDispatch first.
314 if (ReqIpType & ComIpType_Dispatch)
315 {
316 pUnk = pRCW->GetIDispatch();
317 if (pUnk)
318 FetchedIpType = ComIpType_Dispatch;
319 }
320
321 if (ReqIpType & ComIpType_Inspectable)
322 {
323 pUnk = pRCW->GetIInspectable();
324 if (pUnk)
325 FetchedIpType = ComIpType_Inspectable;
326 }
327
328 // If the ObjectRef doesn't support IDispatch and the caller also accepts
329 // an IUnknown pointer, then check for IUnknown.
330 if (!pUnk && (ReqIpType & ComIpType_Unknown))
331 {
332 pUnk = pRCW->GetIUnknown();
333 if (pUnk)
334 FetchedIpType = ComIpType_Unknown;
335 }
336 }
337
338 // If we failed to retrieve an IP then throw an exception.
339 if (pUnk == NULL)
340 COMPlusThrowHR(hr);
341
342 // If the caller wants to know the fetched IP type, then set pFetchedIpType
343 // to the type of the IP.
344 if (pFetchedIpType)
345 *pFetchedIpType = FetchedIpType;
346
347 RETURN pUnk;
348}
349
350
351//+----------------------------------------------------------------------------
352// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, REFIID iid);
353// convert ComIP to an ObjectRef, based on riid
354//+----------------------------------------------------------------------------
355IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, REFIID iid, bool throwIfNoComIP /* = true */)
356{
357 CONTRACT (IUnknown*)
358 {
359 THROWS;
360 GC_TRIGGERS;
361 MODE_COOPERATIVE;
362 PRECONDITION(CheckPointer(poref));
363 POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL, throwIfNoComIP ? NULL_NOT_OK : NULL_OK) : CheckPointer(RETVAL, NULL_OK));
364 }
365 CONTRACT_END;
366
367 ASSERT_PROTECTED(poref);
368
369 // COM had better be started up at this point.
370 _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!");
371
372 BOOL fReleaseWrapper = false;
373 HRESULT hr = E_NOINTERFACE;
374 IUnknown* pUnk = NULL;
375 size_t ul = 0;
376
377 if (*poref == NULL)
378 RETURN NULL;
379
380 MethodTable *pMT = (*poref)->GetMethodTable();
381
382 SyncBlock* pBlock = (*poref)->GetSyncBlock();
383
384 InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo();
385
386 if ((NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed()))
387 {
388 CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref);
389 pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, iid, NULL);
390 }
391 else
392 {
393 SafeComHolder<IUnknown> pUnkHolder;
394
395 RCWHolder pRCW(GetThread());
396 RCWPROTECT_BEGIN(pRCW, pBlock);
397
398 // The interface will be returned addref'ed.
399 pUnkHolder = pRCW->GetComIPFromRCW(iid);
400
401 RCWPROTECT_END(pRCW);
402
403 pUnk = pUnkHolder.Extract();
404 }
405
406 if (throwIfNoComIP && pUnk == NULL)
407 COMPlusThrowHR(hr);
408
409 RETURN pUnk;
410}
411
412
413//+----------------------------------------------------------------------------
414// GetObjectRefFromComIP
415// pUnk : input IUnknown
416// pMTClass : specifies the type of instance to be returned
417// NOTE:** As per COM Rules, the IUnknown passed is shouldn't be AddRef'ed
418//+----------------------------------------------------------------------------
419void GetObjectRefFromComIP(OBJECTREF* pObjOut, IUnknown **ppUnk, MethodTable *pMTClass, MethodTable *pItfMT, DWORD dwFlags)
420{
421 CONTRACTL
422 {
423 THROWS;
424 GC_TRIGGERS;
425 MODE_COOPERATIVE;
426 PRECONDITION(CheckPointer(ppUnk));
427 PRECONDITION(CheckPointer(*ppUnk, NULL_OK));
428 PRECONDITION(CheckPointer(pMTClass, NULL_OK));
429 PRECONDITION(IsProtectedByGCFrame(pObjOut));
430 PRECONDITION(pItfMT == NULL || pItfMT->IsInterface());
431 }
432 CONTRACTL_END;
433
434 // COM had better be started up at this point.
435 _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!");
436
437 IUnknown *pUnk = *ppUnk;
438 Thread * pThread = GetThread();
439
440#ifdef MDA_SUPPORTED
441 MdaInvalidIUnknown* mda = MDA_GET_ASSISTANT(InvalidIUnknown);
442 if (mda && pUnk)
443 {
444 // Test pUnk
445 SafeComHolder<IUnknown> pTemp;
446 HRESULT hr = SafeQueryInterface(pUnk, IID_IUnknown, &pTemp);
447 if (hr != S_OK)
448 mda->ReportViolation();
449 }
450#endif
451
452 *pObjOut = NULL;
453 IUnknown* pOuter = pUnk;
454 SafeComHolder<IUnknown> pAutoOuterUnk = NULL;
455
456 if (pUnk != NULL)
457 {
458 // get CCW for IUnknown
459 ComCallWrapper* pWrap = GetCCWFromIUnknown(pUnk);
460 if (pWrap == NULL)
461 {
462 // could be aggregated scenario
463 HRESULT hr = SafeQueryInterface(pUnk, IID_IUnknown, &pOuter);
464 LogInteropQI(pUnk, IID_IUnknown, hr, "GetObjectRefFromComIP: QI for Outer");
465 IfFailThrow(hr);
466
467 // store the outer in the auto pointer
468 pAutoOuterUnk = pOuter;
469 pWrap = GetCCWFromIUnknown(pOuter);
470 }
471
472 if (pWrap != NULL)
473 { // our tear-off
474 _ASSERTE(pWrap != NULL);
475 AppDomain* pCurrDomain = pThread->GetDomain();
476 ADID pObjDomain = pWrap->GetDomainID();
477 _ASSERTE(pObjDomain == pCurrDomain->GetId());
478 *pObjOut = pWrap->GetObjectRef();
479 }
480
481 if (*pObjOut != NULL)
482 {
483 if (!(dwFlags & ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING))
484 {
485 // Unbox objects from a CLRIReferenceImpl<T> or CLRIReferenceArrayImpl<T>.
486 MethodTable *pMT = (*pObjOut)->GetMethodTable();
487 if (pMT->HasInstantiation())
488 {
489 DWORD nGenericArgs = pMT->GetNumGenericArgs();
490 if (nGenericArgs == 1)
491 {
492 // See if this type C<SomeType> is a G<T>.
493 if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIREFERENCEIMPL)))
494 {
495 TypeHandle thType = pMT->GetInstantiation()[0];
496 COMInterfaceMarshaler::IReferenceOrIReferenceArrayUnboxWorker(*pObjOut, thType, FALSE, pObjOut);
497 }
498 else if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIREFERENCEARRAYIMPL)))
499 {
500 TypeHandle thArrayElementType = pMT->GetInstantiation()[0];
501 COMInterfaceMarshaler::IReferenceOrIReferenceArrayUnboxWorker(*pObjOut, thArrayElementType, TRUE, pObjOut);
502 }
503 }
504 else if ((nGenericArgs == 2) && pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIKEYVALUEPAIRIMPL)))
505 {
506 // Unbox IKeyValuePair from CLRIKeyValuePairImpl
507 COMInterfaceMarshaler::IKeyValuePairUnboxWorker(*pObjOut, pObjOut);
508 }
509 }
510 }
511 }
512 else
513 {
514 // Only pass in the class method table to the interface marshaler if
515 // it is a COM import (or COM import derived) class or a WinRT delegate.
516 MethodTable *pComClassMT = NULL;
517 if (pMTClass)
518 {
519 if (pMTClass->IsComObjectType() ||
520 (pMTClass->IsDelegate() && (pMTClass->IsProjectedFromWinRT() || WinRTTypeNameConverter::IsRedirectedType(pMTClass))))
521 {
522 pComClassMT = pMTClass;
523 }
524 }
525
526 DWORD flags = RCW::CreationFlagsFromObjForComIPFlags((ObjFromComIP::flags)dwFlags);
527
528 // Convert the IP to an OBJECTREF.
529 COMInterfaceMarshaler marshaler;
530
531 marshaler.Init(pOuter, pComClassMT, pThread, flags);
532
533 if (flags & ObjFromComIP::SUPPRESS_ADDREF)
534 {
535 // We can swallow the reference in ppUnk
536 // This only happens in WinRT
537 *pObjOut = marshaler.FindOrCreateObjectRef(ppUnk, pItfMT);
538 }
539 else
540 {
541 *pObjOut = marshaler.FindOrCreateObjectRef(pUnk, pItfMT);
542 }
543 }
544 }
545
546
547 if ((0 == (dwFlags & ObjFromComIP::CLASS_IS_HINT)) && (*pObjOut != NULL))
548 {
549 // make sure we can cast to the specified class
550 if (pMTClass != NULL)
551 {
552 FAULT_NOT_FATAL();
553
554 // Bad format exception thrown for backward compatibility
555 THROW_BAD_FORMAT_MAYBE(pMTClass->IsArray() == FALSE, BFA_UNEXPECTED_ARRAY_TYPE, pMTClass);
556
557 if (!CanCastComObject(*pObjOut, pMTClass))
558 {
559 StackSString ssObjClsName;
560 StackSString ssDestClsName;
561
562 (*pObjOut)->GetMethodTable()->_GetFullyQualifiedNameForClass(ssObjClsName);
563 pMTClass->_GetFullyQualifiedNameForClass(ssDestClsName);
564
565 COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST,
566 ssObjClsName.GetUnicode(), ssDestClsName.GetUnicode());
567 }
568 }
569 else if (dwFlags & ObjFromComIP::REQUIRE_IINSPECTABLE)
570 {
571 MethodTable *pMT = (*pObjOut)->GetMethodTable();
572 if (pMT->IsDelegate() && pMT->IsProjectedFromWinRT())
573 {
574 // This is a WinRT delegate - WinRT delegate doesn't implement IInspectable but we allow unboxing a WinRT delegate
575 // from a IReference<T>
576 }
577 else
578 {
579 // Just call GetComIPFromObjectRef. We could be more efficient here but the code would get complicated
580 // which doesn't seem to be worth it. The function throws an exception if the QI/cast fails.
581 SafeComHolder<IUnknown> pInsp = GetComIPFromObjectRef(pObjOut, ComIpType_Inspectable, NULL);
582 _ASSERTE(pInsp != NULL);
583 }
584 }
585 }
586}
587#endif // FEATURE_COMINTEROP
588
589
590