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. |
27 | IUnknown *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. |
89 | IUnknown *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 | //+---------------------------------------------------------------------------- |
355 | IUnknown *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 | //+---------------------------------------------------------------------------- |
419 | void 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 | |