Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
96197f1
Convert more COM interop MethodDescCallSite to UCO
am11 Mar 12, 2026
83f8dd6
Add guard
am11 Mar 13, 2026
3247c98
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 13, 2026
e4b6de6
Address CR fb
am11 Mar 13, 2026
3e5088f
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 13, 2026
988e82a
Use raw data for value type
am11 Mar 13, 2026
68e943f
Address CR fb
am11 Mar 13, 2026
dc0cc4a
Merge remote-tracking branch 'upstream/main' into feature/MDCS-to-UCO…
am11 Mar 13, 2026
dd50f41
Add comment explaining ulong usage
am11 Mar 13, 2026
5057a6a
Delete {G,S}setOHDelegate
am11 Mar 14, 2026
3ed9700
Update class.h
am11 Mar 14, 2026
f1fc86a
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 14, 2026
81e4f93
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 14, 2026
61f5e70
Assert copyOfArgs.Length <= MaxStackAllocArgCount
am11 Mar 14, 2026
792f177
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 14, 2026
140e394
Update StubHelpers.cs
am11 Mar 15, 2026
dfc2a5d
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 15, 2026
7397553
Simplify stuff
am11 Mar 15, 2026
e25f424
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 15, 2026
cdaee50
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 20, 2026
e4e5d2a
Address feedback
am11 Mar 20, 2026
a08de0d
Add [RequiresUnsafe] attribute
am11 Mar 20, 2026
5693443
Update StubHelpers.cs
am11 Mar 20, 2026
0f433ae
Revert slot conversions
am11 Mar 20, 2026
a2eb88b
Update StubHelpers.cs
am11 Mar 20, 2026
563d118
Merge branch 'main' into feature/MDCS-to-UCOA-pattern
am11 Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1501,8 +1501,79 @@ internal static void SetPendingExceptionObject(Exception? exception)
[SupportedOSPlatform("windows")]
internal static object GetIEnumeratorToEnumVariantMarshaler() => EnumeratorToEnumVariantMarshaler.GetInstance(string.Empty);

private const int DispatchExPropertyCanRead = 1;
private const int DispatchExPropertyCanWrite = 2;

[SupportedOSPlatform("windows")]
[UnmanagedCallersOnly]
[RequiresUnsafe]
private static unsafe void GetDispatchExPropertyFlags(PropertyInfo* pMemberInfo, int* pResult, Exception* pException)
{
try
{
int result = 0;
PropertyInfo property = *pMemberInfo;
if (property.CanRead)
{
result |= DispatchExPropertyCanRead;
}

if (property.CanWrite)
{
result |= DispatchExPropertyCanWrite;
}

*pResult = result;
}
catch (Exception ex)
{
*pException = ex;
}
}

[SupportedOSPlatform("windows")]
[UnmanagedCallersOnly]
[RequiresUnsafe]
private static unsafe void CallICustomQueryInterface(ICustomQueryInterface* pObject, Guid* pIid, IntPtr* ppObject, int* pResult, Exception* pException)
{
try
{
*pResult = (int)pObject->GetInterface(ref *pIid, out *ppObject);
}
catch (Exception ex)
{
*pException = ex;
}
}

[SupportedOSPlatform("windows")]
[UnmanagedCallersOnly]
[RequiresUnsafe]
private static unsafe void InvokeConnectionPointProviderMethod(
object* pProvider,
delegate *<object, object?, void> providerMethodEntryPoint,
object* pDelegate,
delegate*<object, object?, IntPtr, void> delegateCtorMethodEntryPoint,
object* pSubscriber,
IntPtr pEventMethodCodePtr,
Exception* pException)
{
try
{
// Construct the delegate before invoking the provider method.
delegateCtorMethodEntryPoint(*pDelegate, *pSubscriber, pEventMethodCodePtr);

providerMethodEntryPoint(*pProvider, *pDelegate);
}
catch (Exception ex)
{
*pException = ex;
}
}

