Skip to content

Migrate core part of IDispatch support for built-in COM to managed #124423

@huoyaoyuan

Description

@huoyaoyuan

Opening for discussion per #124303 (comment). @AaronRobinsonMSFT

Currently, the IDispatch support relies heavily on managed reflection stack:

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_INFO__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_INFO__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, ARRAY_SIZE(strTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, id);
pObjs->MemberName = (OBJECTREF)StringObject::NewString(strTmp);
}
else
{
pObjs->MemberName = (OBJECTREF)StringObject::NewString(pDispMemberInfo->GetName().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);
}

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)u16_strlen(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;
}

The majority of code is manipulating managed objects in native code. The proposal is to convert the managed-manipulating code to managed.

The scope will include:

  • DispatchInfo::InvokeMemberWorker: this is the core member that manipulates managed reflection types intensively.
  • DispatchMemberInfo: it uses managed MemberInfo and ParameterInfo to represent parameter information. The native signature parsing part is shared with MarshalInfo and should be kept native.
  • Most of DispatchInfo: it uses managed reflection stack to retrieve member list, with manual synchronization etc.
  • Overrides of DispParamMarshaler: most are invoking managed functions or custom marshalers. SafeArray and VT_RECORD will be kept native.

The parameter marshal and coerce logic will be kept as-is, with some code written in plain transcribed managed code.

After the conversion, the central logic of IDispatch support would be managed reflection code, with native calls for pieces unsuitable for managed code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions