Permalink
Fetching contributors…
Cannot retrieve contributors at this time
3727 lines (3154 sloc) 123 KB
// 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 "comutilnative.h"
#include "eeconfig.h"
#include "interoputil.h"
#include "olevariant.h"
#include "commtmemberinfomap.h"
#include "dispparammarshaler.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();
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, &param)
{
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)
{
SAFEARRAY *pSafeArray = *V_ARRAYREF(pOle);
if (pSafeArray && (pSafeArray->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;
}