[SupportedOSPlatform("windows")]
[UnmanagedCallersOnly]
[RequiresUnsafe]
private static unsafe void GetIEnumeratorToEnumVariantMarshaler(object* pResult, Exception* pException)
{
try
Expand Down Expand Up @@ -1693,6 +1764,7 @@ internal static void MulticastDebuggerTraceHelper(object o, int count)
internal static class CultureInfoMarshaler
{
[UnmanagedCallersOnly]
[RequiresUnsafe]
internal static unsafe void GetCurrentCulture(bool bUICulture, object* pResult, Exception* pException)
{
try
Expand All @@ -1708,6 +1780,7 @@ internal static unsafe void GetCurrentCulture(bool bUICulture, object* pResult,
}

[UnmanagedCallersOnly]
[RequiresUnsafe]
internal static unsafe void SetCurrentCulture(bool bUICulture, Globalization.CultureInfo* pValue, Exception* pException)
{
try
Expand All @@ -1724,6 +1797,7 @@ internal static unsafe void SetCurrentCulture(bool bUICulture, Globalization.Cul
}

[UnmanagedCallersOnly]
[RequiresUnsafe]
internal static unsafe void CreateCultureInfo(int culture, object* pResult, Exception* pException)
{
try
Expand Down Expand Up @@ -1769,6 +1843,7 @@ internal static int ConvertToNative(object? managedColor)
}

[UnmanagedCallersOnly]
[RequiresUnsafe]
internal static unsafe void ConvertToManaged(int oleColor, object* pResult, Exception* pException)
{
try
Expand All @@ -1782,6 +1857,7 @@ internal static unsafe void ConvertToManaged(int oleColor, object* pResult, Exce
}

[UnmanagedCallersOnly]
[RequiresUnsafe]
internal static unsafe void ConvertToNative(object* pSrcObj, int* pResult, Exception* pException)
{
try
Expand Down
22 changes: 2 additions & 20 deletions src/coreclr/vm/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -1468,16 +1468,6 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW!
GetOptionalFields()->m_pCoClassForIntf = th;
}

OBJECTHANDLE GetOHDelegate()
{
LIMITED_METHOD_CONTRACT;
return m_ohDelegate;
Comment thread
am11 marked this conversation as resolved.
}
void SetOHDelegate (OBJECTHANDLE _ohDelegate)
{
LIMITED_METHOD_CONTRACT;
m_ohDelegate = _ohDelegate;
}
// Set the COM interface type.
CorIfaceAttr GetComInterfaceType()
{
Expand Down Expand Up @@ -1688,16 +1678,8 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW!
PTR_MethodDescChunk m_pChunks;

#ifdef FEATURE_COMINTEROP
union
{
// For CLR wrapper objects that extend an unmanaged class, this field
// may contain a delegate to be called to allocate the aggregated
// unmanaged class (instead of using CoCreateInstance).
OBJECTHANDLE m_ohDelegate;

// For interfaces this contains the COM interface type.
CorIfaceAttr m_ComInterfaceType;
};
// For interfaces this contains the COM interface type.
CorIfaceAttr m_ComInterfaceType;

ComCallWrapperTemplate *m_pccwTemplate; // points to interop data structures used when this type is exposed to COM
#endif // FEATURE_COMINTEROP
Expand Down
82 changes: 14 additions & 68 deletions src/coreclr/vm/comcallablewrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2314,52 +2314,6 @@ IUnknown* ComCallWrapper::GetBasicIP(bool inspectionOnly)
RETURN ((cbRef != 0xbadf00d) ? pIntf : NULL);
}

struct InvokeICustomQueryInterfaceGetInterfaceArgs
{
ComCallWrapper *pWrap;
GUID *pGuid;
IUnknown **ppUnk;
CustomQueryInterfaceResult *pRetVal;
};

VOID __stdcall InvokeICustomQueryInterfaceGetInterface_CallBack(LPVOID ptr)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(ptr));
}
CONTRACTL_END;
InvokeICustomQueryInterfaceGetInterfaceArgs *pArgs = (InvokeICustomQueryInterfaceGetInterfaceArgs*)ptr;

{
GCX_COOP();
OBJECTREF pObj = pArgs->pWrap->GetObjectRef();

GCPROTECT_BEGIN(pObj);

// 1. Get MD
MethodDesc *pMD = pArgs->pWrap->GetSimpleWrapper()->GetComCallWrapperTemplate()->GetICustomQueryInterfaceGetInterfaceMD();
Comment thread
am11 marked this conversation as resolved.

// 2. Get Object Handle
OBJECTHANDLE hndCustomQueryInterface = pArgs->pWrap->GetObjectHandle();

// 3 construct the MethodDescCallSite
MethodDescCallSite GetInterface(pMD, hndCustomQueryInterface);

ARG_SLOT Args[] = {
ObjToArgSlot(pObj),
PtrToArgSlot(pArgs->pGuid),
PtrToArgSlot(pArgs->ppUnk),
};

*(pArgs->pRetVal) = (CustomQueryInterfaceResult)GetInterface.Call_RetArgSlot(Args);
GCPROTECT_END();
}
}

//--------------------------------------------------------------------------
// check if the interface is supported, return a index into the IMap
// returns -1, if pIntfMT is not supported
Expand Down Expand Up @@ -2601,9 +2555,21 @@ static bool GetComIPFromCCW_HandleCustomQI(
guid = riid;
}

InvokeICustomQueryInterfaceGetInterfaceArgs args = {pWrap, &guid, ppUnkOut, &retVal};
{
GCX_COOP();
OBJECTREF pObj = pWrap->GetObjectRef();

InvokeICustomQueryInterfaceGetInterface_CallBack(&args);
GCPROTECT_BEGIN(pObj);

INT_PTR queriedInterface = reinterpret_cast<INT_PTR>(*ppUnkOut);
INT32 result = static_cast<INT32>(CustomQueryInterfaceResult::NotHandled);
UnmanagedCallersOnlyCaller callICustomQueryInterface(METHOD__STUBHELPERS__CALL_ICUSTOM_QUERY_INTERFACE);
callICustomQueryInterface.InvokeThrowing(&pObj, &guid, &queriedInterface, &result);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also InvokeThrowing_Ret that can be used to return primitives. We use it in a few places and in this case probably is easier to read. You can then also update the managed signature to be more "natural".


*ppUnkOut = reinterpret_cast<IUnknown*>(queriedInterface);
Comment on lines +2564 to +2569
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
INT_PTR queriedInterface = reinterpret_cast<INT_PTR>(*ppUnkOut);
INT32 result = static_cast<INT32>(CustomQueryInterfaceResult::NotHandled);
UnmanagedCallersOnlyCaller callICustomQueryInterface(METHOD__STUBHELPERS__CALL_ICUSTOM_QUERY_INTERFACE);
callICustomQueryInterface.InvokeThrowing(&pObj, &guid, &queriedInterface, &result);
*ppUnkOut = reinterpret_cast<IUnknown*>(queriedInterface);
INT32 result = static_cast<INT32>(CustomQueryInterfaceResult::NotHandled);
UnmanagedCallersOnlyCaller callICustomQueryInterface(METHOD__STUBHELPERS__CALL_ICUSTOM_QUERY_INTERFACE);
callICustomQueryInterface.InvokeThrowing(&pObj, &guid, ppUnkOut, &result);

We do not need a local copy of the IUnknown. This should also reduce changes that the IUknown reference leak if an exception is thrown in a specific spot.

retVal = static_cast<CustomQueryInterfaceResult>(result);
GCPROTECT_END();
}

// return if user already handle the QI
if (retVal == Handled)
Expand Down Expand Up @@ -4674,7 +4640,6 @@ ComCallWrapperTemplate* ComCallWrapperTemplate::CreateTemplate(TypeHandle thClas
pTemplate->m_pClassComMT = NULL; // Defer setting this up.
pTemplate->m_pBasicComMT = NULL;
pTemplate->m_pDefaultItf = NULL;
pTemplate->m_pICustomQueryInterfaceGetInterfaceMD = NULL;
pTemplate->m_flags = 0;

// Determine the COM visibility of classes in our hierarchy.
Expand Down Expand Up @@ -4794,7 +4759,6 @@ ComCallWrapperTemplate *ComCallWrapperTemplate::CreateTemplateForInterface(Metho
pTemplate->m_pClassComMT = NULL;
pTemplate->m_pBasicComMT = NULL;
pTemplate->m_pDefaultItf = pItfMT;
pTemplate->m_pICustomQueryInterfaceGetInterfaceMD = NULL;
pTemplate->m_flags = enum_RepresentsVariantInterface;

// Initialize the one ComMethodTable
Expand Down Expand Up @@ -4928,24 +4892,6 @@ ComMethodTable *ComCallWrapperTemplate::SetupComMethodTableForClass(MethodTable
}


MethodDesc * ComCallWrapperTemplate::GetICustomQueryInterfaceGetInterfaceMD()
{
CONTRACT (MethodDesc*)
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(m_flags & enum_ImplementsICustomQueryInterface);
}
CONTRACT_END;

if (m_pICustomQueryInterfaceGetInterfaceMD == NULL)
m_pICustomQueryInterfaceGetInterfaceMD = m_thClass.GetMethodTable()->GetMethodDescForInterfaceMethod(
CoreLibBinder::GetMethod(METHOD__ICUSTOM_QUERYINTERFACE__GET_INTERFACE),
TRUE /* throwOnConflict */);
RETURN m_pICustomQueryInterfaceGetInterfaceMD;
}

//--------------------------------------------------------------------------
// Module* ComCallMethodDesc::GetModule()
// Get Module
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/comcallablewrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,6 @@ class ComCallWrapperTemplate
// Sets up the class method table for the IClassX and also lays it out.
static ComMethodTable *SetupComMethodTableForClass(MethodTable *pMT, BOOL bLayOutComMT);

MethodDesc * GetICustomQueryInterfaceGetInterfaceMD();

BOOL HasInvisibleParent()
{
LIMITED_METHOD_CONTRACT;
Expand Down Expand Up @@ -328,7 +326,6 @@ class ComCallWrapperTemplate
enum_IsSafeTypeForMarshalling = 0x2000, // The class can be safely marshalled out of process via DCOM
};
DWORD m_flags;
MethodDesc* m_pICustomQueryInterfaceGetInterfaceMD;
ULONG m_cbInterfaces;
SLOT* m_rgpIPtr[1];
};
Expand Down
52 changes: 28 additions & 24 deletions src/coreclr/vm/comconnectionpoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,37 +539,41 @@ void ConnectionPoint::InvokeProviderMethod( OBJECTREF pProvider, OBJECTREF pSubs
// Retrieve the EE class representing the argument.
MethodTable *pDelegateCls = MethodSig.GetLastTypeHandleThrowing().GetMethodTable();

// Make sure we activate the assembly containing the target method desc
// Initialize the delegate using the arguments structure.
// <TODO>Generics: ensure we get the right MethodDesc here and in similar places</TODO>
// Accept both void (object, native int) and void (object, native uint)
MethodDesc *pDlgCtorMD = MemberLoader::FindConstructor(pDelegateCls, &gsig_IM_Obj_IntPtr_RetVoid);
BOOL useUIntPtrCtor = FALSE;
if (pDlgCtorMD == NULL)
{
pDlgCtorMD = MemberLoader::FindConstructor(pDelegateCls, &gsig_IM_Obj_UIntPtr_RetVoid);
useUIntPtrCtor = TRUE;
}

// The loader is responsible for only accepting well-formed delegate classes.
_ASSERTE(pDlgCtorMD);

// Make sure we activate assemblies containing target method descs.
pProvMethodDesc->EnsureActive();
pDlgCtorMD->EnsureActive();
pEventMethodDesc->EnsureActive();

// Allocate an object based on the method table of the delegate class.
OBJECTREF pDelegate = pDelegateCls->Allocate();

GCPROTECT_BEGIN( pDelegate );
{
// Initialize the delegate using the arguments structure.
// <TODO>Generics: ensure we get the right MethodDesc here and in similar places</TODO>
// Accept both void (object, native int) and void (object, native uint)
MethodDesc *pDlgCtorMD = MemberLoader::FindConstructor(pDelegateCls, &gsig_IM_Obj_IntPtr_RetVoid);
if (pDlgCtorMD == NULL)
pDlgCtorMD = MemberLoader::FindConstructor(pDelegateCls, &gsig_IM_Obj_UIntPtr_RetVoid);

// The loader is responsible for only accepting well-formed delegate classes.
_ASSERTE(pDlgCtorMD);

MethodDescCallSite dlgCtor(pDlgCtorMD);

ARG_SLOT CtorArgs[3] = { ObjToArgSlot(pDelegate),
ObjToArgSlot(pSubscriber),
(ARG_SLOT)pEventMethodDesc->GetMultiCallableAddrOfCode()
};
dlgCtor.Call(CtorArgs);

MethodDescCallSite prov(pProvMethodDesc, &pProvider);

// Do the actual invocation of the method method.
ARG_SLOT Args[2] = { ObjToArgSlot( pProvider ), ObjToArgSlot( pDelegate ) };
prov.Call(Args);
UnmanagedCallersOnlyCaller invokeConnectionPointProviderMethod(METHOD__STUBHELPERS__INVOKE_CONNECTION_POINT_PROVIDER_METHOD);

// Construct the delegate and invoke the provider method in one helper.
invokeConnectionPointProviderMethod.InvokeThrowing(
&pProvider,
(INT_PTR)pProvMethodDesc->GetSingleCallableAddrOfCode(),
&pDelegate,
(INT_PTR)pDlgCtorMD->GetSingleCallableAddrOfCode(),
&pSubscriber,
(INT_PTR)pEventMethodDesc->GetSingleCallableAddrOfCode(),
CLR_BOOL_ARG(useUIntPtrCtor));
}
GCPROTECT_END();
}
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,9 @@ DEFINE_METHOD(STUBHELPERS, GET_PENDING_EXCEPTION_OBJECT, GetPendingExce
DEFINE_METHOD(STUBHELPERS, CREATE_CUSTOM_MARSHALER, CreateCustomMarshaler, SM_IntPtr_Int_IntPtr_RetObj)
#ifdef FEATURE_COMINTEROP
DEFINE_METHOD(STUBHELPERS, GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER, GetIEnumeratorToEnumVariantMarshaler, SM_PtrObj_PtrException_RetVoid)
DEFINE_METHOD(STUBHELPERS, GET_DISPATCH_EX_PROPERTY_FLAGS, GetDispatchExPropertyFlags, SM_PtrPropertyInfo_PtrInt_PtrException_RetVoid)
DEFINE_METHOD(STUBHELPERS, CALL_ICUSTOM_QUERY_INTERFACE, CallICustomQueryInterface, SM_PtrICustomQueryInterface_PtrGuid_PtrIntPtr_PtrInt_PtrException_RetVoid)
DEFINE_METHOD(STUBHELPERS, INVOKE_CONNECTION_POINT_PROVIDER_METHOD, InvokeConnectionPointProviderMethod, SM_PtrObj_IntPtr_PtrObj_IntPtr_PtrObj_IntPtr_Bool_PtrException_RetVoid)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DEFINE_METHOD(STUBHELPERS, INVOKE_CONNECTION_POINT_PROVIDER_METHOD, InvokeConnectionPointProviderMethod, SM_PtrObj_IntPtr_PtrObj_IntPtr_PtrObj_IntPtr_Bool_PtrException_RetVoid)
DEFINE_METHOD(STUBHELPERS, INVOKE_CONNECTION_POINT_PROVIDER_METHOD, InvokeConnectionPointProviderMethod, NoSig)

This should be nosig now that it has unmanaged pointers in the signatures (encoding the unmanaged pointers in the signature here is not worth the pain).

#endif // FEATURE_COMINTEROP

DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, SM_Int_RetVoid)
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ DEFINE_METASIG(SM(RefByte_RefByte_UIntPtr_RetVoid, r(b) r(b) U, v))
DEFINE_METASIG(SM(RefByte_Byte_UIntPtr_RetVoid, r(b) b U, v))
DEFINE_METASIG(SM(RefByte_UIntPtr_RetVoid, r(b) U, v))
DEFINE_METASIG(SM(PtrVoid_Byte_UInt_RetVoid, P(v) b K, v))
#ifdef FEATURE_COMINTEROP
DEFINE_METASIG_T(SM(PtrICustomQueryInterface_PtrGuid_PtrIntPtr_PtrInt_PtrException_RetVoid, P(C(ICUSTOM_QUERYINTERFACE)) P(g(GUID)) P(I) P(i) P(C(EXCEPTION)), v))
#endif // FEATURE_COMINTEROP
DEFINE_METASIG_T(SM(PtrObj_IntPtr_PtrObj_IntPtr_PtrObj_IntPtr_Bool_PtrException_RetVoid, P(j) I P(j) I P(j) I F P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrPropertyInfo_PtrInt_PtrException_RetVoid, P(C(PROPERTY_INFO)) P(i) P(C(EXCEPTION)), v))
DEFINE_METASIG(SM(IntPtr_RefObj_IntPtr_RetVoid, I r(j) I, v))
DEFINE_METASIG(SM(IntPtr_RefObj_IntPtr_Int_RetVoid, I r(j) I i,v))
DEFINE_METASIG(SM(IntPtr_IntPtr_Int_Int_IntPtr_RetVoid, I I i i I, v))
Expand Down
Loading
Loading