diff options
Diffstat (limited to 'src/vm/dispatchinfo.cpp')
-rw-r--r-- | src/vm/dispatchinfo.cpp | 3771 |
1 files changed, 3771 insertions, 0 deletions
diff --git a/src/vm/dispatchinfo.cpp b/src/vm/dispatchinfo.cpp new file mode 100644 index 0000000000..0881b29c02 --- /dev/null +++ b/src/vm/dispatchinfo.cpp @@ -0,0 +1,3771 @@ +// 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. +// +// File: DispatchInfo.cpp +// + +// +// Implementation of helpers used to expose IDispatch +// and IDispatchEx to COM. +// + + +#include "common.h" + +#include "dispatchinfo.h" +#include "dispex.h" +#include "object.h" +#include "field.h" +#include "method.hpp" +#include "class.h" +#include "comcallablewrapper.h" +#include "threads.h" +#include "excep.h" +#include "objecthandle.h" +#include "comutilnative.h" +#include "eeconfig.h" +#include "interoputil.h" +#include "olevariant.h" +#include "commtmemberinfomap.h" +#include "dispparammarshaler.h" +#include "security.h" +#include "reflectioninvocation.h" +#include "dbginterface.h" + +#define EXCEPTION_INNER_PROP "InnerException" + +// The name of the properties accessed on the managed member infos. +#define MEMBER_INFO_NAME_PROP "Name" + +// The initial size of the DISPID to member map. +#define DISPID_TO_MEMBER_MAP_INITIAL_SIZE 37 + +// The names of the properties that are accessed on the managed member info's +#define MEMBERINFO_TYPE_PROP "MemberType" + +// The names of the properties that are accessed on managed ParameterInfo. +#define PARAMETERINFO_NAME_PROP "Name" + + +OBJECTHANDLE DispatchInfo::m_hndOleAutBinder = NULL; + +MethodTable* DispatchMemberInfo::s_pMemberTypes[NUM_MEMBER_TYPES] = {NULL}; +EnumMemberTypes DispatchMemberInfo::s_memberTypes[NUM_MEMBER_TYPES] = {Uninitted}; +int DispatchMemberInfo::s_iNumMemberTypesKnown = 0; + +// Helper function to convert between a DISPID and a hashkey. +inline UPTR DispID2HashKey(DISPID DispID) +{ + LIMITED_METHOD_CONTRACT; + + return DispID + 2; +} + +// Typedef for string comparition functions. +typedef int (__cdecl *UnicodeStringCompareFuncPtr)(const wchar_t *, const wchar_t *); + +//-------------------------------------------------------------------------------- +// The DispatchMemberInfo class implementation. + +DispatchMemberInfo::DispatchMemberInfo(DispatchInfo *pDispInfo, DISPID DispID, SString& strName, OBJECTREF MemberInfoObj) +: m_DispID(DispID) +, m_hndMemberInfo(NULL) +, m_apParamMarshaler(NULL) +, m_pParamInOnly(NULL) +, m_strName(strName) +, m_pNext(NULL) +, m_enumType (Uninitted) +, m_iNumParams(-1) +, m_CultureAwareState(Unknown) +, m_bRequiresManagedCleanup(FALSE) +, m_bInitialized(FALSE) +, m_bNeutered(FALSE) +, m_pDispInfo(pDispInfo) +, m_bLastParamOleVarArg(FALSE) +{ + WRAPPER_NO_CONTRACT; // Calls to CreateHandle, above, means not a leaf contract +} + +void DispatchMemberInfo::Neuter() +{ + WRAPPER_NO_CONTRACT; + + if (m_apParamMarshaler) + { + // Need to delete all individual members? + // Can't calculate the exact number here. + delete [] m_apParamMarshaler; + m_apParamMarshaler = NULL; + } + + if (m_pParamInOnly) + { + delete [] m_pParamInOnly; + m_pParamInOnly = NULL; + } + + //m_pNext = NULL; + m_enumType = Uninitted; + m_iNumParams = -1; + m_CultureAwareState = Unknown; + m_bNeutered = TRUE; +} + +DispatchMemberInfo::~DispatchMemberInfo() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + // Delete the parameter marshalers and then delete the array of parameter + // marshalers itself. + if (m_apParamMarshaler) + { + EnumMemberTypes MemberType = GetMemberType(); + int NumParamMarshalers = GetNumParameters() + ((MemberType == Property) ? 2 : 1); + for (int i = 0; i < NumParamMarshalers; i++) + { + if (m_apParamMarshaler[i]) + delete m_apParamMarshaler[i]; + } + delete []m_apParamMarshaler; + } + + if (m_pParamInOnly) + delete [] m_pParamInOnly; + + // Destroy the member info object. + if (m_hndMemberInfo) + DestroyHandle(m_hndMemberInfo); + + // Clear the name of the member. + m_strName.Clear(); +} + +void DispatchMemberInfo::EnsureInitialized() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // Initialize the entry if it hasn't been initialized yet. This must be synchronized. + if (!m_bInitialized) + { + DispatchInfo::LockHolder lh(m_pDispInfo); + + if (!m_bInitialized) + Init(); + } +} + +void DispatchMemberInfo::Init() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + EX_TRY + { + // Determine the type of the member. + DetermineMemberType(); + + // Determine the parameter count. + DetermineParamCount(); + + // Determine the culture awareness of the member. + DetermineCultureAwareness(); + + // Set up the parameter marshaler info. + SetUpParamMarshalerInfo(); + + // Mark the dispatch member info as having been initialized. + m_bInitialized = TRUE; + } + EX_CATCH + { + // If we do throw an exception, then the status of the object + // is in limbo - just neuter it. + Neuter(); + } + EX_END_CATCH(RethrowTerminalExceptions); +} + +HRESULT DispatchMemberInfo::GetIDsOfParameters(__in_ecount(NumNames) WCHAR **astrNames, int NumNames, DISPID *aDispIds, BOOL bCaseSensitive) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + + // The member info must have been initialized before this is called. + PRECONDITION(TRUE == m_bInitialized); + PRECONDITION(CheckPointer(astrNames)); + PRECONDITION(CheckPointer(aDispIds)); + } + CONTRACTL_END; + + int NumNamesMapped = 0; + PTRARRAYREF ParamArray = NULL; + int cNames = 0; + + // Initialize all the ID's to DISPID_UNKNOWN. + for (cNames = 0; cNames < NumNames; cNames++) + aDispIds[cNames] = DISPID_UNKNOWN; + + // Retrieve the appropriate string comparation function. + UnicodeStringCompareFuncPtr StrCompFunc = bCaseSensitive ? wcscmp : SString::_wcsicmp; + + GCPROTECT_BEGIN(ParamArray) + { + // Retrieve the member parameters. + ParamArray = GetParameters(); + + // If we managed to retrieve an non empty array of parameters then go through it and + // map the specified names to ID's. + if ((ParamArray != NULL) && (ParamArray->GetNumComponents() > 0)) + { + int NumParams = ParamArray->GetNumComponents(); + int cParams = 0; + NewArrayHolder< NewArrayHolder<WCHAR> > astrParamNames = new NewArrayHolder<WCHAR>[NumParams]; + + // Go through and retrieve the names of all the components. + for (cParams = 0; cParams < NumParams; cParams++) + { + OBJECTREF ParamInfoObj = ParamArray->GetAt(cParams); + GCPROTECT_BEGIN(ParamInfoObj) + { + // Retrieve the MD to use to retrieve the name of the parameter. + MethodDesc *pGetParamNameMD = MemberLoader::FindPropertyMethod(ParamInfoObj->GetMethodTable(), PARAMETERINFO_NAME_PROP, PropertyGet); + _ASSERTE(pGetParamNameMD && "Unable to find getter method for property ParameterInfo::Name"); + MethodDescCallSite getParamName(pGetParamNameMD, &ParamInfoObj); + + // Retrieve the name of the parameter. + ARG_SLOT GetNameArgs[] = + { + ObjToArgSlot(ParamInfoObj) + }; + STRINGREF MemberNameObj = getParamName.Call_RetSTRINGREF(GetNameArgs); + + // If we got a valid name back then store that in the array of names. + if (MemberNameObj != NULL) + { + astrParamNames[cParams] = new WCHAR[MemberNameObj->GetStringLength() + 1]; + wcscpy_s(astrParamNames[cParams], MemberNameObj->GetStringLength() + 1, MemberNameObj->GetBuffer()); + } + } + GCPROTECT_END(); + } + + // Now go through the list of specfiied names and map then to ID's. + for (cNames = 0; cNames < NumNames; cNames++) + { + for (cParams = 0; cParams < NumParams; cParams++) + { + if (astrParamNames[cParams] && (StrCompFunc(astrNames[cNames], astrParamNames[cParams]) == 0)) + { + aDispIds[cNames] = cParams; + NumNamesMapped++; + break; + } + } + } + } + } + GCPROTECT_END(); + + return (NumNamesMapped == NumNames) ? S_OK : DISP_E_UNKNOWNNAME; +} + +PTRARRAYREF DispatchMemberInfo::GetParameters() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PTRARRAYREF ParamArray = NULL; + MethodDesc *pGetParamsMD = NULL; + + // Retrieve the method to use to retrieve the array of parameters. + switch (GetMemberType()) + { + case Method: + { + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); + break; + } + + case Property: + { + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); + break; + } + } + + // If the member has parameters then retrieve the array of parameters. + if (pGetParamsMD != NULL) + { + MethodDescCallSite getParams(pGetParamsMD, m_hndMemberInfo); + + ARG_SLOT GetParamsArgs[] = + { + ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + }; + + ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); + } + + return ParamArray; +} + +void DispatchMemberInfo::MarshalParamNativeToManaged(int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pSrcVar)); + PRECONDITION(pDestObj != NULL); + PRECONDITION(TRUE == m_bInitialized); + } + CONTRACTL_END; + + if (m_apParamMarshaler && m_apParamMarshaler[iParam + 1]) + m_apParamMarshaler[iParam + 1]->MarshalNativeToManaged(pSrcVar, pDestObj); + else + OleVariant::MarshalObjectForOleVariant(pSrcVar, pDestObj); +} + +void DispatchMemberInfo::MarshalParamManagedToNativeRef(int iParam, OBJECTREF *pSrcObj, VARIANT *pRefVar) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pRefVar)); + PRECONDITION(TRUE == m_bInitialized); + PRECONDITION(pSrcObj != NULL); + } + CONTRACTL_END; + + if (m_apParamMarshaler && m_apParamMarshaler[iParam + 1]) + m_apParamMarshaler[iParam + 1]->MarshalManagedToNativeRef(pSrcObj, pRefVar); + else + OleVariant::MarshalOleRefVariantForObject(pSrcObj, pRefVar); +} + +void DispatchMemberInfo::CleanUpParamManaged(int iParam, OBJECTREF *pObj) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pObj != NULL); + PRECONDITION(TRUE == m_bInitialized); + } + CONTRACTL_END; + + if (m_apParamMarshaler && m_apParamMarshaler[iParam + 1]) + m_apParamMarshaler[iParam + 1]->CleanUpManaged(pObj); +} + +void DispatchMemberInfo::MarshalReturnValueManagedToNative(OBJECTREF *pSrcObj, VARIANT *pDestVar) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pDestVar)); + PRECONDITION(pSrcObj != NULL); + PRECONDITION(TRUE == m_bInitialized); + } + CONTRACTL_END; + + if (m_apParamMarshaler && m_apParamMarshaler[0]) + m_apParamMarshaler[0]->MarshalManagedToNative(pSrcObj, pDestVar); + else + OleVariant::MarshalOleVariantForObject(pSrcObj, pDestVar); +} + +ComMTMethodProps * DispatchMemberInfo::GetMemberProps(OBJECTREF MemberInfoObj, ComMTMemberInfoMap *pMemberMap) +{ + CONTRACT (ComMTMethodProps*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(MemberInfoObj != NULL); + PRECONDITION(CheckPointer(pMemberMap, NULL_OK)); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + DISPID DispId = DISPID_UNKNOWN; + ComMTMethodProps *pMemberProps = NULL; + + // If we don't have a member map then we cannot retrieve properties for the member. + if (!pMemberMap) + RETURN NULL; + + // Get the member's properties. + GCPROTECT_BEGIN(MemberInfoObj); + { + MethodTable *pMemberInfoClass = MemberInfoObj->GetMethodTable(); + if (MscorlibBinder::IsClass(pMemberInfoClass, CLASS__METHOD)) + { + // Retrieve the MethodDesc from the MethodInfo. + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &MemberInfoObj); + ARG_SLOT GetMethodHandleArg = ObjToArgSlot(MemberInfoObj); + MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&GetMethodHandleArg); + if (pMeth) + pMemberProps = pMemberMap->GetMethodProps(pMeth->GetMemberDef(), pMeth->GetModule()); + } + else if (MscorlibBinder::IsClass(pMemberInfoClass, CLASS__RT_FIELD_INFO)) + { + MethodDescCallSite getFieldHandle(METHOD__RTFIELD__GET_FIELDHANDLE, &MemberInfoObj); + ARG_SLOT arg = ObjToArgSlot(MemberInfoObj); + FieldDesc* pFld = (FieldDesc*) getFieldHandle.Call_RetLPVOID(&arg); + if (pFld) + pMemberProps = pMemberMap->GetMethodProps(pFld->GetMemberDef(), pFld->GetModule()); + } + else if (MscorlibBinder::IsClass(pMemberInfoClass, CLASS__PROPERTY)) + { + MethodDescCallSite getToken(METHOD__PROPERTY__GET_TOKEN, &MemberInfoObj); + ARG_SLOT arg = ObjToArgSlot(MemberInfoObj); + mdToken propTok = (mdToken) getToken.Call_RetArgSlot(&arg); + MethodDescCallSite getModule(METHOD__PROPERTY__GET_MODULE, &MemberInfoObj); + ARG_SLOT arg1 = ObjToArgSlot(MemberInfoObj); + REFLECTMODULEBASEREF module = (REFLECTMODULEBASEREF) getModule.Call_RetOBJECTREF(&arg1); + Module* pModule = module->GetModule(); + pMemberProps = pMemberMap->GetMethodProps(propTok, pModule); + } + } + GCPROTECT_END(); + + RETURN pMemberProps; +} + +DISPID DispatchMemberInfo::GetMemberDispId(OBJECTREF MemberInfoObj, ComMTMemberInfoMap *pMemberMap) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pMemberMap, NULL_OK)); + } + CONTRACTL_END; + + _ASSERT(MemberInfoObj); + + DISPID DispId = DISPID_UNKNOWN; + + // Get the member's properties. + ComMTMethodProps *pMemberProps = GetMemberProps(MemberInfoObj, pMemberMap); + + // If we managed to get the properties of the member then extract the DISPID. + if (pMemberProps) + DispId = pMemberProps->dispid; + + return DispId; +} + +LPWSTR DispatchMemberInfo::GetMemberName(OBJECTREF MemberInfoObj, ComMTMemberInfoMap *pMemberMap) +{ + CONTRACT (LPWSTR) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(MemberInfoObj != NULL); + PRECONDITION(CheckPointer(pMemberMap, NULL_OK)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + NewArrayHolder<WCHAR> strMemberName = NULL; + ComMTMethodProps *pMemberProps = NULL; + + GCPROTECT_BEGIN(MemberInfoObj); + { + // Get the member's properties. + pMemberProps = GetMemberProps(MemberInfoObj, pMemberMap); + + // If we managed to get the member's properties then extract the name. + if (pMemberProps) + { + int MemberNameLen = (INT)wcslen(pMemberProps->pName); + strMemberName = new WCHAR[MemberNameLen + 1]; + + memcpy(strMemberName, pMemberProps->pName, (MemberNameLen + 1) * sizeof(WCHAR)); + } + else + { + // Retrieve the Get method for the Name property. + MethodDesc *pMD = MemberLoader::FindPropertyMethod(MemberInfoObj->GetMethodTable(), MEMBER_INFO_NAME_PROP, PropertyGet); + _ASSERTE(pMD && "Unable to find getter method for property MemberInfo::Name"); + MethodDescCallSite propGet(pMD, &MemberInfoObj); + + // Prepare the arguments. + ARG_SLOT Args[] = + { + ObjToArgSlot(MemberInfoObj) + }; + + // Retrieve the value of the Name property. + STRINGREF strObj = propGet.Call_RetSTRINGREF(Args); + _ASSERTE(strObj != NULL); + + // Copy the name into the buffer we will return. + int MemberNameLen = strObj->GetStringLength(); + strMemberName = new WCHAR[strObj->GetStringLength() + 1]; + memcpy(strMemberName, strObj->GetBuffer(), MemberNameLen * sizeof(WCHAR)); + strMemberName[MemberNameLen] = 0; + } + } + GCPROTECT_END(); + + strMemberName.SuppressRelease(); + RETURN strMemberName; +} + +void DispatchMemberInfo::DetermineMemberType() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + + // This should not be called more than once. + PRECONDITION(m_enumType == Uninitted); + } + CONTRACTL_END; + + OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + + // Check to see if the member info is of a type we have already seen. + TypeHandle pMemberInfoClass = MemberInfoObj->GetTypeHandle(); + for (int i = 0 ; i < s_iNumMemberTypesKnown ; i++) + { + if (pMemberInfoClass.GetMethodTable() == s_pMemberTypes[i]) + { + m_enumType = s_memberTypes[i]; + return; + } + } + + GCPROTECT_BEGIN(MemberInfoObj); + { + // Retrieve the method descriptor for the type property accessor. + MethodDesc *pMD = MemberLoader::FindPropertyMethod(MemberInfoObj->GetMethodTable(), MEMBERINFO_TYPE_PROP, PropertyGet); + _ASSERTE(pMD && "Unable to find getter method for property MemberInfo::Type"); + MethodDescCallSite propGet(pMD, &MemberInfoObj); + + // Prepare the arguments that will be used to retrieve the value of all the properties. + ARG_SLOT Args[] = + { + ObjToArgSlot(MemberInfoObj) + }; + + // Retrieve the actual type of the member info. + m_enumType = (EnumMemberTypes)propGet.Call_RetArgSlot(Args); + } + GCPROTECT_END(); + + if (s_iNumMemberTypesKnown < NUM_MEMBER_TYPES) + { + s_pMemberTypes[s_iNumMemberTypesKnown] = MemberInfoObj->GetMethodTable(); + s_memberTypes[s_iNumMemberTypesKnown++] = m_enumType; + } +} + +void DispatchMemberInfo::DetermineParamCount() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + // This should not be called more than once. + PRECONDITION(m_iNumParams == -1); + } + CONTRACTL_END; + + MethodDesc *pGetParamsMD = NULL; + + OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + GCPROTECT_BEGIN(MemberInfoObj); + { + // Retrieve the method to use to retrieve the array of parameters. + switch (GetMemberType()) + { + case Method: + { + pGetParamsMD = DispatchInfo::GetMethodInfoMD(METHOD__METHOD__GET_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + _ASSERTE(pGetParamsMD && "Unable to find method MemberBase::GetParameters"); + break; + } + + case Property: + { + pGetParamsMD = DispatchInfo::GetPropertyInfoMD(METHOD__PROPERTY__GET_INDEX_PARAMETERS, ObjectFromHandle(m_hndMemberInfo)->GetTypeHandle()); + _ASSERTE(pGetParamsMD && "Unable to find method PropertyInfo::GetIndexParameters"); + break; + } + } + + // If the member has parameters then get their count. + if (pGetParamsMD != NULL) + { + MethodDescCallSite getParams(pGetParamsMD, &MemberInfoObj); + + ARG_SLOT GetParamsArgs[] = + { + ObjToArgSlot(ObjectFromHandle(m_hndMemberInfo)) + }; + + PTRARRAYREF ParamArray = (PTRARRAYREF) getParams.Call_RetOBJECTREF(GetParamsArgs); + if (ParamArray != NULL) + m_iNumParams = ParamArray->GetNumComponents(); + } + else + { + m_iNumParams = 0; + } + } + GCPROTECT_END(); +} + +void DispatchMemberInfo::DetermineCultureAwareness() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + // This should not be called more than once. + PRECONDITION(m_CultureAwareState == Unknown); + } + CONTRACTL_END; + + // Load the LCIDConversionAttribute type. + MethodTable * pLcIdConvAttrClass = MscorlibBinder::GetClass(CLASS__LCID_CONVERSION_TYPE); + + // Check to see if the attribute is set. + OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + GCPROTECT_BEGIN(MemberInfoObj); + { + // Retrieve the method to use to determine if the DispIdAttribute custom attribute is set. + MethodDesc *pGetCustomAttributesMD = DispatchInfo::GetCustomAttrProviderMD(MemberInfoObj->GetTypeHandle()); + MethodDescCallSite getCustomAttributes(pGetCustomAttributesMD, &MemberInfoObj); + + // Prepare the arguments. + ARG_SLOT GetCustomAttributesArgs[] = + { + 0, + ObjToArgSlot(pLcIdConvAttrClass->GetManagedClassObject()), + 0, + }; + + // Now that we have potentially triggered a GC in the GetManagedClassObject + // call above, it is safe to set the 'this' using our properly protected + // MemberInfoObj value. + GetCustomAttributesArgs[0] = ObjToArgSlot(MemberInfoObj); + + // Retrieve the custom attributes of type LCIDConversionAttribute. + PTRARRAYREF CustomAttrArray = NULL; + EX_TRY + { + CustomAttrArray = (PTRARRAYREF) getCustomAttributes.Call_RetOBJECTREF(GetCustomAttributesArgs); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions) + + GCPROTECT_BEGIN(CustomAttrArray) + { + if ((CustomAttrArray != NULL) && (CustomAttrArray->GetNumComponents() > 0)) + m_CultureAwareState = Aware; + else + m_CultureAwareState = NonAware; + } + GCPROTECT_END(); + } + GCPROTECT_END(); +} + +void DispatchMemberInfo::SetUpParamMarshalerInfo() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + BOOL bSetUpReturnValueOnly = FALSE; + OBJECTREF SetterObj = NULL; + OBJECTREF GetterObj = NULL; + OBJECTREF MemberInfoObj = ObjectFromHandle(m_hndMemberInfo); + + GCPROTECT_BEGIN(SetterObj); + GCPROTECT_BEGIN(GetterObj); + GCPROTECT_BEGIN(MemberInfoObj); + { + MethodTable *pMemberInfoMT = MemberInfoObj->GetMethodTable(); + + if (MscorlibBinder::IsClass(pMemberInfoMT, CLASS__METHOD)) + { + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &MemberInfoObj); + ARG_SLOT arg = ObjToArgSlot(MemberInfoObj); + MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + if (pMeth) + SetUpMethodMarshalerInfo(pMeth, FALSE); + } + else if (MscorlibBinder::IsClass(pMemberInfoMT, CLASS__FIELD)) + { + MethodDescCallSite getFieldHandle(METHOD__RTFIELD__GET_FIELDHANDLE, &MemberInfoObj); + ARG_SLOT arg = ObjToArgSlot(MemberInfoObj); + FieldDesc* pFld = (FieldDesc*) getFieldHandle.Call_RetLPVOID(&arg); + if (pFld) + SetUpFieldMarshalerInfo(pFld); + } + else if (MscorlibBinder::IsClass(pMemberInfoMT, CLASS__PROPERTY)) + { + BOOL isGetter = FALSE; + MethodDescCallSite getSetter(METHOD__PROPERTY__GET_SETTER, &MemberInfoObj); + ARG_SLOT args[] = + { + ObjToArgSlot(MemberInfoObj), + BoolToArgSlot(false) + }; + SetterObj = getSetter.Call_RetOBJECTREF(args); + + if (SetterObj != NULL) + { + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &SetterObj); + ARG_SLOT arg = ObjToArgSlot(SetterObj); + MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + if (pMeth) + { + bSetUpReturnValueOnly = TRUE; + SetUpMethodMarshalerInfo(pMeth, FALSE); + } + } + + MethodDescCallSite getGetter(METHOD__PROPERTY__GET_GETTER, &MemberInfoObj); + ARG_SLOT args1[] = + { + ObjToArgSlot(MemberInfoObj), + BoolToArgSlot(false) + }; + GetterObj = getGetter.Call_RetOBJECTREF(args1); + + if (GetterObj != NULL) + { + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &GetterObj); + ARG_SLOT arg = ObjToArgSlot(GetterObj); + MethodDesc* pMeth = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + if (pMeth) + { + // Only set up the marshalling information for the parameters if we + // haven't done it already for the setter. + SetUpMethodMarshalerInfo(pMeth, bSetUpReturnValueOnly); + } + } + } + else + { + // @FUTURE: Add support for user defined derived classes for + // MethodInfo, PropertyInfo and FieldInfo. + } + } + GCPROTECT_END(); + GCPROTECT_END(); + GCPROTECT_END(); +} + +void DispatchMemberInfo::SetUpMethodMarshalerInfo(MethodDesc *pMD, BOOL bReturnValueOnly) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pMD)); + } + CONTRACTL_END; + + GCX_PREEMP(); + + MetaSig msig(pMD); + LPCSTR szName; + USHORT usSequence; + DWORD dwAttr; + mdParamDef returnParamDef = mdParamDefNil; + mdParamDef currParamDef = mdParamDefNil; + + int numArgs = msig.NumFixedArgs(); + + IMDInternalImport *pInternalImport = msig.GetModule()->GetMDImport(); + + HENUMInternalHolder hEnumParams(pInternalImport); + + // + // Initialize the parameter definition enum. + // + hEnumParams.EnumInit(mdtParamDef, pMD->GetMemberDef()); + + // + // Retrieve the paramdef for the return type and determine which is the next + // parameter that has parameter information. + // + do + { + if (pInternalImport->EnumNext(&hEnumParams, &currParamDef)) + { + IfFailThrow(pInternalImport->GetParamDefProps(currParamDef, &usSequence, &dwAttr, &szName)); + + if (usSequence == 0) + { + // The first parameter, if it has sequence 0, actually describes the return type. + returnParamDef = currParamDef; + } + } + else + { + usSequence = (USHORT)-1; + } + } + while (usSequence == 0); + + // Look up the best fit mapping info via Assembly & Interface level attributes + BOOL BestFit = TRUE; + BOOL ThrowOnUnmappableChar = FALSE; + ReadBestFitCustomAttribute(pMD, &BestFit, &ThrowOnUnmappableChar); + + // + // Unless the bReturnValueOnly flag is set, set up the marshaling info for the parameters. + // + if (!bReturnValueOnly) + { + int iParam = 1; + CorElementType mtype; + while (ELEMENT_TYPE_END != (mtype = msig.NextArg())) + { + // + // Get the parameter token if the current parameter has one. + // + mdParamDef paramDef = mdParamDefNil; + if (usSequence == iParam) + { + paramDef = currParamDef; + + if (pInternalImport->EnumNext(&hEnumParams, &currParamDef)) + { + IfFailThrow(pInternalImport->GetParamDefProps(currParamDef, &usSequence, &dwAttr, &szName)); + + // Validate that the param def tokens are in order. + _ASSERTE((usSequence > iParam) && "Param def tokens are not in order"); + } + else + { + usSequence = (USHORT)-1; + } + } + + + // + // Set up the marshaling info for the parameter. + // + + MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), paramDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, + (CorNativeLinkType)0, (CorNativeLinkFlags)0, + TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE + #ifdef _DEBUG + , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam + #endif + ); + + + // + // Based on the MarshalInfo, set up a DispParamMarshaler for the parameter. + // + SetUpDispParamMarshalerForMarshalInfo(iParam, &Info); + + // + // Get the in/out/ref attributes. + // + SetUpDispParamAttributes(iParam, &Info); + + m_bLastParamOleVarArg |= Info.IsOleVarArgCandidate(); + + // + // Increase the argument index. + // + iParam++; + } + + // Make sure that there are not more param def tokens then there are COM+ arguments. + _ASSERTE( usSequence == (USHORT)-1 && "There are more parameter information tokens then there are COM+ arguments" ); + } + + // + // Set up the marshaling info for the return value. + // + + if (!msig.IsReturnTypeVoid()) + { + MarshalInfo Info(msig.GetModule(), msig.GetReturnProps(), msig.GetSigTypeContext(), returnParamDef, MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, + (CorNativeLinkType)0, (CorNativeLinkFlags)0, + FALSE, 0, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE +#ifdef _DEBUG + , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, 0 +#endif + ); + + SetUpDispParamMarshalerForMarshalInfo(0, &Info); + } +} + +void DispatchMemberInfo::SetUpFieldMarshalerInfo(FieldDesc *pField) +{ + // @TODO(DM): Implement this. + LIMITED_METHOD_CONTRACT; +} + +void DispatchMemberInfo::SetUpDispParamMarshalerForMarshalInfo(int iParam, MarshalInfo *pInfo) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pInfo)); + } + CONTRACTL_END; + + DispParamMarshaler *pDispParamMarshaler = pInfo->GenerateDispParamMarshaler(); + if (pDispParamMarshaler) + { + // If the array of marshalers hasn't been allocated yet, then allocate it. + if (!m_apParamMarshaler) + { + // The array needs to be one more than the number of parameters for + // normal methods and fields and 2 more properties. + EnumMemberTypes MemberType = GetMemberType(); + int NumParamMarshalers = GetNumParameters() + ((MemberType == Property) ? 2 : 1); + m_apParamMarshaler = new DispParamMarshaler*[NumParamMarshalers]; + memset(m_apParamMarshaler, 0, sizeof(DispParamMarshaler*) * NumParamMarshalers); + } + + // Set the DispParamMarshaler in the array. + m_apParamMarshaler[iParam] = pDispParamMarshaler; + + // If the disp param marshaler requires managed cleanup, then set + // m_bRequiresManagedCleanup to TRUE to indicate the method requires + // managed cleanup. + if (pDispParamMarshaler->RequiresManagedCleanup()) + m_bRequiresManagedCleanup = TRUE; + } +} + + +void DispatchMemberInfo::SetUpDispParamAttributes(int iParam, MarshalInfo* Info) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(Info)); + } + CONTRACTL_END; + + // If the arry of In Only parameter indicators hasn't been allocated yet, then allocate it. + if (!m_pParamInOnly) + { + // The array needs to be one more than the number of parameters for + // normal methods and fields and 2 more properties. + EnumMemberTypes MemberType = GetMemberType(); + int NumInOnlyFlags = GetNumParameters() + ((MemberType == Property) ? 2 : 1); + m_pParamInOnly = new BOOL[NumInOnlyFlags]; + memset(m_pParamInOnly, 0, sizeof(BOOL) * NumInOnlyFlags); + } + + m_pParamInOnly[iParam] = ( Info->IsIn() && !Info->IsOut() ); +} + + +//-------------------------------------------------------------------------------- +// The DispatchInfo class implementation. + +DispatchInfo::DispatchInfo(MethodTable *pMT) +: m_pMT(pMT) +, m_pFirstMemberInfo(NULL) +, m_lock(CrstInterop, (CrstFlags)(CRST_HOST_BREAKABLE | CRST_REENTRANCY)) +, m_CurrentDispID(0x10000) +, m_bInvokeUsingInvokeMember(FALSE) +, m_bAllowMembersNotInComMTMemberMap(FALSE) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pMT)); + } + CONTRACTL_END; + + // Init the hashtable. + m_DispIDToMemberInfoMap.Init(DISPID_TO_MEMBER_MAP_INITIAL_SIZE, NULL); +} + +DispatchInfo::~DispatchInfo() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + DispatchMemberInfo* pCurrMember = m_pFirstMemberInfo; + while (pCurrMember) + { + // Retrieve the next member. + DispatchMemberInfo* pNextMember = pCurrMember->m_pNext; + + // Delete the current member. + delete pCurrMember; + + // Process the next member. + pCurrMember = pNextMember; + } +} + +DispatchMemberInfo* DispatchInfo::FindMember(DISPID DispID) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // We need to special case DISPID_UNKNOWN and -2 because the hashtable cannot handle them. + // This is OK since these are invalid DISPID's. + if ((DispID == DISPID_UNKNOWN) || (DispID == -2)) + RETURN NULL; + + // Lookup in the hashtable to find member with the specified DISPID. Note: this hash is unsynchronized, but Gethash + // doesn't require synchronization. + UPTR Data = (UPTR)m_DispIDToMemberInfoMap.Gethash(DispID2HashKey(DispID)); + if (Data != -1) + { + // We have found the member, so ensure it is initialized and return it. + DispatchMemberInfo *pMemberInfo = (DispatchMemberInfo*)Data; + + pMemberInfo->EnsureInitialized(); + + RETURN pMemberInfo; + } + else + { + RETURN NULL; + } +} + +DispatchMemberInfo* DispatchInfo::FindMember(SString& strName, BOOL bCaseSensitive) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + BOOL fFound = FALSE; + + // Go through the list of DispatchMemberInfo's to try and find one with the + // specified name. + DispatchMemberInfo *pCurrMemberInfo = m_pFirstMemberInfo; + while (pCurrMemberInfo) + { + if (ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo) != NULL) + { + // Compare the 2 strings. + if (bCaseSensitive ? + pCurrMemberInfo->m_strName.Equals(strName) : + pCurrMemberInfo->m_strName.EqualsCaseInsensitive(strName)) + { + // We have found the member, so ensure it is initialized and return it. + pCurrMemberInfo->EnsureInitialized(); + + RETURN pCurrMemberInfo; + } + } + + // Process the next member. + pCurrMemberInfo = pCurrMemberInfo->m_pNext; + } + + // No member has been found with the coresponding name. + RETURN NULL; +} + +// Helper method used to create DispatchMemberInfo's. This is only here because +// we can't call new inside a method that has a EX_TRY statement. +DispatchMemberInfo* DispatchInfo::CreateDispatchMemberInfoInstance(DISPID DispID, SString& strMemberName, OBJECTREF MemberInfoObj) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + DispatchMemberInfo* pInfo = new DispatchMemberInfo(this, DispID, strMemberName, MemberInfoObj); + pInfo->SetHandle(MemberInfoObj->GetMethodTable()->GetDomain()->CreateHandle(MemberInfoObj)); + + RETURN pInfo; +} + +// Used for cleanup of managed objects via custom marshalers. This class is stack-allocated +// in code:DispatchInfo.InvokeMemberWorker to guarantee cleanup in the face of exception. +class ManagedParamCleanupHolder +{ + DispatchMemberInfo *m_pDispMemberInfo; + InvokeObjects *m_pObjs; + int m_CleanUpArrayArraySize; + +public: + ManagedParamCleanupHolder(DispatchMemberInfo *pDispMemberInfo, InvokeObjects *pObjs) + : m_pDispMemberInfo(pDispMemberInfo), + m_pObjs(pObjs), + m_CleanUpArrayArraySize(-1) + { + LIMITED_METHOD_CONTRACT; + m_pObjs->CleanUpArray = NULL; + } + + void SetData(PTRARRAYREF pCleanUpArray, int iCleanUpArrayArraySize) + { + LIMITED_METHOD_CONTRACT; + m_CleanUpArrayArraySize = iCleanUpArrayArraySize; + m_pObjs->CleanUpArray = pCleanUpArray; + } + + ~ManagedParamCleanupHolder() + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + // If the member info requires managed object cleanup, then do it now. + if (m_pObjs->CleanUpArray != NULL && m_pDispMemberInfo->RequiresManagedObjCleanup()) + { + GCX_COOP(); + _ASSERTE(m_CleanUpArrayArraySize != -1); + + for (int i = 0; i < m_CleanUpArrayArraySize; i++) + { + // Clean up all the managed parameters that were generated. + m_pObjs->TmpObj = m_pObjs->CleanUpArray->GetAt(i); + m_pDispMemberInfo->CleanUpParamManaged(i, &m_pObjs->TmpObj); + } + } + } +}; + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +void DispatchInfo::InvokeMemberWorker(DispatchMemberInfo* pDispMemberInfo, + InvokeObjects* pObjs, + int NumParams, + int NumArgs, + int NumNamedArgs, + int& NumByrefArgs, + int& iSrcArg, + DISPID id, + DISPPARAMS* pdp, + VARIANT* pVarRes, + WORD wFlags, + LCID lcid, + DISPID* pSrcArgNames, + VARIANT* pSrcArgs, + OBJECTHANDLE* aByrefStaticArrayBackupObjHandle, + int* pManagedMethodParamIndexMap, + VARIANT** aByrefArgOleVariant) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + // there are too many fields in pObjs, here I assume once one of them is + // protected, the whole structure is protected. + PRECONDITION(IsProtectedByGCFrame(&pObjs->MemberInfo)); + } + CONTRACTL_END; + + int iDestArg; + ManagedParamCleanupHolder cleanupHolder(pDispMemberInfo, pObjs); + BOOL bPropValIsByref = FALSE; + EnumMemberTypes MemberType; + + Thread* pThread = GetThread(); + AppDomain* pAppDomain = pThread->GetDomain(); + + SafeArrayHolder pSA(NULL); + VARIANT safeArrayVar; + HRESULT hr; + + // Allocate the array of used flags. + BYTE *aArgUsedFlags = (BYTE*)_alloca(NumParams * sizeof(BYTE)); + memset(aArgUsedFlags, 0, NumParams * sizeof(BYTE)); + + size_t cbByrefArgMngVariantIndex; + if (!ClrSafeInt<size_t>::multiply(sizeof(DWORD), NumArgs, cbByrefArgMngVariantIndex)) + ThrowHR(COR_E_OVERFLOW); + + DWORD *aByrefArgMngVariantIndex = (DWORD *)_alloca(cbByrefArgMngVariantIndex); + + + // + // Retrieve information required for the invoke call. + // + + pObjs->OleAutBinder = DispatchInfo::GetOleAutBinder(); + + + // + // Allocate the array of arguments + // + + // Allocate the array that will contain the converted variants in the right order. + // If the invoke is for a PROPUT or a PROPPUTREF and we are going to call through + // invoke member then allocate the array one bigger to allow space for the property + // value. + int ArraySize = NumParams; + if (m_bInvokeUsingInvokeMember && (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))) + { + if (!ClrSafeInt<int>::addition(ArraySize, 1, ArraySize)) + ThrowHR(COR_E_OVERFLOW); + } + + pObjs->ParamArray = (PTRARRAYREF)AllocateObjectArray(ArraySize, g_pObjectClass); + + + // + // Convert the property set argument if the invoke is a PROPERTYPUT OR PROPERTYPUTREF. + // + + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + // Convert the variant. + VARIANT *pSrcOleVariant = RetrieveSrcVariant(&pdp->rgvarg[0]); + MarshalParamNativeToManaged(pDispMemberInfo, NumArgs, pSrcOleVariant, &pObjs->PropVal); + + // Remember if the property value is byref or not. + bPropValIsByref = V_VT(pSrcOleVariant) & VT_BYREF; + + // If the variant is a byref static array, then remember the property value. + if (IsVariantByrefStaticArray(pSrcOleVariant)) + SetObjectReference(&pObjs->ByrefStaticArrayBackupPropVal, pObjs->PropVal, pAppDomain); + } + + + // + // Convert the named arguments. + // + + if (!m_bInvokeUsingInvokeMember) + { + for (iSrcArg = 0; iSrcArg < NumNamedArgs; iSrcArg++) + { + // Determine the destination index. + iDestArg = pSrcArgNames[iSrcArg]; + + // Check for duplicate param DISPID's. + if (aArgUsedFlags[iDestArg] != 0) + COMPlusThrowHR(DISP_E_PARAMNOTFOUND); + + // Convert the variant. + VARIANT *pSrcOleVariant = RetrieveSrcVariant(&pSrcArgs[iSrcArg]); + MarshalParamNativeToManaged(pDispMemberInfo, iDestArg, pSrcOleVariant, &pObjs->TmpObj); + pObjs->ParamArray->SetAt(iDestArg, pObjs->TmpObj); + + // If the argument is byref then add it to the array of byref arguments. + if (V_VT(pSrcOleVariant) & VT_BYREF) + { + // Remember what arg this really is. + pManagedMethodParamIndexMap[NumByrefArgs] = iDestArg; + + aByrefArgOleVariant[NumByrefArgs] = pSrcOleVariant; + aByrefArgMngVariantIndex[NumByrefArgs] = iDestArg; + + // If the variant is a byref static array, then remember the objectref we + // converted the variant to. + if (IsVariantByrefStaticArray(pSrcOleVariant)) + aByrefStaticArrayBackupObjHandle[NumByrefArgs] = pAppDomain->CreateHandle(pObjs->TmpObj); + + NumByrefArgs++; + } + + // Mark the slot the argument is in as occupied. + aArgUsedFlags[iDestArg] = 1; + } + } + else + { + for (iSrcArg = 0, iDestArg = 0; iSrcArg < NumNamedArgs; iSrcArg++, iDestArg++) + { + // Check for duplicate param DISPID's. + if (aArgUsedFlags[iDestArg] != 0) + COMPlusThrowHR(DISP_E_PARAMNOTFOUND); + + // Convert the variant. + VARIANT *pSrcOleVariant = RetrieveSrcVariant(&pSrcArgs[iSrcArg]); + MarshalParamNativeToManaged(pDispMemberInfo, iDestArg, pSrcOleVariant, &pObjs->TmpObj); + pObjs->ParamArray->SetAt(iDestArg, pObjs->TmpObj); + + // If the argument is byref then add it to the array of byref arguments. + if (V_VT(pSrcOleVariant) & VT_BYREF) + { + // Remember what arg this really is. + pManagedMethodParamIndexMap[NumByrefArgs] = iDestArg; + + aByrefArgOleVariant[NumByrefArgs] = pSrcOleVariant; + aByrefArgMngVariantIndex[NumByrefArgs] = iDestArg; + + // If the variant is a byref static array, then remember the objectref we + // converted the variant to. + if (IsVariantByrefStaticArray(pSrcOleVariant)) + aByrefStaticArrayBackupObjHandle[NumByrefArgs] = pAppDomain->CreateHandle(pObjs->TmpObj); + + NumByrefArgs++; + } + + // Mark the slot the argument is in as occupied. + aArgUsedFlags[iDestArg] = 1; + } + } + + + // + // Fill in the positional arguments. These are copied in reverse order and we also + // need to skip the arguments already filled in by named arguments. + // + BOOL bLastParamOleVarArg = pDispMemberInfo && pDispMemberInfo->IsLastParamOleVarArg(); + BOOL bByRefArg; + + // We support VarArg by aligning with the behavior of params array in C#. + // Here are things we do for callers depends on the arguments it passes: + // a) NumArgs == NumParams -1: + // We generate a SAFEARRAY with 0 elements and pass the VARIANT + // wrapping it to the callee + // b) NumArgs == NumParams && the first argument is NOT safearray: + // Note that arguments are passed from right to left so that the first argument + // passed by caller should be mapped to the last parameter of the callee + // We generate a SAFEARRAY to wrap the argument and pass the VARIANT + // wrapping the SAFEARRAY to the callee + // c) NumArgs == NumParams && the first argument is safearray: + // We directly pass it to the callee. To compact with v2 behavior, we loose the + // conditions by checking if the VT of the safearray varaint is VT_ARRAY only + // d) NumArgs > NumParams: + // We generate a SAFEARRAY to wrap then and pass the VARIANT wrapping + // the SAFEARRAY to the callee + for (iSrcArg = NumArgs - 1, iDestArg = 0; + iSrcArg >= NumNamedArgs || (iDestArg == NumParams - 1 && bLastParamOleVarArg)/* for vararg case a) */; + iSrcArg--, iDestArg++) + { + // Skip the arguments already filled in by named args. + for (; aArgUsedFlags[iDestArg] != 0; iDestArg++); + _ASSERTE(iDestArg < NumParams); + + // Convert the variant. + VARIANT *pSrcOleVariant = NULL; + VARIANT *pFrstVarargOleVariant = NULL; + BOOL bSrcOleVariantCached = FALSE; + bByRefArg = FALSE; + if (iDestArg == NumParams-1 && bLastParamOleVarArg) + { + // VarArg scenario + BOOL bSrcArgIsSafeArray = FALSE; + if (iSrcArg == NumNamedArgs) + { + pSrcOleVariant = RetrieveSrcVariant(&pSrcArgs[iSrcArg]); + bSrcOleVariantCached = TRUE; + if ((V_VT(pSrcOleVariant) == (VT_ARRAY | VT_VARIANT)) || + (V_VT(pSrcOleVariant) == VT_ARRAY) // see the comments in case c) above + ) + { + // vararg case c) + bSrcArgIsSafeArray = TRUE; + bByRefArg = V_VT(pSrcOleVariant) & VT_BYREF; + } + } + + if (!bSrcArgIsSafeArray) + { + // vararg case a), b) and d) + // 1. Construct a safearray + LONG lSafeArrayArg = 0; + bByRefArg = FALSE; + pSA = SafeArrayCreateVector(VT_VARIANT, 0, iSrcArg - NumNamedArgs + 1); + if (pSA.GetValue() == NULL) + COMPlusThrowHR(E_OUTOFMEMORY); + V_VT(&safeArrayVar) = VT_VARIANT | VT_ARRAY; + V_ARRAY(&safeArrayVar) = pSA; + + // 2. Put the remaining srcArg into the safearray + for (; iSrcArg >= NumNamedArgs; iSrcArg--, lSafeArrayArg++) + { + if (!bSrcOleVariantCached) + pSrcOleVariant = RetrieveSrcVariant(&pSrcArgs[iSrcArg]); + else + bSrcOleVariantCached = FALSE; + if (FAILED(hr = SafeArrayPutElement(pSA, &lSafeArrayArg, pSrcOleVariant))) + COMPlusThrowHR(hr); + // Handle the UnMarshal Scenario + if (lSafeArrayArg == 0) + pFrstVarargOleVariant = pSrcOleVariant; + // If any of the VARIANTS which are put into safearray is BYREF, we need marshal back it + bByRefArg |= V_VT(pSrcOleVariant) & VT_BYREF; + } + + // 3. Adjust the pSrcOleVariant in order to marshal to the params array in managed side + pSrcOleVariant = &safeArrayVar; + } + } + else + { + pSrcOleVariant = RetrieveSrcVariant(&pSrcArgs[iSrcArg]); + bByRefArg = V_VT(pSrcOleVariant) & VT_BYREF; + } + + + MarshalParamNativeToManaged(pDispMemberInfo, iDestArg, pSrcOleVariant, &pObjs->TmpObj); + pObjs->ParamArray->SetAt(iDestArg, pObjs->TmpObj); + + // If the argument is byref then add it to the array of byref arguments. + if (bByRefArg) + { + // Remember what arg this really is. + pManagedMethodParamIndexMap[NumByrefArgs] = iDestArg; + + // Remember the original variant so that we can unmarshal it back + // Note that when pSA is set, pSrcOleVaraint is re-write so that we use the first argument + // of vararg instead + if (pSA != NULL) + aByrefArgOleVariant[NumByrefArgs] = pFrstVarargOleVariant; + else + aByrefArgOleVariant[NumByrefArgs] = pSrcOleVariant; + + aByrefArgMngVariantIndex[NumByrefArgs] = iDestArg; + + // If the variant is a byref static array, then remember the objectref we + // converted the variant to. + if (IsVariantByrefStaticArray(pSrcOleVariant)) + aByrefStaticArrayBackupObjHandle[NumByrefArgs] = pAppDomain->CreateHandle(pObjs->TmpObj); + + NumByrefArgs++; + } + } + + // Set the source arg back to -1 to indicate we are finished converting args. + iSrcArg = -1; + + + // + // Fill in all the remaining arguments with Missing.Value. + // + + for (; iDestArg < NumParams; iDestArg++) + { + if (aArgUsedFlags[iDestArg] == 0) + { + pObjs->ParamArray->SetAt(iDestArg, pAppDomain->GetMissingObject()); + } + } + + + // + // Set up the binding flags to pass to reflection. + // + + int BindingFlags = ConvertInvokeFlagsToBindingFlags(wFlags) | BINDER_OptionalParamBinding; + + + // + // Do the actual invocation on the member info. + // + + if (!m_bInvokeUsingInvokeMember) + { + PREFIX_ASSUME(pDispMemberInfo != NULL); + + if (pDispMemberInfo->IsCultureAware()) + { + // If the method is culture aware, then set the specified culture on the thread. + GetCultureInfoForLCID(lcid, &pObjs->CultureInfo); + pObjs->OldCultureInfo = pThread->GetCulture(FALSE); + pThread->SetCultureId(lcid, FALSE); + } + + // If the method has custom marshalers then we will need to call + // the clean up method on the objects. So we need to make a copy of the + // ParamArray since it might be changed by reflection if any of the + // parameters are byref. + if (pDispMemberInfo->RequiresManagedObjCleanup()) + { + // Allocate the clean up array. + int CleanUpArrayArraySize = NumParams; + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + if (!ClrSafeInt<int>::addition(CleanUpArrayArraySize, 1, CleanUpArrayArraySize)) + ThrowHR(COR_E_OVERFLOW); + } + cleanupHolder.SetData((PTRARRAYREF)AllocateObjectArray(CleanUpArrayArraySize, g_pObjectClass), CleanUpArrayArraySize); + _ASSERTE(pObjs->CleanUpArray != NULL); + + // Copy the parameters into the clean up array. + for (int i = 0; i < ArraySize; i++) + pObjs->CleanUpArray->SetAt(i, pObjs->ParamArray->GetAt(i)); + + // If this invoke is for a PROPUT or PROPPUTREF, then add the property object to + // the end of the clean up array. + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + pObjs->CleanUpArray->SetAt(NumParams, pObjs->PropVal); + } + + // Retrieve the member info object and the type of the member. + pObjs->MemberInfo = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + MemberType = pDispMemberInfo->GetMemberType(); + + // Determine whether the member has a link time security check. If so we + // need to emulate this (since the caller is obviously not jitted in this + // case). Only methods and properties can have a link time check. + MethodDesc *pMDforSecurity = NULL; + + if (MemberType == Method) + { + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &pObjs->MemberInfo); + ARG_SLOT arg = ObjToArgSlot(pObjs->MemberInfo); + pMDforSecurity = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + } + else if (MemberType == Property) + { + MethodDescCallSite getSetter(METHOD__PROPERTY__GET_SETTER, &pObjs->MemberInfo); + ARG_SLOT args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + BoolToArgSlot(false) + }; + OBJECTREF method = getSetter.Call_RetOBJECTREF(args); + if (method == NULL) + { + MethodDescCallSite getGetter(METHOD__PROPERTY__GET_GETTER, &pObjs->MemberInfo); + ARG_SLOT args1[] = + { + ObjToArgSlot(pObjs->MemberInfo), + BoolToArgSlot(false) + }; + method = getGetter.Call_RetOBJECTREF(args1); + } + + if (method != NULL) + { + GCPROTECT_BEGIN(method) + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &method); + ARG_SLOT arg = ObjToArgSlot(method); + pMDforSecurity = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + GCPROTECT_END(); + } + } + + if (pMDforSecurity) + Security::CheckLinkDemandAgainstAppDomain(pMDforSecurity); + + switch (MemberType) + { + case Field: + { + // Make sure this invoke is actually for a property put or get. + if (wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET)) + { + // Do some more validation now that we know the type of the invocation. + if (NumNamedArgs != 0) + COMPlusThrowHR(DISP_E_NONAMEDARGS); + if (NumArgs != 0) + COMPlusThrowHR(DISP_E_BADPARAMCOUNT); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetFieldInfoMD(METHOD__FIELD__GET_VALUE, pObjs->MemberInfo->GetTypeHandle()); + MethodDescCallSite getValue(pMD, &pObjs->MemberInfo); + + // Prepare the arguments that will be passed to Invoke. + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + ObjToArgSlot(pObjs->Target), + }; + + // Do the actual method invocation. + pObjs->RetVal = getValue.Call_RetOBJECTREF(Args); + } + else if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + // Do some more validation now that we know the type of the invocation. + if (NumArgs != 0) + COMPlusThrowHR(DISP_E_BADPARAMCOUNT); + if (NumNamedArgs != 0) + COMPlusThrowHR(DISP_E_NONAMEDARGS); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetFieldInfoMD(METHOD__FIELD__SET_VALUE, pObjs->MemberInfo->GetTypeHandle()); + MethodDescCallSite setValue(pMD, &pObjs->MemberInfo); + + // Prepare the arguments that will be passed to Invoke. + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + ObjToArgSlot(pObjs->Target), + ObjToArgSlot(pObjs->PropVal), + (ARG_SLOT) BindingFlags, + ObjToArgSlot(pObjs->OleAutBinder), + ObjToArgSlot(pObjs->CultureInfo), + }; + + // Do the actual method invocation. + setValue.Call(Args); + } + else + { + COMPlusThrowHR(DISP_E_MEMBERNOTFOUND); + } + + break; + } + + case Property: + { + // Make sure this invoke is actually for a property put or get. + if (wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET)) + { + if (!IsPropertyAccessorVisible(false, &pObjs->MemberInfo)) + COMPlusThrowHR(DISP_E_MEMBERNOTFOUND); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetPropertyInfoMD(METHOD__PROPERTY__GET_VALUE, pObjs->MemberInfo->GetTypeHandle()); + MethodDescCallSite getValue(pMD, &pObjs->MemberInfo); + + // Prepare the arguments that will be passed to GetValue(). + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + ObjToArgSlot(pObjs->Target), + (ARG_SLOT) BindingFlags, + ObjToArgSlot(pObjs->OleAutBinder), + ObjToArgSlot(pObjs->ParamArray), + ObjToArgSlot(pObjs->CultureInfo), + }; + + // Do the actual method invocation. + pObjs->RetVal = getValue.Call_RetOBJECTREF(Args); + } + else if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + if (!IsPropertyAccessorVisible(true, &pObjs->MemberInfo)) + COMPlusThrowHR(DISP_E_MEMBERNOTFOUND); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetPropertyInfoMD(METHOD__PROPERTY__SET_VALUE, pObjs->MemberInfo->GetTypeHandle()); + MethodDescCallSite setValue(pMD, &pObjs->MemberInfo); + + // Prepare the arguments that will be passed to SetValue(). + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + ObjToArgSlot(pObjs->Target), + ObjToArgSlot(pObjs->PropVal), + (ARG_SLOT) BindingFlags, + ObjToArgSlot(pObjs->OleAutBinder), + ObjToArgSlot(pObjs->ParamArray), + ObjToArgSlot(pObjs->CultureInfo), + }; + + // Do the actual method invocation. + setValue.Call(Args); + } + else + { + COMPlusThrowHR(DISP_E_MEMBERNOTFOUND); + } + + break; + } + + case Method: + { + // Make sure this invoke is actually for a method. We also allow + // prop gets since it is harmless and it allows the user a bit + // more freedom. + if (!(wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET))) + COMPlusThrowHR(DISP_E_MEMBERNOTFOUND); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetMethodInfoMD(METHOD__METHOD__INVOKE, pObjs->MemberInfo->GetTypeHandle()); + MethodDescCallSite invoke(pMD, &pObjs->MemberInfo); + + // Prepare the arguments that will be passed to Invoke. + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->MemberInfo), + ObjToArgSlot(pObjs->Target), + (ARG_SLOT) BindingFlags, + ObjToArgSlot(pObjs->OleAutBinder), + ObjToArgSlot(pObjs->ParamArray), + ObjToArgSlot(pObjs->CultureInfo), + }; + + // Do the actual method invocation. + pObjs->RetVal = invoke.Call_RetOBJECTREF(Args); + break; + } + + default: + { + COMPlusThrowHR(E_UNEXPECTED); + } + } + } + else + { + // Convert the LCID into a CultureInfo. + GetCultureInfoForLCID(lcid, &pObjs->CultureInfo); + + pObjs->ReflectionObj = GetReflectionObject(); + + // Retrieve the method descriptor that will be called on. + MethodDesc *pMD = GetInvokeMemberMD(); + MethodDescCallSite invokeMember(pMD, &pObjs->ReflectionObj); + + // Allocate the string that will contain the name of the member. + if (!pDispMemberInfo) + { + WCHAR strTmp[64]; + _snwprintf_s(strTmp, NumItems(strTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, id); + pObjs->MemberName = (OBJECTREF)StringObject::NewString(strTmp); + } + else + { + pObjs->MemberName = (OBJECTREF)StringObject::NewString(pDispMemberInfo->m_strName.GetUnicode()); + } + + // If there are named arguments, then set up the array of named arguments + // to pass to InvokeMember. + if (NumNamedArgs > 0) + SetUpNamedParamArray(pDispMemberInfo, pSrcArgNames, NumNamedArgs, &pObjs->NamedArgArray); + + // If this is a PROPUT or a PROPPUTREF then we need to add the value + // being set as the last argument in the argument array. + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + pObjs->ParamArray->SetAt(NumParams, pObjs->PropVal); + + // Prepare the arguments that will be passed to Invoke. + ARG_SLOT Args[] = + { + ObjToArgSlot(pObjs->ReflectionObj), + ObjToArgSlot(pObjs->MemberName), + (ARG_SLOT) BindingFlags, + ObjToArgSlot(pObjs->OleAutBinder), + ObjToArgSlot(pObjs->Target), + ObjToArgSlot(pObjs->ParamArray), + ObjToArgSlot(NULL), // @TODO(DM): Look into setting the byref modifiers. + ObjToArgSlot(pObjs->CultureInfo), + ObjToArgSlot(pObjs->NamedArgArray), + }; + + // Do the actual method invocation. + pObjs->RetVal = invokeMember.Call_RetOBJECTREF(Args); + } + + + // + // Convert the return value and the byref arguments. + // + + // If the property value is byref then convert it back. + if (bPropValIsByref) + MarshalParamManagedToNativeRef(pDispMemberInfo, NumArgs, &pObjs->PropVal, &pObjs->ByrefStaticArrayBackupPropVal, &pdp->rgvarg[0]); + + // Convert all the ByRef arguments back. + for (int i = 0; i < NumByrefArgs; i++) + { + // Get the real parameter index for this arg. + int iParamIndex = pManagedMethodParamIndexMap[i]; + + if (!pDispMemberInfo || m_bInvokeUsingInvokeMember || !pDispMemberInfo->IsParamInOnly(iParamIndex)) + { + pObjs->TmpObj = pObjs->ParamArray->GetAt(aByrefArgMngVariantIndex[i]); + if (pSA != NULL && iParamIndex == NumParams -1) + { + // VarArg scenario + // Here we only unmarshal the object whose corresponding VARIANT is VarArg + OleVariant::MarshalVariantArrayComToOle((BASEARRAYREF*)&pObjs->TmpObj, (void *)(aByrefArgOleVariant[i]), NULL, TRUE, FALSE, TRUE, TRUE, -1); + } + else + { + MarshalParamManagedToNativeRef(pDispMemberInfo, iParamIndex, &pObjs->TmpObj, (OBJECTREF*)aByrefStaticArrayBackupObjHandle[i], aByrefArgOleVariant[i]); + } + } + + if (aByrefStaticArrayBackupObjHandle[i]) + { + DestroyHandle(aByrefStaticArrayBackupObjHandle[i]); + aByrefStaticArrayBackupObjHandle[i] = NULL; + } + } + + // Convert the return COM+ object to an OLE variant. + if (pVarRes) + MarshalReturnValueManagedToNative(pDispMemberInfo, &pObjs->RetVal, pVarRes); +} +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +void DispatchInfo::InvokeMemberDebuggerWrapper( + DispatchMemberInfo* pDispMemberInfo, + InvokeObjects* pObjs, + int NumParams, + int NumArgs, + int NumNamedArgs, + int& NumByrefArgs, + int& iSrcArg, + DISPID id, + DISPPARAMS* pdp, + VARIANT* pVarRes, + WORD wFlags, + LCID lcid, + DISPID* pSrcArgNames, + VARIANT* pSrcArgs, + OBJECTHANDLE* aByrefStaticArrayBackupObjHandle, + int* pManagedMethodParamIndexMap, + VARIANT** aByrefArgOleVariant, + Frame * pFrame) + +{ + // Use static contracts b/c we have SEH. + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_ANY; + + // @todo - we have a PAL_TRY/PAL_EXCEPT here as a general (cross-platform) way to get a 1st-pass + // filter. If that's bad perf, we could inline an FS:0 handler for x86-only; and then inline + // both this wrapper and the main body. + + struct Param : public NotifyOfCHFFilterWrapperParam + { + DispatchInfo* pThis; + DispatchMemberInfo* pDispMemberInfo; + InvokeObjects* pObjs; + int NumParams; + int NumArgs; + int NumNamedArgs; + int& NumByrefArgs; + int& iSrcArg; + DISPID id; + DISPPARAMS* pdp; + VARIANT* pVarRes; + WORD wFlags; + LCID lcid; + DISPID* pSrcArgNames; + VARIANT* pSrcArgs; + OBJECTHANDLE* aByrefStaticArrayBackupObjHandle; + int* pManagedMethodParamIndexMap; + VARIANT** aByrefArgOleVariant; + + Param(int& _NumByrefArgs, int& _iSrcArg) + : NumByrefArgs(_NumByrefArgs), iSrcArg(_iSrcArg) + {} + } param(NumByrefArgs, iSrcArg); + + param.pFrame = GetThread()->GetFrame(); // Inherited from NotifyOfCHFFilterWrapperParam + param.pThis = this; + param.pDispMemberInfo = pDispMemberInfo; + param.pObjs = pObjs; + param.NumParams = NumParams; + param.NumArgs = NumArgs; + param.NumNamedArgs = NumNamedArgs; + //param.NumByrefArgs = NumByrefArgs; + //param.iSrcArg = iSrcArg; + param.id = id; + param.pdp = pdp; + param.pVarRes = pVarRes; + param.wFlags = wFlags; + param.lcid = lcid; + param.pSrcArgNames = pSrcArgNames; + param.pSrcArgs = pSrcArgs; + param.aByrefStaticArrayBackupObjHandle = aByrefStaticArrayBackupObjHandle; + param.pManagedMethodParamIndexMap = pManagedMethodParamIndexMap; + param.aByrefArgOleVariant = aByrefArgOleVariant; + + PAL_TRY(Param *, pParam, ¶m) + { + pParam->pThis->InvokeMemberWorker(pParam->pDispMemberInfo, + pParam->pObjs, + pParam->NumParams, + pParam->NumArgs, + pParam->NumNamedArgs, + pParam->NumByrefArgs, + pParam->iSrcArg, + pParam->id, + pParam->pdp, + pParam->pVarRes, + pParam->wFlags, + pParam->lcid, + pParam->pSrcArgNames, + pParam->pSrcArgs, + pParam->aByrefStaticArrayBackupObjHandle, + pParam->pManagedMethodParamIndexMap, + pParam->aByrefArgOleVariant); + } + PAL_EXCEPT_FILTER(NotifyOfCHFFilterWrapper) + { + // Should never reach here b/c handler should always continue search. + _ASSERTE(false); + } + PAL_ENDTRY +} + +// Helper method that invokes the member with the specified DISPID. +HRESULT DispatchInfo::InvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pVarRes, EXCEPINFO *pei, IServiceProvider *pspCaller, unsigned int *puArgErr) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pSimpleWrap)); + PRECONDITION(CheckPointer(pdp, NULL_OK)); + PRECONDITION(CheckPointer(pVarRes, NULL_OK)); + PRECONDITION(CheckPointer(pei, NULL_OK)); + PRECONDITION(CheckPointer(pspCaller, NULL_OK)); + PRECONDITION(CheckPointer(puArgErr, NULL_OK)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + int iSrcArg = -1; + int iBaseErrorArg = 0; + int NumArgs; + int NumNamedArgs; + int NumParams; + InvokeObjects Objs; + DISPID *pSrcArgNames = NULL; + VARIANT *pSrcArgs = NULL; + ULONG_PTR ulActCtxCookie = 0; + Thread *pThread = GetThread(); + + // + // Validate the arguments. + // + + if (!pdp) + return E_POINTER; + if (!pdp->rgvarg && (pdp->cArgs > 0)) + return E_INVALIDARG; + if (!pdp->rgdispidNamedArgs && (pdp->cNamedArgs > 0)) + return E_INVALIDARG; + if (pdp->cNamedArgs > pdp->cArgs) + return E_INVALIDARG; + if ((int)pdp->cArgs < 0 || (int)pdp->cNamedArgs < 0) + return E_INVALIDARG; + + + // + // Clear the out arguments before we start. + // + + if (pVarRes) + SafeVariantClear(pVarRes); + if (puArgErr) + *puArgErr = -1; + + + // + // Convert the default LCID's to actual LCID's. + // + + if(lcid == LOCALE_SYSTEM_DEFAULT || lcid == 0) + lcid = GetSystemDefaultLCID(); + + if(lcid == LOCALE_USER_DEFAULT) + lcid = GetUserDefaultLCID(); + + // + // Set the value of the variables we use internally. + // + + NumArgs = pdp->cArgs; + NumNamedArgs = pdp->cNamedArgs; + memset(&Objs, 0, sizeof(InvokeObjects)); + + if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + // Since this invoke is for a property put or put ref we need to add 1 to + // the iSrcArg to get the argument that is in error. + iBaseErrorArg = 1; + + if (NumArgs < 1) + { + return DISP_E_BADPARAMCOUNT; + } + else + { + NumArgs--; + pSrcArgs = &pdp->rgvarg[1]; + } + + if (NumNamedArgs < 1) + { + if (NumNamedArgs < 0) + return DISP_E_BADPARAMCOUNT; + + // Verify if we really want to do this or return E_INVALIDARG instead. + _ASSERTE(NumNamedArgs == 0); + _ASSERTE(pSrcArgNames == NULL); + } + else + { + NumNamedArgs--; + pSrcArgNames = &pdp->rgdispidNamedArgs[1]; + } + } + else + { + pSrcArgs = pdp->rgvarg; + pSrcArgNames = pdp->rgdispidNamedArgs; + } + + // + // Do a lookup in the hashtable to find the DispatchMemberInfo for the DISPID. + // + + DispatchMemberInfo *pDispMemberInfo = FindMember(id); + if (!pDispMemberInfo || !(*((Object **)pDispMemberInfo->m_hndMemberInfo))) + { + pDispMemberInfo = NULL; + } + else if (pDispMemberInfo->IsNeutered()) + { + COMPlusThrow(kInvalidOperationException); + } + + // + // If the member is not known then make sure that the DispatchInfo we have + // supports unknown members. + // + + if (m_bInvokeUsingInvokeMember) + { + // Since we do not have any information regarding the member then we + // must assume the number of formal parameters matches the number of args. + NumParams = NumArgs; + } + else + { + // If we haven't found the member then fail the invoke call. + if (!pDispMemberInfo) + return DISP_E_MEMBERNOTFOUND; + + // DISPATCH_CONSTRUCT only works when calling InvokeMember. + if (wFlags & DISPATCH_CONSTRUCT) + return E_INVALIDARG; + + // We have the member so retrieve the number of formal parameters. + NumParams = pDispMemberInfo->GetNumParameters(); + + if (pDispMemberInfo->IsLastParamOleVarArg()) + { + // named args aren't allowed in a vararg function, + // unless it's a lone DISPID_PROPERTYPUT (note that we already decrement + // the value of NumNamedArgs for DISPID_PROPERTYPUT so that no special + // check needed be done here for it + // the logic is borrowed from the one in OLEAUT32!CTypeInfo2::Invoke + if (NumNamedArgs > 0) + return DISP_E_NONAMEDARGS; + } + else + { + // Make sure the number of arguments does not exceed the number of parameters. + if(NumArgs > NumParams) + return DISP_E_BADPARAMCOUNT; + } + + // Validate that all the named arguments are known. + for (iSrcArg = 0; iSrcArg < NumNamedArgs; iSrcArg++) + { + // There are some members we do not know about so we will call InvokeMember() + // passing in the DISPID's directly so the caller can try to handle them. + if (pSrcArgNames[iSrcArg] < 0 || pSrcArgNames[iSrcArg] >= NumParams) + return DISP_E_MEMBERNOTFOUND; + } + } + + OBJECTREF pThrowable = NULL; + + // + // The member is present so we need to convert the arguments and then do the + // actual invocation. + // + GCPROTECT_BEGIN(pThrowable); + GCPROTECT_BEGIN(Objs); + { + // + // Allocate information used by the method. + // + + int NumByrefArgs = 0; + + // Allocate the array of backup byref static array objects. + size_t cbStaticArrayBackupObjHandle; + if (!ClrSafeInt<size_t>::multiply(sizeof(OBJECTHANDLE *), NumArgs, cbStaticArrayBackupObjHandle)) + ThrowHR(COR_E_OVERFLOW); + + OBJECTHANDLE *aByrefStaticArrayBackupObjHandle = (OBJECTHANDLE *)_alloca(cbStaticArrayBackupObjHandle); + memset(aByrefStaticArrayBackupObjHandle, 0, cbStaticArrayBackupObjHandle); + + // Allocate the array that maps method params to their indices. + size_t cbManagedMethodParamIndexMap; + if (!ClrSafeInt<size_t>::multiply(sizeof(int), NumArgs, cbManagedMethodParamIndexMap)) + ThrowHR(COR_E_OVERFLOW); + + int *pManagedMethodParamIndexMap = (int *)_alloca(cbManagedMethodParamIndexMap); + + // Allocate the array of byref objects. + size_t cbByrefArgOleVariant; + if (!ClrSafeInt<size_t>::multiply(sizeof(VARIANT *), NumArgs, cbByrefArgOleVariant)) + ThrowHR(COR_E_OVERFLOW); + + VARIANT **aByrefArgOleVariant = (VARIANT **)_alloca(cbByrefArgOleVariant); + + Objs.Target = pSimpleWrap->GetObjectRef(); + + // + // Invoke the method. + // + + // The sole purpose of having this frame is to tell the debugger that we have a catch handler here + // which may swallow managed exceptions. The debugger needs this in order to send a + // CatchHandlerFound (CHF) notification. + FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame; + EX_TRY + { + InvokeMemberDebuggerWrapper(pDispMemberInfo, + &Objs, + NumParams, + NumArgs, + NumNamedArgs, + NumByrefArgs, + iSrcArg, + id, + pdp, + pVarRes, + wFlags, + lcid, + pSrcArgNames, + pSrcArgs, + aByrefStaticArrayBackupObjHandle, + pManagedMethodParamIndexMap, + aByrefArgOleVariant, + &catchFrame); + } + EX_CATCH + { + pThrowable = GET_THROWABLE(); + + // RethrowCorruptingExceptionsEx, in EX_END_CATCH below, will ensure that CEs are rethrown. + } + EX_END_CATCH(RethrowCorruptingExceptionsEx(!CEHelper::CanIDispatchTargetHandleException())) + catchFrame.Pop(); + + if (pThrowable != NULL) + { + // Do cleanup - make sure that return value and outgoing arguments are cleared + if (pVarRes != NULL) + SafeVariantClear(pVarRes); + + for (int i = 0; i < NumByrefArgs; i++) + { + if (!pDispMemberInfo || m_bInvokeUsingInvokeMember || !pDispMemberInfo->IsParamInOnly(i)) + { + // Out and in/out byref arguments are outgoing and should be cleared + CleanUpNativeParam(pDispMemberInfo, pManagedMethodParamIndexMap[i], (OBJECTREF *)aByrefStaticArrayBackupObjHandle[i], aByrefArgOleVariant[i]); + } + + // Destroy all the handles we allocated for the byref static safe array's. + if (aByrefStaticArrayBackupObjHandle[i] != NULL) + { + DestroyHandle(aByrefStaticArrayBackupObjHandle[i]); + aByrefStaticArrayBackupObjHandle[i] = NULL; + } + } + + // Do HR conversion. + hr = SetupErrorInfo(pThrowable); + if (hr == COR_E_TARGETINVOCATION) + { + hr = DISP_E_EXCEPTION; + if (pei) + { + // Retrieve the exception iformation. + GetExcepInfoForInvocationExcep(pThrowable, pei); + + // Clear the IErrorInfo on the current thread since it does contains + // information on the TargetInvocationException which conflicts with + // the information in the returned EXCEPINFO. + IErrorInfo *pErrInfo = NULL; + HRESULT hr2 = SafeGetErrorInfo(&pErrInfo); + _ASSERTE(hr2 == S_OK); + SafeRelease(pErrInfo); + } + } + else if (hr == COR_E_OVERFLOW) + { + hr = DISP_E_OVERFLOW; + if (iSrcArg != -1) + { + if (puArgErr) + *puArgErr = iSrcArg + iBaseErrorArg; + } + } + else if (hr == COR_E_INVALIDOLEVARIANTTYPE) + { + hr = DISP_E_BADVARTYPE; + if (iSrcArg != -1) + { + if (puArgErr) + *puArgErr = iSrcArg + iBaseErrorArg; + } + } + else if (hr == COR_E_ARGUMENT) + { + hr = E_INVALIDARG; + if (iSrcArg != -1) + { + if (puArgErr) + *puArgErr = iSrcArg + iBaseErrorArg; + } + } + else if (hr == COR_E_SAFEARRAYTYPEMISMATCH) + { + hr = DISP_E_TYPEMISMATCH; + if (iSrcArg != -1) + { + if (puArgErr) + *puArgErr = iSrcArg + iBaseErrorArg; + } + } + else if (hr == COR_E_MISSINGMEMBER || hr == COR_E_MISSINGMETHOD) + { + hr = DISP_E_MEMBERNOTFOUND; + + // This exception should never occur while we are marshaling arguments. + _ASSERTE(iSrcArg == -1); + } + } + + // If the culture was changed then restore it to the old culture. + if (Objs.OldCultureInfo != NULL) + pThread->SetCulture(&Objs.OldCultureInfo, FALSE); + } + GCPROTECT_END(); + GCPROTECT_END(); + return hr; +} + +void DispatchInfo::DestroyMemberInfoHandles() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + DispatchMemberInfo* pCurrMember = m_pFirstMemberInfo; + while (pCurrMember) + { + // Destroy the handle + DestroyHandle(pCurrMember->m_hndMemberInfo); + pCurrMember->m_hndMemberInfo = NULL; + // Process the next member. + pCurrMember = pCurrMember->m_pNext; + } +} + +// Parameter marshaling helpers. +void DispatchInfo::MarshalParamNativeToManaged(DispatchMemberInfo *pMemberInfo, int iParam, VARIANT *pSrcVar, OBJECTREF *pDestObj) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + SO_INTOLERANT; + } + CONTRACTL_END; + + if (pMemberInfo && !m_bInvokeUsingInvokeMember) + pMemberInfo->MarshalParamNativeToManaged(iParam, pSrcVar, pDestObj); + else + OleVariant::MarshalObjectForOleVariant(pSrcVar, pDestObj); +} + +void DispatchInfo::MarshalParamManagedToNativeRef(DispatchMemberInfo *pMemberInfo, int iParam, OBJECTREF *pSrcObj, OBJECTREF *pBackupStaticArray, VARIANT *pRefVar) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pMemberInfo, NULL_OK)); + PRECONDITION(pSrcObj != NULL); + PRECONDITION(CheckPointer(pRefVar)); + } + CONTRACTL_END; + + if (pBackupStaticArray && (*pBackupStaticArray != NULL)) + { + // The contents of a static array can change, but not the array itself. If + // the array has changed, then throw an exception. + if (*pSrcObj != *pBackupStaticArray) + COMPlusThrow(kInvalidOperationException, IDS_INVALID_REDIM); + + // Retrieve the element VARTYPE and method table. + VARTYPE ElementVt = V_VT(pRefVar) & ~(VT_BYREF | VT_ARRAY); + MethodTable *pElementMT = (*(BASEARRAYREF *)pSrcObj)->GetArrayElementTypeHandle().GetMethodTable(); + + // Convert the contents of the managed array into the original SAFEARRAY. + OleVariant::MarshalSafeArrayForArrayRef((BASEARRAYREF *)pSrcObj, *V_ARRAYREF(pRefVar), ElementVt, pElementMT); + } + else +{ + if (pMemberInfo && !m_bInvokeUsingInvokeMember) + pMemberInfo->MarshalParamManagedToNativeRef(iParam, pSrcObj, pRefVar); + else + OleVariant::MarshalOleRefVariantForObject(pSrcObj, pRefVar); +} +} + +void DispatchInfo::MarshalReturnValueManagedToNative(DispatchMemberInfo *pMemberInfo, OBJECTREF *pSrcObj, VARIANT *pDestVar) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + SO_INTOLERANT; + } + CONTRACTL_END; + + if (pMemberInfo && !m_bInvokeUsingInvokeMember) + pMemberInfo->MarshalReturnValueManagedToNative(pSrcObj, pDestVar); + else + OleVariant::MarshalOleVariantForObject(pSrcObj, pDestVar); +} + +void DispatchInfo::CleanUpNativeParam(DispatchMemberInfo *pDispMemberInfo, int iParamIndex, OBJECTREF *pBackupStaticArray, VARIANT *pArgVariant) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pArgVariant != NULL); + } + CONTRACTL_END; + + EX_TRY + { + switch (V_VT(pArgVariant) & ~VT_BYREF) + { + case VT_I1: case VT_I2: case VT_I4: case VT_I8: + case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8: + case VT_INT: case VT_UINT: case VT_PTR: + case VT_R4: case VT_R8: case VT_BOOL: + case VT_CY: case VT_DATE: + case VT_ERROR: case VT_HRESULT: + case VT_DECIMAL: + { + // the argument type is a value type - overwrite it with zeros + UINT uSize = OleVariant::GetElementSizeForVarType(V_VT(pArgVariant) & ~VT_BYREF, NULL); + FillMemory(V_BYREF(pArgVariant), uSize, 0); + break; + } + + default: + { + // marshal managed null into the VARIANT which works for reference types + OBJECTREF Null = NULL; + + GCPROTECT_BEGIN(Null); // the local stays NULL, this is just to satisfy contracts + MarshalParamManagedToNativeRef(pDispMemberInfo, iParamIndex, &Null, pBackupStaticArray, pArgVariant); + GCPROTECT_END(); + } + } + } + EX_CATCH + { + // if the argument was totally corrupted and cleanup failed, just swallow it and continue + } + EX_END_CATCH(SwallowAllExceptions) +} + +void DispatchInfo::SetUpNamedParamArray(DispatchMemberInfo *pMemberInfo, DISPID *pSrcArgNames, int NumNamedArgs, PTRARRAYREF *pNamedParamArray) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(CheckPointer(pMemberInfo, NULL_OK)); + PRECONDITION(CheckPointer(pSrcArgNames)); + PRECONDITION(pNamedParamArray != NULL); + } + CONTRACTL_END; + + PTRARRAYREF ParamArray = NULL; + int NumParams = pMemberInfo ? pMemberInfo->GetNumParameters() : 0; + int iSrcArg; + int iDestArg; + BOOL bGotParams = FALSE; + + GCPROTECT_BEGIN(ParamArray) + { + // Allocate the array of named parameters. + *pNamedParamArray = (PTRARRAYREF)AllocateObjectArray(NumNamedArgs, g_pObjectClass); + ParamArray = pMemberInfo ? pMemberInfo->GetParameters() : NULL; + int numArrayComponents = (pMemberInfo && ParamArray != NULL)? (int)ParamArray->GetNumComponents() : 0; + + // Convert all the named parameters from DISPID's to string. + for (iSrcArg = 0, iDestArg = 0; iSrcArg < NumNamedArgs; iSrcArg++, iDestArg++) + { + BOOL bParamNameSet = FALSE; + + // Check to see if the DISPID is one that we can map to a parameter name. + if (pMemberInfo && pSrcArgNames[iSrcArg] >= 0 && pSrcArgNames[iSrcArg] < numArrayComponents) + { + // The DISPID is one that we assigned, map it back to its name. + + // If we managed to get the parameters and if the current ID maps + // to an entry in the array. + if (ParamArray != NULL && numArrayComponents > pSrcArgNames[iSrcArg]) + { + OBJECTREF ParamInfoObj = ParamArray->GetAt(pSrcArgNames[iSrcArg]); + GCPROTECT_BEGIN(ParamInfoObj) + { + // Retrieve the MD to use to retrieve the name of the parameter. + MethodDesc *pGetParamNameMD = MemberLoader::FindPropertyMethod(ParamInfoObj->GetMethodTable(), PARAMETERINFO_NAME_PROP, PropertyGet); + _ASSERTE(pGetParamNameMD && "Unable to find getter method for property ParameterInfo::Name"); + MethodDescCallSite getParamName(pGetParamNameMD, &ParamInfoObj); + + // Retrieve the name of the parameter. + ARG_SLOT GetNameArgs[] = + { + ObjToArgSlot(ParamInfoObj) + }; + + STRINGREF MemberNameObj = getParamName.Call_RetSTRINGREF(GetNameArgs); + + // If we got a valid name back then use it as the named parameter. + if (MemberNameObj != NULL) + { + (*pNamedParamArray)->SetAt(iDestArg, (OBJECTREF)MemberNameObj); + bParamNameSet = TRUE; + } + } + GCPROTECT_END(); + } + } + + // If we haven't set the param name yet, then set it to [DISP=XXXX]. + if (!bParamNameSet) + { + WCHAR wszTmp[64]; + + _snwprintf_s(wszTmp, NumItems(wszTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, pSrcArgNames[iSrcArg]); + STRINGREF strTmp = StringObject::NewString(wszTmp); + (*pNamedParamArray)->SetAt(iDestArg, (OBJECTREF)strTmp); + } + } + } + GCPROTECT_END(); +} + +VARIANT *DispatchInfo::RetrieveSrcVariant(VARIANT *pDispParamsVariant) +{ + CONTRACT (VARIANT*) + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(CheckPointer(pDispParamsVariant)); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + // For VB6 compatibility reasons, if the VARIANT is a VT_BYREF | VT_VARIANT that + // contains another VARIANT with VT_BYREF | VT_VARIANT, then we need to extract the + // inner VARIANT and use it instead of the outer one. Note that if the inner VARIANT + // is VT_BYREF | VT_VARIANT | VT_ARRAY, it will pass the below test too. + if (V_VT(pDispParamsVariant) == (VT_VARIANT | VT_BYREF) && + (V_VT(V_VARIANTREF(pDispParamsVariant)) & (VT_TYPEMASK | VT_BYREF)) == (VT_VARIANT | VT_BYREF)) + { + RETURN (V_VARIANTREF(pDispParamsVariant)); + } + else + { + RETURN pDispParamsVariant; + } +} + + +bool DispatchInfo::IsPropertyAccessorVisible(bool fIsSetter, OBJECTREF* pMemberInfo) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(pMemberInfo != NULL); + PRECONDITION (IsProtectedByGCFrame (pMemberInfo)); + } + CONTRACTL_END; + + MethodTable *pMemberInfoClass = (*pMemberInfo)->GetMethodTable(); + + if (MscorlibBinder::IsClass(pMemberInfoClass, CLASS__PROPERTY)) + { + // Get the property's MethodDesc + MethodDesc* pMDForProperty = NULL; + OBJECTREF method = NULL; + GCPROTECT_BEGIN(method) + { + // Get the property method token + BinderMethodID methodID; + + if (fIsSetter) + { + methodID = METHOD__PROPERTY__GET_SETTER; + } + else + { + methodID = METHOD__PROPERTY__GET_GETTER; + } + + MethodDescCallSite getMethod(methodID, pMemberInfo); + ARG_SLOT args[] = + { + ObjToArgSlot(*pMemberInfo), + BoolToArgSlot(true) + }; + method = getMethod.Call_RetOBJECTREF(args); + + if (method != NULL) + { + MethodDescCallSite getMethodHandle(METHOD__METHOD_BASE__GET_METHODDESC, &method); + ARG_SLOT arg = ObjToArgSlot(method); + pMDForProperty = (MethodDesc*) getMethodHandle.Call_RetLPVOID(&arg); + } + } + GCPROTECT_END(); + + if (pMDForProperty == NULL) + return false; + + // Check to see if the new method is a property accessor. + mdToken tkMember = mdTokenNil; + MethodTable *pDeclaringMT = pMDForProperty->GetMethodTable(); + if (pMDForProperty->GetModule()->GetPropertyInfoForMethodDef(pMDForProperty->GetMemberDef(), &tkMember, NULL, NULL) == S_OK) + { + if (IsMemberVisibleFromCom(pDeclaringMT, tkMember, pMDForProperty->GetMemberDef())) + return true; + } + } + + return false; +} + +MethodDesc* DispatchInfo::GetFieldInfoMD(BinderMethodID Method, TypeHandle hndFieldInfoType) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END + + MethodDesc *pMD; + + // If the current class is the standard implementation then return the cached method desc + if (MscorlibBinder::IsClass(hndFieldInfoType.GetMethodTable(), CLASS__FIELD)) + { + pMD = MscorlibBinder::GetMethod(Method); + } + else + { + pMD = MemberLoader::FindMethod(hndFieldInfoType.GetMethodTable(), + MscorlibBinder::GetMethodName(Method), MscorlibBinder::GetMethodSig(Method)); + } + _ASSERTE(pMD && "Unable to find specified FieldInfo method"); + + // Return the specified method desc. + RETURN pMD; +} + +MethodDesc* DispatchInfo::GetPropertyInfoMD(BinderMethodID Method, TypeHandle hndPropInfoType) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END + + MethodDesc *pMD; + + // If the current class is the standard implementation then return the cached method desc if present. + if (MscorlibBinder::IsClass(hndPropInfoType.GetMethodTable(), CLASS__PROPERTY)) + { + pMD = MscorlibBinder::GetMethod(Method); + } + else + { + pMD = MemberLoader::FindMethod(hndPropInfoType.GetMethodTable(), + MscorlibBinder::GetMethodName(Method), MscorlibBinder::GetMethodSig(Method)); + } + _ASSERTE(pMD && "Unable to find specified PropertyInfo method"); + + // Return the specified method desc. + RETURN pMD; +} + +MethodDesc* DispatchInfo::GetMethodInfoMD(BinderMethodID Method, TypeHandle hndMethodInfoType) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END + + MethodDesc *pMD; + + // If the current class is the standard implementation then return the cached method desc. + if (MscorlibBinder::IsClass(hndMethodInfoType.GetMethodTable(), CLASS__METHOD)) + { + pMD = MscorlibBinder::GetMethod(Method); + } + else + { + pMD = MemberLoader::FindMethod(hndMethodInfoType.GetMethodTable(), + MscorlibBinder::GetMethodName(Method), MscorlibBinder::GetMethodSig(Method)); + } + _ASSERTE(pMD && "Unable to find specified MethodInfo method"); + + // Return the specified method desc. + RETURN pMD; +} + +MethodDesc* DispatchInfo::GetCustomAttrProviderMD(TypeHandle hndCustomAttrProvider) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + MethodTable *pMT = hndCustomAttrProvider.AsMethodTable(); + MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICUSTOM_ATTR_PROVIDER__GET_CUSTOM_ATTRIBUTES)); + + // Return the specified method desc. + RETURN pMD; +} + +// This method synchronizes the DispatchInfo's members with the ones in the method tables type. +// The return value will be set to TRUE if the object was out of synch and members where +// added and it will be set to FALSE otherwise. +BOOL DispatchInfo::SynchWithManagedView() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + NewArrayHolder<WCHAR> strMemberName = NULL; + NewHolder<ComMTMemberInfoMap> pMemberMap = NULL; + + // This represents the new member to add and it is also used to determine if members have + // been added or not. + NewHolder<DispatchMemberInfo> pMemberToAdd = NULL; + + Thread* pThread = SetupThreadNoThrow(); + if (pThread == NULL) + return FALSE; + + // Determine if this is the first time we synch. + BOOL bFirstSynch = (m_pFirstMemberInfo == NULL); + + // This method needs to be synchronized to make sure two threads don't try and + // add members at the same time. + CrstHolder ch(&m_lock); + { + // Make sure we switch to cooperative mode before we start. + GCX_COOP(); + + // Go through the list of member info's and find the end. + DispatchMemberInfo **ppNextMember = &m_pFirstMemberInfo; + while (*ppNextMember) + ppNextMember = &((*ppNextMember)->m_pNext); + + // Retrieve the member info map. + pMemberMap = GetMemberInfoMap(); + + for (int cPhase = 0; cPhase < 3; cPhase++) + { + PTRARRAYREF MemberArrayObj = NULL; + GCPROTECT_BEGIN(MemberArrayObj); + + // Retrieve the appropriate array of members for the current phase. + switch (cPhase) + { + case 0: + // Retrieve the array of properties. + MemberArrayObj = RetrievePropList(); + break; + + case 1: + // Retrieve the array of fields. + MemberArrayObj = RetrieveFieldList(); + break; + + case 2: + // Retrieve the array of methods. + MemberArrayObj = RetrieveMethList(); + break; + } + + // Retrieve the number of components in the member array. + UINT NumComponents = 0; + if (MemberArrayObj != NULL) + NumComponents = MemberArrayObj->GetNumComponents(); + + // Go through all the member info's in the array and see if they are already + // in the DispatchExInfo. + for (UINT i = 0; i < NumComponents; i++) + { + BOOL bMatch = FALSE; + + OBJECTREF CurrMemberInfoObj = MemberArrayObj->GetAt(i); + GCPROTECT_BEGIN(CurrMemberInfoObj) + { + DispatchMemberInfo *pCurrMemberInfo = m_pFirstMemberInfo; + while (pCurrMemberInfo) + { + // We can simply compare the OBJECTREF's. + if (CurrMemberInfoObj == ObjectFromHandle(pCurrMemberInfo->m_hndMemberInfo)) + { + // We have found a match. + bMatch = TRUE; + break; + } + + // Check the next member. + pCurrMemberInfo = pCurrMemberInfo->m_pNext; + } + + // If we have not found a match then we need to add the member info to the + // list of member info's that will be added to the DispatchExInfo. + if (!bMatch) + { + DISPID MemberID = DISPID_UNKNOWN; + BOOL bAddMember = FALSE; + + + // + // Attempt to retrieve the properties of the member. + // + + ComMTMethodProps *pMemberProps = DispatchMemberInfo::GetMemberProps(CurrMemberInfoObj, pMemberMap); + + // + // Determine if we are to add this member or not. + // + + if (pMemberProps) + bAddMember = pMemberProps->bMemberVisible; + else + bAddMember = m_bAllowMembersNotInComMTMemberMap; + + if (bAddMember) + { + // + // Retrieve the DISPID of the member. + // + MemberID = DispatchMemberInfo::GetMemberDispId(CurrMemberInfoObj, pMemberMap); + + // + // If the member does not have an explicit DISPID or if the specified DISPID + // is already in use then we need to generate a dynamic DISPID for the member. + // + + if ((MemberID == DISPID_UNKNOWN) || (FindMember(MemberID) != NULL)) + MemberID = GenerateDispID(); + + // + // Retrieve the name of the member. + // + + strMemberName = DispatchMemberInfo::GetMemberName(CurrMemberInfoObj, pMemberMap); + + // + // Create a DispatchInfoMemberInfo that will represent the member. + // + + SString sName(strMemberName); + pMemberToAdd = CreateDispatchMemberInfoInstance(MemberID, sName, CurrMemberInfoObj); + + // + // Add the member to the end of the list. + // + + *ppNextMember = pMemberToAdd; + + // Update ppNextMember to be ready for the next new member. + ppNextMember = &((*ppNextMember)->m_pNext); + + // Add the member to the map. Note, the hash is unsynchronized, but we already have our lock + // so we're okay. + m_DispIDToMemberInfoMap.InsertValue(DispID2HashKey(MemberID), pMemberToAdd); + pMemberToAdd.SuppressRelease(); + } + } + } + GCPROTECT_END(); + } + + GCPROTECT_END(); + } + // GC mode toggles back here + } + // Check to see if any new members were added to the expando object. + return pMemberToAdd ? TRUE : FALSE; + + // locks released and memory cleaned up here +} + +// This method retrieves the OleAutBinder type. +OBJECTREF DispatchInfo::GetOleAutBinder() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + // If we have already create the instance of the OleAutBinder then simply return it. + if (m_hndOleAutBinder) + return ObjectFromHandle(m_hndOleAutBinder); + + MethodTable *pOleAutBinderClass = MscorlibBinder::GetClass(CLASS__OLE_AUT_BINDER); + + // Allocate an instance of the OleAutBinder class. + OBJECTREF OleAutBinder = AllocateObject(pOleAutBinderClass); + + // Keep a handle to the OleAutBinder instance. + m_hndOleAutBinder = CreateGlobalHandle(OleAutBinder); + + return OleAutBinder; +} + +BOOL DispatchInfo::VariantIsMissing(VARIANT *pOle) +{ + LIMITED_METHOD_CONTRACT; + + return (V_VT(pOle) == VT_ERROR) && (V_ERROR(pOle) == DISP_E_PARAMNOTFOUND); +} + +PTRARRAYREF DispatchInfo::RetrievePropList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // return value + PTRARRAYREF orRetVal; + + // Retrieve the exposed class object. + OBJECTREF TargetObj = GetReflectionObject(); + + GCPROTECT_BEGIN(TargetObj); + MethodDescCallSite getProperties(METHOD__CLASS__GET_PROPERTIES, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the type object. + orRetVal = (PTRARRAYREF) getProperties.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return orRetVal; +} + +PTRARRAYREF DispatchInfo::RetrieveFieldList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // return value + PTRARRAYREF orRetVal; + + // Retrieve the exposed class object. + OBJECTREF TargetObj = GetReflectionObject(); + + GCPROTECT_BEGIN(TargetObj); + MethodDescCallSite getFields(METHOD__CLASS__GET_FIELDS, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the type object. + orRetVal = (PTRARRAYREF) getFields.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return orRetVal; +} + +PTRARRAYREF DispatchInfo::RetrieveMethList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // return value + PTRARRAYREF orRetVal; + + // Retrieve the exposed class object. + OBJECTREF TargetObj = GetReflectionObject(); + + GCPROTECT_BEGIN(TargetObj); + MethodDescCallSite getMethods(METHOD__CLASS__GET_METHODS, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the type object. + orRetVal = (PTRARRAYREF) getMethods.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return orRetVal; +} + +// Virtual method to retrieve the InvokeMember method desc. +MethodDesc* DispatchInfo::GetInvokeMemberMD() +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + SO_INTOLERANT; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + RETURN MscorlibBinder::GetMethod(METHOD__CLASS__INVOKE_MEMBER); +} + +// Virtual method to retrieve the object associated with this DispatchInfo that +// implements IReflect. +OBJECTREF DispatchInfo::GetReflectionObject() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + SO_INTOLERANT; + } + CONTRACTL_END; + + return m_pMT->GetManagedClassObject(); +} + +// Virtual method to retrieve the member info map. +ComMTMemberInfoMap *DispatchInfo::GetMemberInfoMap() +{ + CONTRACT (ComMTMemberInfoMap*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + + // Create the member info map. + NewHolder<ComMTMemberInfoMap> pMemberInfoMap (new ComMTMemberInfoMap(m_pMT)); + + // Initialize it. + pMemberInfoMap->Init(sizeof(void*)); + + pMemberInfoMap.SuppressRelease(); + RETURN pMemberInfoMap; +} + +// Helper function to fill in an EXCEPINFO for an InvocationException. +void DispatchInfo::GetExcepInfoForInvocationExcep(OBJECTREF objException, EXCEPINFO *pei) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(objException != NULL); + PRECONDITION(CheckPointer(pei)); + } + CONTRACTL_END; + + MethodDesc *pMD; + ExceptionData ED; + OBJECTREF InnerExcep = NULL; + + // Initialize the EXCEPINFO. + memset(pei, 0, sizeof(EXCEPINFO)); + pei->scode = E_FAIL; + + GCPROTECT_BEGIN(InnerExcep) + GCPROTECT_BEGIN(objException) + { + // Retrieve the method desc to access the InnerException property. + pMD = MemberLoader::FindPropertyMethod(objException->GetMethodTable(), EXCEPTION_INNER_PROP, PropertyGet); + _ASSERTE(pMD && "Unable to find get method for proprety Exception.InnerException"); + MethodDescCallSite propGet(pMD, &objException); + + // Retrieve the value of the InnerException property. + ARG_SLOT GetInnerExceptionArgs[] = { ObjToArgSlot(objException) }; + InnerExcep = propGet.Call_RetOBJECTREF(GetInnerExceptionArgs); + + // If the inner exception object is null then we can't get any info. + if (InnerExcep != NULL) + { + // Retrieve the exception data for the inner exception. + ExceptionNative::GetExceptionData(InnerExcep, &ED); + pei->bstrSource = ED.bstrSource; + pei->bstrDescription = ED.bstrDescription; + pei->bstrHelpFile = ED.bstrHelpFile; + pei->dwHelpContext = ED.dwHelpContext; + pei->scode = ED.hr; + } + } + GCPROTECT_END(); + GCPROTECT_END(); +} + +int DispatchInfo::ConvertInvokeFlagsToBindingFlags(int InvokeFlags) +{ + LIMITED_METHOD_CONTRACT; + + int BindingFlags = 0; + + // Check to see if DISPATCH_CONSTRUCT is set. + if (InvokeFlags & DISPATCH_CONSTRUCT) + BindingFlags |= BINDER_CreateInstance; + + // Check to see if DISPATCH_METHOD is set. + if (InvokeFlags & DISPATCH_METHOD) + BindingFlags |= BINDER_InvokeMethod; + + if (InvokeFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + // We are dealing with a PROPPUT or PROPPUTREF or both. + if ((InvokeFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) == (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + BindingFlags |= BINDER_SetProperty; + } + else if (InvokeFlags & DISPATCH_PROPERTYPUT) + { + BindingFlags |= BINDER_PutDispProperty; + } + else + { + BindingFlags |= BINDER_PutRefDispProperty; + } + } + else + { + // We are dealing with a PROPGET. + if (InvokeFlags & DISPATCH_PROPERTYGET) + BindingFlags |= BINDER_GetProperty; + } + + return BindingFlags; +} + +BOOL DispatchInfo::IsVariantByrefStaticArray(VARIANT *pOle) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(CheckPointer(pOle)); + } + CONTRACTL_END; + + if (V_VT(pOle) & VT_BYREF && V_VT(pOle) & VT_ARRAY) + { + if ((*V_ARRAYREF(pOle))->fFeatures & FADF_STATIC) + return TRUE; + } + + return FALSE; +} + +DISPID DispatchInfo::GenerateDispID() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_INTOLERANT; + } + CONTRACTL_END; + + // Find the next unused DISPID. Note, the hash is unsynchronized, but Gethash doesn't require synchronization. + for (; (UPTR)m_DispIDToMemberInfoMap.Gethash(DispID2HashKey(m_CurrentDispID)) != -1; m_CurrentDispID++); + return m_CurrentDispID++; +} + +//-------------------------------------------------------------------------------- +// The DispatchExInfo class implementation. + +DispatchExInfo::DispatchExInfo(SimpleComCallWrapper *pSimpleWrapper, MethodTable *pMT, BOOL bSupportsExpando) +: DispatchInfo(pMT) +, m_pSimpleWrapperOwner(pSimpleWrapper) +, m_bSupportsExpando(bSupportsExpando) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pSimpleWrapper)); + PRECONDITION(CheckPointer(pMT)); + } + CONTRACTL_END; + + // Set the flags to specify the behavior of the base DispatchInfo class. + m_bAllowMembersNotInComMTMemberMap = TRUE; + m_bInvokeUsingInvokeMember = TRUE; +} + +DispatchExInfo::~DispatchExInfo() +{ + WRAPPER_NO_CONTRACT; +} + +BOOL DispatchExInfo::SupportsExpando() +{ + LIMITED_METHOD_CONTRACT; + + return m_bSupportsExpando; +} + +// Methods to lookup members. These methods synch with the managed view if they fail to +// find the method. +DispatchMemberInfo* DispatchExInfo::SynchFindMember(DISPID DispID) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + DispatchMemberInfo *pMemberInfo = FindMember(DispID); + + if (!pMemberInfo && SynchWithManagedView()) + pMemberInfo = FindMember(DispID); + + RETURN pMemberInfo; +} + +DispatchMemberInfo* DispatchExInfo::SynchFindMember(SString& strName, BOOL bCaseSensitive) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + DispatchMemberInfo *pMemberInfo = FindMember(strName, bCaseSensitive); + + if (!pMemberInfo && SynchWithManagedView()) + pMemberInfo = FindMember(strName, bCaseSensitive); + + RETURN pMemberInfo; +} + +// Helper method that invokes the member with the specified DISPID. These methods synch +// with the managed view if they fail to find the method. +HRESULT DispatchExInfo::SynchInvokeMember(SimpleComCallWrapper *pSimpleWrap, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pVarRes, EXCEPINFO *pei, IServiceProvider *pspCaller, unsigned int *puArgErr) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // Invoke the member. + HRESULT hr = InvokeMember(pSimpleWrap, id, lcid, wFlags, pdp, pVarRes, pei, pspCaller, puArgErr); + + // If the member was not found then we need to synch and try again if the managed view has changed. + if ((hr == DISP_E_MEMBERNOTFOUND) && SynchWithManagedView()) + hr = InvokeMember(pSimpleWrap, id, lcid, wFlags, pdp, pVarRes, pei, pspCaller, puArgErr); + + return hr; +} + +// Helper method used to create DispatchMemberInfo's. This is only here because +// we can't call new inside a method that has a EX_TRY statement. +DispatchMemberInfo* DispatchExInfo::CreateDispatchMemberInfoInstance(DISPID DispID, SString& strMemberName, OBJECTREF MemberInfoObj) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + INJECT_FAULT(COMPlusThrowOM()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + DispatchMemberInfo* pInfo = new DispatchMemberInfo(this, DispID, strMemberName, MemberInfoObj); + + AppDomainFromIDHolder pDomain(m_pSimpleWrapperOwner->GetDomainID(), FALSE); + pDomain.ThrowIfUnloaded(); + + pInfo->SetHandle(pDomain->CreateHandle(MemberInfoObj)); + + RETURN pInfo; +} + + +DispatchMemberInfo* DispatchExInfo::GetFirstMember() +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // Start with the first member. + DispatchMemberInfo **ppNextMemberInfo = &m_pFirstMemberInfo; + + // If the next member is not set we need to sink up with the expando object + // itself to make sure that this member is really the last member and that + // other members have not been added without us knowing. + if (!(*ppNextMemberInfo)) + { + if (SynchWithManagedView()) + { + // New members have been added to the list and since they must be added + // to the end the next member of the previous end of the list must + // have been updated. + _ASSERTE(*ppNextMemberInfo); + } + } + + // Now we need to make sure we skip any members that are deleted. + while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); + + RETURN *ppNextMemberInfo; +} + +DispatchMemberInfo* DispatchExInfo::GetNextMember(DISPID CurrMemberDispID) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + // Do a lookup in the hashtable to find the DispatchMemberInfo for the DISPID. + DispatchMemberInfo *pDispMemberInfo = FindMember(CurrMemberDispID); + if (!pDispMemberInfo) + RETURN NULL; + + // Start from the next member. + DispatchMemberInfo **ppNextMemberInfo = &pDispMemberInfo->m_pNext; + + // If the next member is not set we need to sink up with the expando object + // itself to make sure that this member is really the last member and that + // other members have not been added without us knowing. + if (!(*ppNextMemberInfo)) + { + if (SynchWithManagedView()) + { + // New members have been added to the list and since they must be added + // to the end the next member of the previous end of the list must + // have been updated. + _ASSERTE(*ppNextMemberInfo); + } + } + + // Now we need to make sure we skip any members that are deleted. + while ((*ppNextMemberInfo) && !ObjectFromHandle((*ppNextMemberInfo)->m_hndMemberInfo)) + ppNextMemberInfo = &((*ppNextMemberInfo)->m_pNext); + + RETURN *ppNextMemberInfo; +} + +DispatchMemberInfo* DispatchExInfo::AddMember(SString& strName, BOOL bCaseSensitive) +{ + CONTRACT (DispatchMemberInfo*) + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + INJECT_FAULT(COMPlusThrowOM()); + PRECONDITION(m_bSupportsExpando == TRUE); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + } + CONTRACT_END; + + DispatchMemberInfo *pDispMemberInfo = NULL; + + // Attempt to find the member in the DispatchEx information. + pDispMemberInfo = SynchFindMember(strName, bCaseSensitive); + + // If we haven't found the member, then we need to add it. + if (!pDispMemberInfo) + { + // Take a lock before we check again to see if the member has been added by another thread. + CrstHolder ch(&m_lock); + + // Now that we are inside the lock, check without synching. + pDispMemberInfo = FindMember(strName, bCaseSensitive); + if (!pDispMemberInfo) + { + struct _gc { + STRINGREF strObj; + OBJECTREF TargetObj; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + GCPROTECT_BEGIN(gc); + + // Retrieve the MethodDesc for AddField() + MethodDesc *pMD = GetIExpandoMD(METHOD__IEXPANDO__ADD_FIELD); + + // Allocate the string object that will be passed to the AddField method. + gc.strObj = StringObject::NewString(strName.GetUnicode()); + + // Retrieve the COM+ object that is being exposed to COM. + gc.TargetObj = GetReflectionObject(); + + MethodDescCallSite addField(pMD, &gc.TargetObj); + + // Prepare the arguments that will be passed to AddField. + ARG_SLOT Args[] = + { + ObjToArgSlot(gc.TargetObj), + ObjToArgSlot(gc.strObj) + }; + + // Add the field to the target expando. + OBJECTREF pMemberInfo = addField.Call_RetOBJECTREF(Args); + + // Generate the DISPID for this member. + DISPID DispID = GenerateDispID(); + + // Create a new DispatchMemberInfo that will represent this member. + pDispMemberInfo = CreateDispatchMemberInfoInstance(DispID, strName, pMemberInfo); + + // Go through the list of member info's and find the end. + DispatchMemberInfo **ppNextMember = &m_pFirstMemberInfo; + while (*ppNextMember) + ppNextMember = &((*ppNextMember)->m_pNext); + + // Add the new member info to the end of the list. + *ppNextMember = pDispMemberInfo; + + // Add the member to the hashtable. Note, the hash is unsynchronized, but we already have our lock so + // we're okay. + m_DispIDToMemberInfoMap.InsertValue(DispID2HashKey(DispID), pDispMemberInfo); + + GCPROTECT_END(); + } + } + + RETURN pDispMemberInfo; +} + +void DispatchExInfo::DeleteMember(DISPID DispID) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(m_bSupportsExpando); + } + CONTRACTL_END + + // Do a lookup in the hashtable to find the DispatchMemberInfo for the DISPID. + // This needs to be done outside of the lock because SyncFindMember will acquire + // the lock as well. + DispatchMemberInfo *pDispMemberInfo = SynchFindMember(DispID); + + // Take a lock before we check that the member has not already been deleted. + { + CrstHolder ch(&m_lock); + + // If the member does not exist, it is static or has been deleted then we have nothing more to do. + if (pDispMemberInfo && (ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo) != NULL)) + { + OBJECTREF TargetObj = GetReflectionObject(); + GCPROTECT_BEGIN(TargetObj); + + // Retrieve the DeleteMember MethodDesc. + MethodDesc *pMD = GetIExpandoMD(METHOD__IEXPANDO__REMOVE_MEMBER); + MethodDescCallSite removeMember(pMD, &TargetObj); + + OBJECTREF MemberInfoObj = ObjectFromHandle(pDispMemberInfo->m_hndMemberInfo); + + // Prepare the arguments that will be passed to RemoveMember. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + ObjToArgSlot(MemberInfoObj) + }; + + // Call the DeleteMember method. + removeMember.Call(Args); + + // Set the handle to point to NULL to indicate the member has been removed. + StoreObjectInHandle(pDispMemberInfo->m_hndMemberInfo, NULL); + + GCPROTECT_END(); + } + } +} + +MethodDesc* DispatchExInfo::GetIReflectMD(BinderMethodID Method) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + MethodTable *pMT = m_pSimpleWrapperOwner->GetMethodTable(); + MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method)); + + // Return the specified method desc. + RETURN pMD; +} + + +MethodDesc* DispatchExInfo::GetIExpandoMD(BinderMethodID Method) +{ + CONTRACT (MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(SupportsExpando()); + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + MethodTable *pMT = m_pSimpleWrapperOwner->GetMethodTable(); + MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method)); + + // Return the specified method desc. + RETURN pMD; +} + +PTRARRAYREF DispatchExInfo::RetrievePropList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PTRARRAYREF oPropList; + + // Retrieve the expando OBJECTREF. + OBJECTREF TargetObj = GetReflectionObject(); + GCPROTECT_BEGIN(TargetObj); + + // Retrieve the GetMembers MethodDesc. + MethodDesc *pMD = GetIReflectMD(METHOD__IREFLECT__GET_PROPERTIES); + MethodDescCallSite getProperties(pMD, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the expando object + oPropList = (PTRARRAYREF) getProperties.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return oPropList; +} + +PTRARRAYREF DispatchExInfo::RetrieveFieldList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PTRARRAYREF oFieldList; + + // Retrieve the expando OBJECTREF. + OBJECTREF TargetObj = GetReflectionObject(); + GCPROTECT_BEGIN(TargetObj); + + // Retrieve the GetMembers MethodDesc. + MethodDesc *pMD = GetIReflectMD(METHOD__IREFLECT__GET_FIELDS); + MethodDescCallSite getFields(pMD, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the expando object + oFieldList = (PTRARRAYREF) getFields.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return oFieldList; +} + +PTRARRAYREF DispatchExInfo::RetrieveMethList() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + PTRARRAYREF oMethList; + + // Retrieve the expando OBJECTREF. + OBJECTREF TargetObj = GetReflectionObject(); + GCPROTECT_BEGIN(TargetObj); + + // Retrieve the GetMembers MethodDesc. + MethodDesc *pMD = GetIReflectMD(METHOD__IREFLECT__GET_METHODS); + MethodDescCallSite getMethods(pMD, &TargetObj); + + // Prepare the arguments that will be passed to the method. + ARG_SLOT Args[] = + { + ObjToArgSlot(TargetObj), + (ARG_SLOT)BINDER_DefaultLookup + }; + + // Retrieve the array of members from the expando object + oMethList = (PTRARRAYREF) getMethods.Call_RetOBJECTREF(Args); + + GCPROTECT_END(); + + return oMethList; +} + +// Virtual method to retrieve the InvokeMember method desc. +MethodDesc* DispatchExInfo::GetInvokeMemberMD() +{ + CONTRACT(MethodDesc*) + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + SO_INTOLERANT; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END; + + RETURN GetIReflectMD(METHOD__IREFLECT__INVOKE_MEMBER); +} + +// Virtual method to retrieve the object associated with this DispatchInfo that +// implements IReflect. +OBJECTREF DispatchExInfo::GetReflectionObject() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // Runtime type is very special. Because of how it is implemented, calling methods + // through IDispatch on a runtime type object doesn't work like other IReflect implementors + // work. To be able to invoke methods on the runtime type, we need to invoke them + // on the runtime type that represents runtime type. This is why for runtime type, + // we get the exposed class object and not the actual objectred contained in the + // wrapper. + + if (m_pMT == g_pRuntimeTypeClass || + MscorlibBinder::IsClass(m_pMT, CLASS__CLASS_INTROSPECTION_ONLY)) + return m_pMT->GetManagedClassObject(); + else + return m_pSimpleWrapperOwner->GetObjectRef(); +} + +// Virtual method to retrieve the member info map. +ComMTMemberInfoMap *DispatchExInfo::GetMemberInfoMap() +{ + LIMITED_METHOD_CONTRACT; + + // There is no member info map for IExpando objects. + return NULL; +} |