diff options
Diffstat (limited to 'src/vm/interopconverter.cpp')
-rw-r--r-- | src/vm/interopconverter.cpp | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/src/vm/interopconverter.cpp b/src/vm/interopconverter.cpp new file mode 100644 index 0000000000..5e7f53ef75 --- /dev/null +++ b/src/vm/interopconverter.cpp @@ -0,0 +1,984 @@ +// 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. + + +#include "common.h" +#include "vars.hpp" +#include "excep.h" +#include "interoputil.h" +#include "interopconverter.h" +#ifdef FEATURE_REMOTING +#include "remoting.h" +#endif +#include "olevariant.h" +#include "comcallablewrapper.h" + +#ifdef FEATURE_COMINTEROP + +#include "stdinterfaces.h" +#include "runtimecallablewrapper.h" +#include "cominterfacemarshaler.h" +#include "mdaassistants.h" +#include "binder.h" +#include "winrttypenameconverter.h" +#include "typestring.h" + +struct MshlPacket +{ + DWORD size; +}; + +// if the object we are creating is a proxy to another appdomain, want to create the wrapper for the +// new object in the appdomain of the proxy target +IUnknown* GetIUnknownForMarshalByRefInServerDomain(OBJECTREF* poref) +{ + CONTRACT (IUnknown*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(poref)); + PRECONDITION((*poref)->GetTrueMethodTable()->IsMarshaledByRef()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + Context *pContext = NULL; + +#ifdef FEATURE_REMOTING + // so this is an proxy type, + // now get it's underlying appdomain which will be null if non-local + if ((*poref)->IsTransparentProxy()) + pContext = CRemotingServices::GetServerContextForProxy(*poref); +#endif + + if (pContext == NULL) + pContext = GetCurrentContext(); + + _ASSERTE(pContext->GetDomain() == GetCurrentContext()->GetDomain()); + + CCWHolder pWrap = ComCallWrapper::InlineGetWrapper(poref); + + IUnknown* pUnk = ComCallWrapper::GetComIPFromCCW(pWrap, IID_IUnknown, NULL); + + RETURN pUnk; +} + +#ifdef FEATURE_REMOTING +//+---------------------------------------------------------------------------- +// IUnknown* GetIUnknownForTransparentProxy(OBJECTREF otp) +//+---------------------------------------------------------------------------- + +IUnknown* GetIUnknownForTransparentProxy(OBJECTREF* poref, BOOL fIsBeingMarshalled) +{ + CONTRACT (IUnknown*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(poref)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + GCX_COOP(); + + IUnknown* pUnk; + + OBJECTREF realProxy = ObjectToOBJECTREF(CRemotingServices::GetRealProxy(OBJECTREFToObject(*poref))); + _ASSERTE(realProxy != NULL); + + GCPROTECT_BEGIN(realProxy); + + MethodDescCallSite getDCOMProxy(METHOD__REAL_PROXY__GETDCOMPROXY, &realProxy); + + ARG_SLOT args[] = { + ObjToArgSlot(realProxy), + BoolToArgSlot(fIsBeingMarshalled), + }; + + ARG_SLOT ret = getDCOMProxy.Call_RetArgSlot(args); + + pUnk = (IUnknown*)ret; + + GCPROTECT_END(); + + RETURN pUnk; +} +#endif // FEATURE_REMOTING + +//-------------------------------------------------------------------------------- +// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, MethodTable *pMT, ...) +// Convert ObjectRef to a COM IP, based on MethodTable* pMT. +IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, MethodTable *pMT, BOOL bSecurityCheck, BOOL bEnableCustomizedQueryInterface) +{ + CONTRACT (IUnknown*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(poref)); + PRECONDITION(CheckPointer(pMT)); + PRECONDITION(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!"); + POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL) : CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + BOOL fReleaseWrapper = false; + HRESULT hr = E_NOINTERFACE; + SafeComHolder<IUnknown> pUnk = NULL; + size_t ul = 0; + + if (*poref == NULL) + RETURN NULL; + +#ifdef FEATURE_REMOTING + if ((*poref)->IsTransparentProxy()) + { + // Retrieve the IID of the interface to QI for. + IID iid; + if (pMT->IsInterface()) + { + pMT->GetGuid(&iid, TRUE); + } + else + { + ComCallWrapperTemplate *pTemplate = ComCallWrapperTemplate::GetTemplate(TypeHandle(pMT)); + if (pTemplate->SupportsIClassX()) + { + ComMethodTable *pComMT = pTemplate->GetClassComMT(); + iid = pComMT->GetIID(); + } + else + { + // if IClassX is not supported, we try the default interface of the class + MethodTable *pDefItfMT = pMT->GetDefaultWinRTInterface(); + if (pDefItfMT != NULL) + { + pDefItfMT->GetGuid(&iid, TRUE); + } + else + { + // else we fail because the class has no IID associated with it + IfFailThrow(E_NOINTERFACE); + } + } + } + + // Retrieve an IUnknown for the TP. + SafeComHolder<IUnknown> pProxyUnk = GetIUnknownForTransparentProxy(poref, FALSE); + + // QI for the requested interface. + IfFailThrow(SafeQueryInterface(pProxyUnk, iid, &pUnk)); + goto LExit; + } +#endif // FEATURE_REMOTING + + SyncBlock* pBlock = (*poref)->GetSyncBlock(); + + InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo(); + + // If we have a CCW, or a NULL CCW but the RCW field was never used, + // get the pUnk from the ComCallWrapper, otherwise from the RCW + if ((NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed())) + { + CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref); + + GetComIPFromCCW::flags flags = GetComIPFromCCW::None; + if (!bSecurityCheck) { flags |= GetComIPFromCCW::SuppressSecurityCheck; } + if (!bEnableCustomizedQueryInterface) { flags |= GetComIPFromCCW::SuppressCustomizedQueryInterface; } + + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, GUID_NULL, pMT, flags); + } + else + { + RCWHolder pRCW(GetThread()); + RCWPROTECT_BEGIN(pRCW, pBlock); + + // The interface will be returned addref'ed. + pUnk = pRCW->GetComIPFromRCW(pMT); + + RCWPROTECT_END(pRCW); + } + +#ifdef FEATURE_REMOTING +LExit: +#endif + // If we failed to retrieve an IP then throw an exception. + if (pUnk == NULL) + COMPlusThrowHR(hr); + + pUnk.SuppressRelease(); + RETURN pUnk; +} + + +//-------------------------------------------------------------------------------- +// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, ComIpType ReqIpType, ComIpType *pFetchedIpType); +// Convert ObjectRef to a COM IP of the requested type. +IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, ComIpType ReqIpType, ComIpType *pFetchedIpType) +{ + CONTRACT (IUnknown*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION((ReqIpType & (ComIpType_Dispatch | ComIpType_Unknown | ComIpType_Inspectable)) != 0); + PRECONDITION(CheckPointer(poref)); + PRECONDITION(ReqIpType != 0); + POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL) : CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // COM had better be started up at this point. + _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!"); + + BOOL fReleaseWrapper = false; + HRESULT hr = E_NOINTERFACE; + IUnknown* pUnk = NULL; + size_t ul = 0; + ComIpType FetchedIpType = ComIpType_None; + + if (*poref == NULL) + RETURN NULL; + + MethodTable *pMT = (*poref)->GetMethodTable(); +#ifdef FEATURE_REMOTING + if (pMT->IsTransparentProxy()) + { + SafeComHolder<IUnknown> pProxyUnk = GetIUnknownForTransparentProxy(poref, FALSE); + + if (ReqIpType & ComIpType_Dispatch) + { + hr = SafeQueryInterface(pProxyUnk, IID_IDispatch, &pUnk); + if (SUCCEEDED(hr)) + { + // In Whidbey we used to return ComIpType_Unknown here to maintain backward compatibility with + // previous releases where we had mistakenly returned ComIpType_None (which was interpreted as + // ComIpType_Unknown by the callers of this method). + FetchedIpType = ComIpType_Dispatch; + goto LExit; + } + } + + if (ReqIpType & ComIpType_Inspectable) + { + hr = SafeQueryInterface(pProxyUnk, IID_IInspectable, &pUnk); + if (SUCCEEDED(hr)) + { + FetchedIpType = ComIpType_Inspectable; + goto LExit; + } + } + + if (ReqIpType & ComIpType_Unknown) + { + hr = SafeQueryInterface(pProxyUnk, IID_IUnknown, &pUnk); + if (SUCCEEDED(hr)) + { + FetchedIpType = ComIpType_Unknown; + goto LExit; + } + } + + goto LExit; + } +#endif // FEATURE_REMOTING + + SyncBlock* pBlock = (*poref)->GetSyncBlock(); + + InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo(); + + if ( (NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed()) ) + { + CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref); + + // If the user requested IDispatch, then check for IDispatch first. + if (ReqIpType & ComIpType_Dispatch) + { + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IDispatch, NULL); + if (pUnk) + FetchedIpType = ComIpType_Dispatch; + } + + if (ReqIpType & ComIpType_Inspectable) + { + WinMDAdapter::RedirectedTypeIndex redirectedTypeIndex; + + MethodTable * pMT = (*poref)->GetMethodTable(); + + // + // Check whether this object is of a legal WinRT type (including array) + // + // Note that System.RuntimeType is a weird case - we only redirect System.Type at type + // level, but when we boxing the actual instance, we expect it to be a System.RuntimeType + // instance, which is not redirected and not a legal WinRT type + // + // Therefore, special case for System.RuntimeType and treat it as a legal WinRT type + // only for boxing + // + if (pMT->IsLegalWinRTType(poref) || + MscorlibBinder::IsClass(pMT, CLASS__CLASS)) + { + // The managed signature contains Object, and native signature is IInspectable. + // "Box" value types by allocating an IReference<T> and storing them inside it. + // Similarly, String must be an IReference<HSTRING>. Delegates get wrapped too. + // Arrays must be stored in an IReferenceArray<T>. + // System.Type is in fact internal type System.RuntimeType (CLASS__CLASS) that inherits from it. + // Note: We do not allow System.ReflectionOnlyType that inherits from System.RuntimeType. + // KeyValuePair`2 must be exposed as CLRIKeyValuePair. + if (pMT->HasInstantiation() && pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__KEYVALUEPAIRGENERIC))) + { + TypeHandle th = TypeHandle(MscorlibBinder::GetClass(CLASS__CLRIKEYVALUEPAIRIMPL)).Instantiate(pMT->GetInstantiation()); + + MethodDesc *method = MethodDesc::FindOrCreateAssociatedMethodDesc( + MscorlibBinder::GetMethod(METHOD__CLRIKEYVALUEPAIRIMPL__BOXHELPER), + th.GetMethodTable(), + FALSE, + Instantiation(), + FALSE); + _ASSERTE(method != NULL); + + MethodDescCallSite boxHelper(method); + + ARG_SLOT Args[] = + { + ObjToArgSlot(*poref), + }; + OBJECTREF orCLRKeyValuePair = boxHelper.Call_RetOBJECTREF(Args); + + GCPROTECT_BEGIN(orCLRKeyValuePair); + CCWHolder pCCWHoldBoxed = ComCallWrapper::InlineGetWrapper(&orCLRKeyValuePair); + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHoldBoxed, IID_IInspectable, NULL); + GCPROTECT_END(); + } + else if ((pMT->IsValueType() || + pMT->IsStringOrArray() || + pMT->IsDelegate() || + MscorlibBinder::IsClass(pMT, CLASS__CLASS))) + { + OBJECTREF orBoxedIReference = NULL; + MethodDescCallSite createIReference(METHOD__FACTORYFORIREFERENCE__CREATE_IREFERENCE); + + ARG_SLOT Args[] = + { + ObjToArgSlot(*poref), + }; + + // Call FactoryForIReference::CreateIReference(Object) for an IReference<T> or IReferenceArray<T>. + orBoxedIReference = createIReference.Call_RetOBJECTREF(Args); + + GCPROTECT_BEGIN(orBoxedIReference); + CCWHolder pCCWHoldBoxed = ComCallWrapper::InlineGetWrapper(&orBoxedIReference); + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHoldBoxed, IID_IInspectable, NULL); + GCPROTECT_END(); + } + else if (WinRTTypeNameConverter::ResolveRedirectedType(pMT, &redirectedTypeIndex)) + { + // This is a redirected type - see if we need to manually marshal it + if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Uri) + { + UriMarshalingInfo *pUriMarshalInfo = GetAppDomain()->GetMarshalingData()->GetUriMarshalingInfo(); + struct + { + OBJECTREF ref; + STRINGREF refRawUri; + } + gc; + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.ref = *poref; + + MethodDescCallSite getRawURI(pUriMarshalInfo->GetSystemUriOriginalStringMD()); + ARG_SLOT getRawURIArgs[] = + { + ObjToArgSlot(gc.ref) + }; + + gc.refRawUri = (STRINGREF)getRawURI.Call_RetOBJECTREF(getRawURIArgs); + + DWORD cchRawUri = gc.refRawUri->GetStringLength(); + LPCWSTR wszRawUri = gc.refRawUri->GetBuffer(); + + { + GCX_PREEMP(); + pUnk = CreateWinRTUri(wszRawUri, static_cast<INT32>(cchRawUri)); + } + + GCPROTECT_END(); + } + else if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_NotifyCollectionChangedEventArgs || + redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_ComponentModel_PropertyChangedEventArgs) + { + MethodDesc *pMD; + EventArgsMarshalingInfo *pInfo = GetAppDomain()->GetMarshalingData()->GetEventArgsMarshalingInfo(); + + if (redirectedTypeIndex == WinMDAdapter::RedirectedTypeIndex_System_Collections_Specialized_NotifyCollectionChangedEventArgs) + pMD = pInfo->GetSystemNCCEventArgsToWinRTNCCEventArgsMD(); + else + pMD = pInfo->GetSystemPCEventArgsToWinRTPCEventArgsMD(); + + MethodDescCallSite marshalMethod(pMD); + ARG_SLOT methodArgs[] = + { + ObjToArgSlot(*poref) + }; + pUnk = (IUnknown *)marshalMethod.Call_RetLPVOID(methodArgs); + } + else + { + _ASSERTE(!W("Unexpected redirected type seen in GetComIPFromObjectRef")); + } + } + else + { + // + // WinRT reference type - marshal as IInspectable + // + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IInspectable, /* pIntfMT = */ NULL); + } + } + else + { + // + // Marshal non-WinRT types as IInspectable* to enable round-tripping (for example, TextBox.Tag property) + // By default, this returns ICustomPropertyProvider; + // + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IInspectable, /* pIntfMT = */ NULL); + } + + if (pUnk) + FetchedIpType = ComIpType_Inspectable; + } + + // If the ObjectRef doesn't support IDispatch and the caller also accepts + // an IUnknown pointer, then check for IUnknown. + if (!pUnk && (ReqIpType & ComIpType_Unknown)) + { + if (ReqIpType & ComIpType_OuterUnknown) + { + // check if the object is aggregated + SimpleComCallWrapper* pSimpleWrap = pCCWHold->GetSimpleWrapper(); + if (pSimpleWrap) + { + pUnk = pSimpleWrap->GetOuter(); + if (pUnk) + SafeAddRef(pUnk); + } + } + if (!pUnk) + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, IID_IUnknown, NULL); + if (pUnk) + FetchedIpType = ComIpType_Unknown; + } + } + else + { + RCWHolder pRCW(GetThread()); + + // This code is hot, use a simple RCWHolder check (i.e. don't increment the use count on the RCW). + // @TODO: Cache IInspectable & IDispatch so we don't have to QI every time we come here. + pRCW.InitFastCheck(pBlock); + + // If the user requested IDispatch, then check for IDispatch first. + if (ReqIpType & ComIpType_Dispatch) + { + pUnk = pRCW->GetIDispatch(); + if (pUnk) + FetchedIpType = ComIpType_Dispatch; + } + + if (ReqIpType & ComIpType_Inspectable) + { + pUnk = pRCW->GetIInspectable(); + if (pUnk) + FetchedIpType = ComIpType_Inspectable; + } + + // If the ObjectRef doesn't support IDispatch and the caller also accepts + // an IUnknown pointer, then check for IUnknown. + if (!pUnk && (ReqIpType & ComIpType_Unknown)) + { + pUnk = pRCW->GetIUnknown(); + if (pUnk) + FetchedIpType = ComIpType_Unknown; + } + } + +#ifdef FEATURE_REMOTING +LExit: +#endif + // If we failed to retrieve an IP then throw an exception. + if (pUnk == NULL) + COMPlusThrowHR(hr); + + // If the caller wants to know the fetched IP type, then set pFetchedIpType + // to the type of the IP. + if (pFetchedIpType) + *pFetchedIpType = FetchedIpType; + + RETURN pUnk; +} + + +//+---------------------------------------------------------------------------- +// IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, REFIID iid); +// convert ComIP to an ObjectRef, based on riid +//+---------------------------------------------------------------------------- +IUnknown *GetComIPFromObjectRef(OBJECTREF *poref, REFIID iid, bool throwIfNoComIP /* = true */) +{ + CONTRACT (IUnknown*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(poref)); + POSTCONDITION((*poref) != NULL ? CheckPointer(RETVAL, throwIfNoComIP ? NULL_NOT_OK : NULL_OK) : CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + ASSERT_PROTECTED(poref); + + // COM had better be started up at this point. + _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!"); + + BOOL fReleaseWrapper = false; + HRESULT hr = E_NOINTERFACE; + IUnknown* pUnk = NULL; + size_t ul = 0; + + if (*poref == NULL) + RETURN NULL; + + MethodTable *pMT = (*poref)->GetMethodTable(); +#ifdef FEATURE_REMOTING + if (pMT->IsTransparentProxy()) + { + SafeComHolder<IUnknown> pProxyUnk = GetIUnknownForTransparentProxy(poref, FALSE); + IfFailThrow(SafeQueryInterface(pProxyUnk, iid, &pUnk)); + goto LExit; + } +#endif + + SyncBlock* pBlock = (*poref)->GetSyncBlock(); + + InteropSyncBlockInfo* pInteropInfo = pBlock->GetInteropInfo(); + + if ((NULL != pInteropInfo->GetCCW()) || (!pInteropInfo->RCWWasUsed())) + { + CCWHolder pCCWHold = ComCallWrapper::InlineGetWrapper(poref); + pUnk = ComCallWrapper::GetComIPFromCCW(pCCWHold, iid, NULL); + } + else + { + SafeComHolder<IUnknown> pUnkHolder; + + RCWHolder pRCW(GetThread()); + RCWPROTECT_BEGIN(pRCW, pBlock); + + // The interface will be returned addref'ed. + pUnkHolder = pRCW->GetComIPFromRCW(iid); + + RCWPROTECT_END(pRCW); + + pUnk = pUnkHolder.Extract(); + } + +#ifdef FEATURE_REMOTING +LExit: +#endif + if (throwIfNoComIP && pUnk == NULL) + COMPlusThrowHR(hr); + + RETURN pUnk; +} + +#ifdef FEATURE_REMOTING +OBJECTREF GetObjectRefFromComIP_CrossDomain(ADID objDomainId, ComCallWrapper* pWrap) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + OBJECTREF oref = NULL; + + EX_TRY + { + // the CCW belongs to a different domain.. + // unmarshal the object to the current domain + if (!UnMarshalObjectForCurrentDomain(objDomainId, pWrap, &oref)) + oref = NULL; + } + EX_CATCH + { + // fall back to creating an RCW if we were unable to + // marshal the object (most commonly because the object + // graph is not serializable) + oref = NULL; + } + EX_END_CATCH(SwallowAllExceptions) + + return oref; +} +#endif //#ifdef FEATURE_REMOTING + +//+---------------------------------------------------------------------------- +// GetObjectRefFromComIP +// pUnk : input IUnknown +// pMTClass : specifies the type of instance to be returned +// NOTE:** As per COM Rules, the IUnknown passed is shouldn't be AddRef'ed +//+---------------------------------------------------------------------------- +void GetObjectRefFromComIP(OBJECTREF* pObjOut, IUnknown **ppUnk, MethodTable *pMTClass, MethodTable *pItfMT, DWORD dwFlags) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(ppUnk)); + PRECONDITION(CheckPointer(*ppUnk, NULL_OK)); + PRECONDITION(CheckPointer(pMTClass, NULL_OK)); + PRECONDITION(IsProtectedByGCFrame(pObjOut)); + PRECONDITION(pItfMT == NULL || pItfMT->IsInterface()); + } + CONTRACTL_END; + + // COM had better be started up at this point. + _ASSERTE(g_fComStarted && "COM has not been started up, make sure EnsureComStarted is called before any COM objects are used!"); + + IUnknown *pUnk = *ppUnk; + Thread * pThread = GetThread(); + +#ifdef MDA_SUPPORTED + MdaInvalidIUnknown* mda = MDA_GET_ASSISTANT(InvalidIUnknown); + if (mda && pUnk) + { + // Test pUnk + SafeComHolder<IUnknown> pTemp; + HRESULT hr = SafeQueryInterface(pUnk, IID_IUnknown, &pTemp); + if (hr != S_OK) + mda->ReportViolation(); + } +#endif + + *pObjOut = NULL; + IUnknown* pOuter = pUnk; + SafeComHolder<IUnknown> pAutoOuterUnk = NULL; + + if (pUnk != NULL) + { + // get CCW for IUnknown + ComCallWrapper* pWrap = GetCCWFromIUnknown(pUnk); + if (pWrap == NULL) + { + // could be aggregated scenario + HRESULT hr = SafeQueryInterface(pUnk, IID_IUnknown, &pOuter); + LogInteropQI(pUnk, IID_IUnknown, hr, "GetObjectRefFromComIP: QI for Outer"); + IfFailThrow(hr); + + // store the outer in the auto pointer + pAutoOuterUnk = pOuter; + pWrap = GetCCWFromIUnknown(pOuter); + } + + if (pWrap != NULL) + { // our tear-off + _ASSERTE(pWrap != NULL); + AppDomain* pCurrDomain = pThread->GetDomain(); + ADID pObjDomain = pWrap->GetDomainID(); +#ifdef FEATURE_REMOTING + if (pObjDomain == pCurrDomain->GetId()) + *pObjOut = pWrap->GetObjectRef(); + else + *pObjOut = GetObjectRefFromComIP_CrossDomain(pObjDomain, pWrap); +#else + _ASSERTE(pObjDomain == pCurrDomain->GetId()); + *pObjOut = pWrap->GetObjectRef(); +#endif + } + + if (*pObjOut != NULL) + { + if (!(dwFlags & ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING)) + { + // Unbox objects from a CLRIReferenceImpl<T> or CLRIReferenceArrayImpl<T>. + MethodTable *pMT = (*pObjOut)->GetMethodTable(); + if (pMT->HasInstantiation()) + { + DWORD nGenericArgs = pMT->GetNumGenericArgs(); + if (nGenericArgs == 1) + { + // See if this type C<SomeType> is a G<T>. + if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIREFERENCEIMPL))) + { + TypeHandle thType = pMT->GetInstantiation()[0]; + COMInterfaceMarshaler::IReferenceOrIReferenceArrayUnboxWorker(*pObjOut, thType, FALSE, pObjOut); + } + else if (pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIREFERENCEARRAYIMPL))) + { + TypeHandle thArrayElementType = pMT->GetInstantiation()[0]; + COMInterfaceMarshaler::IReferenceOrIReferenceArrayUnboxWorker(*pObjOut, thArrayElementType, TRUE, pObjOut); + } + } + else if ((nGenericArgs == 2) && pMT->HasSameTypeDefAs(MscorlibBinder::GetClass(CLASS__CLRIKEYVALUEPAIRIMPL))) + { + // Unbox IKeyValuePair from CLRIKeyValuePairImpl + COMInterfaceMarshaler::IKeyValuePairUnboxWorker(*pObjOut, pObjOut); + } + } + } + } + else + { + // Only pass in the class method table to the interface marshaler if + // it is a COM import (or COM import derived) class or a WinRT delegate. + MethodTable *pComClassMT = NULL; + if (pMTClass) + { + if (pMTClass->IsComObjectType() || + (pMTClass->IsDelegate() && (pMTClass->IsProjectedFromWinRT() || WinRTTypeNameConverter::IsRedirectedType(pMTClass)))) + { + pComClassMT = pMTClass; + } + } + + DWORD flags = RCW::CreationFlagsFromObjForComIPFlags((ObjFromComIP::flags)dwFlags); + + // Convert the IP to an OBJECTREF. + COMInterfaceMarshaler marshaler; + + marshaler.Init(pOuter, pComClassMT, pThread, flags); + + if (flags & ObjFromComIP::SUPPRESS_ADDREF) + { + // We can swallow the reference in ppUnk + // This only happens in WinRT + *pObjOut = marshaler.FindOrCreateObjectRef(ppUnk, pItfMT); + } + else + { + *pObjOut = marshaler.FindOrCreateObjectRef(pUnk, pItfMT); + } + } + } + + + if ((0 == (dwFlags & ObjFromComIP::CLASS_IS_HINT)) && (*pObjOut != NULL)) + { + // make sure we can cast to the specified class + if (pMTClass != NULL) + { + FAULT_NOT_FATAL(); + + // Bad format exception thrown for backward compatibility + THROW_BAD_FORMAT_MAYBE(pMTClass->IsArray() == FALSE, BFA_UNEXPECTED_ARRAY_TYPE, pMTClass); + + if (!CanCastComObject(*pObjOut, pMTClass)) + { + StackSString ssObjClsName; + StackSString ssDestClsName; + + (*pObjOut)->GetTrueMethodTable()->_GetFullyQualifiedNameForClass(ssObjClsName); + pMTClass->_GetFullyQualifiedNameForClass(ssDestClsName); + + COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, + ssObjClsName.GetUnicode(), ssDestClsName.GetUnicode()); + } + } + else if (dwFlags & ObjFromComIP::REQUIRE_IINSPECTABLE) + { + MethodTable *pMT = (*pObjOut)->GetMethodTable(); + if (pMT->IsDelegate() && pMT->IsProjectedFromWinRT()) + { + // This is a WinRT delegate - WinRT delegate doesn't implement IInspectable but we allow unboxing a WinRT delegate + // from a IReference<T> + } + else + { + // Just call GetComIPFromObjectRef. We could be more efficient here but the code would get complicated + // which doesn't seem to be worth it. The function throws an exception if the QI/cast fails. + SafeComHolder<IUnknown> pInsp = GetComIPFromObjectRef(pObjOut, ComIpType_Inspectable, NULL); + _ASSERTE(pInsp != NULL); + } + } + } +} +#endif // FEATURE_COMINTEROP + + +#ifdef FEATURE_REMOTING +//-------------------------------------------------------- +// ConvertObjectToBSTR +// serializes object to a BSTR, caller needs to SysFree the Bstr +// and GCPROTECT the oref parameter. +//-------------------------------------------------------------------------------- +BOOL ConvertObjectToBSTR(OBJECTREF* oref, BOOL fCrossRuntime, BSTR* pBStr) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pBStr)); + PRECONDITION(IsProtectedByGCFrame (oref)); + } + CONTRACTL_END; + + *pBStr = NULL; + + MethodTable *pMT = (*oref)->GetMethodTable(); + if (!pMT->IsTransparentProxy() && !pMT->IsMarshaledByRef() && !pMT->IsSerializable()) + { + // The object is not serializable - don't waste time calling to managed and trying to + // serialize it with a formatter. This is an optimization so we don't throw and catch + // SerializationException unnecessarily. + return FALSE; + } + + // We will be using the remoting services so make sure remoting is started up. + CRemotingServices::EnsureRemotingStarted(); + + MethodDescCallSite marshalToBuffer(METHOD__REMOTING_SERVICES__MARSHAL_TO_BUFFER); + + ARG_SLOT args[] = + { + ObjToArgSlot(*oref), + BoolToArgSlot(fCrossRuntime) + }; + + BASEARRAYREF aref = (BASEARRAYREF) marshalToBuffer.Call_RetOBJECTREF(args); + + if (aref != NULL) + { + _ASSERTE(!aref->IsMultiDimArray()); + //@todo ASSERTE that the array is a byte array + + ULONG cbSize = aref->GetNumComponents(); + BYTE* pBuf = (BYTE *)aref->GetDataPtr(); + + BSTR bstr = SysAllocStringByteLen(NULL, cbSize); + if (bstr == NULL) + COMPlusThrowOM(); + + CopyMemory(bstr, pBuf, cbSize); + *pBStr = bstr; + } + + return TRUE; +} + +//-------------------------------------------------------------------------------- +// ConvertBSTRToObject +// deserializes a BSTR, created using ConvertObjectToBSTR, this api SysFree's the BSTR +//-------------------------------------------------------------------------------- +OBJECTREF ConvertBSTRToObject(BSTR bstr, BOOL fCrossRuntime) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + BSTRHolder localBstr(bstr); + + OBJECTREF oref = NULL; + + // We will be using the remoting services so make sure remoting is started up. + CRemotingServices::EnsureRemotingStarted(); + + MethodDescCallSite unmarshalFromBuffer(METHOD__REMOTING_SERVICES__UNMARSHAL_FROM_BUFFER); + + // convert BSTR to a byte array + + // allocate a byte array + INT32 elementCount = SysStringByteLen(bstr); + TypeHandle t = OleVariant::GetArrayForVarType(VT_UI1, TypeHandle((MethodTable *)NULL)); + BASEARRAYREF aref = (BASEARRAYREF) AllocateArrayEx(t, &elementCount, 1); + // copy the bstr data into the managed byte array + memcpyNoGCRefs(aref->GetDataPtr(), bstr, elementCount); + + ARG_SLOT args[] = + { + ObjToArgSlot((OBJECTREF)aref), + BoolToArgSlot(fCrossRuntime) + }; + + oref = unmarshalFromBuffer.Call_RetOBJECTREF(args); + + return oref; +} + +//-------------------------------------------------------------------------------- +// UnMarshalObjectForCurrentDomain +// unmarshal the managed object for the current domain +//-------------------------------------------------------------------------------- +struct ConvertObjectToBSTR_Args +{ + OBJECTREF* oref; + BOOL fCrossRuntime; + BSTR *pBStr; + BOOL fResult; +}; + +void ConvertObjectToBSTR_Wrapper(LPVOID ptr) +{ + WRAPPER_NO_CONTRACT; + + ConvertObjectToBSTR_Args *args = (ConvertObjectToBSTR_Args *)ptr; + args->fResult = ConvertObjectToBSTR(args->oref, args->fCrossRuntime, args->pBStr); +} + + +BOOL UnMarshalObjectForCurrentDomain(ADID pObjDomain, ComCallWrapper* pWrap, OBJECTREF* pResult) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pWrap)); + PRECONDITION(CheckPointer(pResult)); + } + CONTRACTL_END; + + Thread* pThread = GetThread(); + _ASSERTE(pThread); + + _ASSERTE(pThread->GetDomain() != NULL); + _ASSERTE(pThread->GetDomain()->GetId()!= pObjDomain); + + BSTR bstr = NULL; + ConvertObjectToBSTR_Args args; + args.fCrossRuntime = FALSE; + args.pBStr = &bstr; + + OBJECTREF oref = pWrap->GetObjectRef(); + + GCPROTECT_BEGIN(oref); + { + args.oref = &oref; + pThread->DoADCallBack(pObjDomain, ConvertObjectToBSTR_Wrapper, &args); + } + GCPROTECT_END(); + + if (args.fResult) + { + _ASSERTE(bstr != NULL); + *pResult = ConvertBSTRToObject(bstr, FALSE); + } + else + { + *pResult = NULL; + } + + return args.fResult; +} +#endif //FEATURE_REMOTING |