Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 13 additions & 30 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3327,21 +3327,21 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetMethodDescPtrFromIpEx(TADDR fu
return S_OK;
}

HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsDelegate(VMPTR_Object vmObject, OUT BOOL * pResult)
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsDelegate(CORDB_ADDRESS objectAddress, OUT BOOL * pResult)
{
DD_ENTER_MAY_THROW;

HRESULT hr = S_OK;
EX_TRY
{

if (vmObject.IsNull())
if (objectAddress == (CORDB_ADDRESS)NULL)
{
*pResult = FALSE;
}
else
{
Object *pObj = vmObject.GetDacPtr();
PTR_Object pObj = dac_cast<PTR_Object>(CORDB_ADDRESS_TO_TADDR(objectAddress));
*pResult = pObj->GetGCSafeMethodTable()->IsDelegate();
}
}
Expand All @@ -3354,11 +3354,11 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsDelegate(VMPTR_Object vmObject,
// DacDbi API: GetDelegateType
// Given a delegate pointer, compute the type of delegate according to the data held in it.
//-----------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateType(VMPTR_Object delegateObject, DelegateType *delegateType)
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateType(CORDB_ADDRESS delegateObject, DelegateType *delegateType)
{
DD_ENTER_MAY_THROW;

_ASSERTE(!delegateObject.IsNull());
_ASSERTE(delegateObject != (CORDB_ADDRESS)NULL);
_ASSERTE(delegateType != NULL);

#ifdef _DEBUG
Expand All @@ -3378,7 +3378,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateType(VMPTR_Object dele
// - DELEGATE KINDS TABLE in comdelegate.cpp

*delegateType = DelegateType::kUnknownDelegateType;
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(delegateObject.GetDacPtr());
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(CORDB_ADDRESS_TO_TADDR(delegateObject));
INT_PTR invocationCount = pDelObj->GetInvocationCount();

if (invocationCount == -1)
Expand Down Expand Up @@ -3439,7 +3439,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateType(VMPTR_Object dele

HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateFunctionData(
DelegateType delegateType,
VMPTR_Object delegateObject,
CORDB_ADDRESS delegateObject,
OUT VMPTR_Assembly *ppFunctionAssembly,
OUT mdMethodDef *pMethodDef)
{
Expand All @@ -3453,7 +3453,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateFunctionData(
#endif

HRESULT hr = S_OK;
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(delegateObject.GetDacPtr());
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(CORDB_ADDRESS_TO_TADDR(delegateObject));
TADDR targetMethodPtr = (TADDR)NULL;
VMPTR_MethodDesc pMD;

Expand Down Expand Up @@ -3481,8 +3481,8 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateFunctionData(

HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateTargetObject(
DelegateType delegateType,
VMPTR_Object delegateObject,
OUT VMPTR_Object *ppTargetObj,
CORDB_ADDRESS delegateObject,
OUT CORDB_ADDRESS *ppTargetObj,
OUT VMPTR_AppDomain *ppTargetAppDomain)
{
DD_ENTER_MAY_THROW;
Expand All @@ -3495,19 +3495,19 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateTargetObject(
#endif

HRESULT hr = S_OK;
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(delegateObject.GetDacPtr());
PTR_DelegateObject pDelObj = dac_cast<PTR_DelegateObject>(CORDB_ADDRESS_TO_TADDR(delegateObject));

switch (delegateType)
{
case kClosedDelegate:
{
PTR_Object pRemoteTargetObj = OBJECTREFToObject(pDelObj->GetTarget());
ppTargetObj->SetDacTargetPtr(pRemoteTargetObj.GetAddr());
*ppTargetObj = (CORDB_ADDRESS)pRemoteTargetObj.GetAddr();
break;
}

default:
ppTargetObj->SetDacTargetPtr((TADDR)NULL);
*ppTargetObj = (CORDB_ADDRESS)NULL;
break;
}

Expand Down Expand Up @@ -5979,23 +5979,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetHandleAddressFromVmHandle(VMPT
return hr;
}

// Create a TargetBuffer which describes the location of the object
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectContents(VMPTR_Object obj, OUT TargetBuffer * pRetVal)
{
DD_ENTER_MAY_THROW;

HRESULT hr = S_OK;
EX_TRY
{
PTR_Object objPtr = obj.GetDacPtr();

_ASSERTE(objPtr->GetSize() <= 0xffffffff);
*pRetVal = TargetBuffer(PTR_TO_TADDR(objPtr), (ULONG)objPtr->GetSize());
}
EX_CATCH_HRESULT(hr);
return hr;
}

// ============================================================================
// functions to get information about objects referenced via an instance of CordbReferenceValue or
// CordbHandleValue
Expand Down
13 changes: 5 additions & 8 deletions src/coreclr/debug/daccess/dacdbiimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,20 +295,20 @@ class DacDbiInterfaceImpl :
// Returns true if the argument is a runtime callable wrapper
HRESULT STDMETHODCALLTYPE IsRcw(VMPTR_Object vmObject, OUT BOOL * pResult);

HRESULT STDMETHODCALLTYPE IsDelegate(VMPTR_Object vmObject, OUT BOOL * pResult);
HRESULT STDMETHODCALLTYPE IsDelegate(CORDB_ADDRESS objectAddress, OUT BOOL * pResult);

HRESULT STDMETHODCALLTYPE GetDelegateType(VMPTR_Object delegateObject, DelegateType *delegateType);
HRESULT STDMETHODCALLTYPE GetDelegateType(CORDB_ADDRESS delegateObject, DelegateType *delegateType);

HRESULT STDMETHODCALLTYPE GetDelegateFunctionData(
DelegateType delegateType,
VMPTR_Object delegateObject,
CORDB_ADDRESS delegateObject,
OUT VMPTR_Assembly *ppFunctionAssembly,
OUT mdMethodDef *pMethodDef);

HRESULT STDMETHODCALLTYPE GetDelegateTargetObject(
DelegateType delegateType,
VMPTR_Object delegateObject,
OUT VMPTR_Object *ppTargetObj,
CORDB_ADDRESS delegateObject,
OUT CORDB_ADDRESS *ppTargetObj,
OUT VMPTR_AppDomain *ppTargetAppDomain);

HRESULT STDMETHODCALLTYPE GetLoaderHeapMemoryRanges(OUT DacDbiArrayList<COR_MEMORY_RANGE> * pRanges);
Expand Down Expand Up @@ -838,9 +838,6 @@ class DacDbiInterfaceImpl :
// Get the target address from a VMPTR_OBJECTHANDLE, i.e., the handle address
HRESULT STDMETHODCALLTYPE GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle, OUT CORDB_ADDRESS * pRetVal);

// Gets the target address of an VMPTR of an Object
HRESULT STDMETHODCALLTYPE GetObjectContents(VMPTR_Object obj, OUT TargetBuffer * pRetVal);

// Create a VMPTR_OBJECTHANDLE from a CORDB_ADDRESS pointing to an object handle
HRESULT STDMETHODCALLTYPE GetVmObjectHandle(CORDB_ADDRESS handleAddress, OUT VMPTR_OBJECTHANDLE * pRetVal);

Expand Down
41 changes: 9 additions & 32 deletions src/coreclr/debug/di/divalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,26 +263,9 @@ void CordbValue::CreateVCObjOrRefValue(CordbAppDomain * pAppdomain
}
} // CordbValue::CreateValueByType

// Create the proper ICDValue instance based on the given remote heap object
// Arguments:
// pAppDomain - the app domain the remote object is in
// vmObj - the remote object to get an ICDValue for
ICorDebugValue* CordbValue::CreateHeapValue(CordbAppDomain* pAppDomain, VMPTR_Object vmObj)
{
// Create a temporary reference and dereference it to construct the heap value we want.
RSSmartPtr<CordbReferenceValue> pRefValue(CordbValue::CreateHeapReferenceValue(pAppDomain, vmObj));
ICorDebugValue* pExtValue;
IfFailThrow(pRefValue->Dereference(&pExtValue));
return pExtValue;
}

CordbReferenceValue* CordbValue::CreateHeapReferenceValue(CordbAppDomain* pAppDomain, VMPTR_Object vmObj)
CordbReferenceValue* CordbValue::CreateHeapReferenceValue(CordbAppDomain* pAppDomain, CORDB_ADDRESS objAddr)
{
IDacDbiInterface* pDac = pAppDomain->GetProcess()->GetDAC();

TargetBuffer objBuffer;
IfFailThrow(pDac->GetObjectContents(vmObj, &objBuffer));
VOID* pRemoteAddr = CORDB_ADDRESS_TO_PTR(objBuffer.pAddress);
VOID* pRemoteAddr = CORDB_ADDRESS_TO_PTR(objAddr);
// This creates a local reference that has a remote address in it. Ie &pRemoteAddr is an address
// in the host address space and pRemoteAddr is an address in the target.
MemoryRange localReferenceDescription(&pRemoteAddr, sizeof(pRemoteAddr));
Expand Down Expand Up @@ -2622,10 +2605,8 @@ HRESULT CordbObjectValue::IsDelegate()
{
IDacDbiInterface *pDAC = GetProcess()->GetDAC();

VMPTR_Object vmObj;
IfFailThrow(pDAC->GetObject(objAddr, &vmObj));
Copy link
Copy Markdown
Member

@max-charlamb max-charlamb May 11, 2026

Choose a reason for hiding this comment

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

Is there a reason to move away from VMPTR_Object in favor of the not type safe CORDB_ADDRESS?

I see in this code path it is a bit redundant, but it seems reasonable to thread all of the object creation through the same method for the purpose of address validation (if we want that).

It looks like we could greatly reduce the PR diff if we limit this change to removing the method.

If we still want to change the VMPTR_Object to CORDB_ADDRESS could we do it in a separate PR and handle all the uses uniformly?

Copy link
Copy Markdown
Contributor

@rcj1 rcj1 May 11, 2026

Choose a reason for hiding this comment

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

We need to use CORDB_ADDRESS instead of the VMPTR because VMPTR is not designed to allow us to grab the raw address in the DBI. (Well, technically theres VmPtrToCookie, but I’m not sure it was designed for this.) I am thinking about a way to handle this uniformly - maybe we can provide a conversion operator from VMPTR to another opaque handle type that we only use on signatures.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actually, maybe there is a good way to keep the type safety in the general case. But nevertheless, as this path started out with a raw CORDB_ADDRESS, I don’t think we’re really losing much here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Eh, the thing is with the status quo shape we could verify the object if we wanted to. I wonder if we could just leave this as-is and replace the DBI API with a conversion operator.

BOOL fIsDelegate;
IfFailThrow(pDAC->IsDelegate(vmObj, &fIsDelegate));
IfFailThrow(pDAC->IsDelegate(objAddr, &fIsDelegate));

if (!fIsDelegate)
hr = S_FALSE;
Expand All @@ -2650,25 +2631,23 @@ HRESULT IsSupportedDelegateHelper(IDacDbiInterface::DelegateType delType)
HRESULT CordbObjectValue::GetTargetHelper(ICorDebugReferenceValue **ppTarget)
{
IDacDbiInterface::DelegateType delType;
VMPTR_Object pDelegateObj;
VMPTR_Object pDelegateTargetObj;
CORDB_ADDRESS pDelegateTargetObj;
VMPTR_AppDomain pAppDomainOfTarget;

CORDB_ADDRESS delegateAddr = m_valueHome.GetAddress();

IDacDbiInterface *pDAC = GetProcess()->GetDAC();
IfFailThrow(pDAC->GetObject(delegateAddr, &pDelegateObj));

HRESULT hr = pDAC->GetDelegateType(pDelegateObj, &delType);
HRESULT hr = pDAC->GetDelegateType(delegateAddr, &delType);
if (hr != S_OK)
return hr;

hr = IsSupportedDelegateHelper(delType);
if (hr != S_OK)
return hr;

hr = pDAC->GetDelegateTargetObject(delType, pDelegateObj, &pDelegateTargetObj, &pAppDomainOfTarget);
if (hr != S_OK || pDelegateTargetObj.IsNull())
hr = pDAC->GetDelegateTargetObject(delType, delegateAddr, &pDelegateTargetObj, &pAppDomainOfTarget);
if (hr != S_OK || pDelegateTargetObj == (CORDB_ADDRESS)NULL)
{
*ppTarget = NULL;
return hr;
Expand All @@ -2686,15 +2665,13 @@ HRESULT CordbObjectValue::GetTargetHelper(ICorDebugReferenceValue **ppTarget)
HRESULT CordbObjectValue::GetFunctionHelper(ICorDebugFunction **ppFunction)
{
IDacDbiInterface::DelegateType delType;
VMPTR_Object pDelegateObj;

*ppFunction = NULL;
CORDB_ADDRESS delegateAddr = m_valueHome.GetAddress();

IDacDbiInterface *pDAC = GetProcess()->GetDAC();
IfFailThrow(pDAC->GetObject(delegateAddr, &pDelegateObj));

HRESULT hr = pDAC->GetDelegateType(pDelegateObj, &delType);
HRESULT hr = pDAC->GetDelegateType(delegateAddr, &delType);
if (hr != S_OK)
return hr;

Expand All @@ -2708,7 +2685,7 @@ HRESULT CordbObjectValue::GetFunctionHelper(ICorDebugFunction **ppFunction)

VMPTR_Assembly functionAssembly;
mdMethodDef functionMethodDef = 0;
hr = pDAC->GetDelegateFunctionData(delType, pDelegateObj, &functionAssembly, &functionMethodDef);
hr = pDAC->GetDelegateFunctionData(delType, delegateAddr, &functionAssembly, &functionMethodDef);
if (hr != S_OK)
return hr;

Expand Down
6 changes: 1 addition & 5 deletions src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -8621,13 +8621,9 @@ class CordbValue : public CordbBase
EnregisteredValueHomeHolder * ppRemoteRegAddr,
ICorDebugValue** ppValue);

// Create the proper ICDValue instance based on the given remote heap object
static ICorDebugValue* CreateHeapValue(CordbAppDomain* pAppDomain,
VMPTR_Object vmObj);

// Creates a proper CordbReferenceValue instance based on the given remote heap object
static CordbReferenceValue* CreateHeapReferenceValue(CordbAppDomain* pAppDomain,
VMPTR_Object vmObj);
CORDB_ADDRESS objAddr);

// Returns a pointer to the ValueHome field of this instance of CordbValue if one exists or NULL
// otherwise. Therefore, this also tells us indirectly whether this instance of CordbValue is also an
Expand Down
32 changes: 8 additions & 24 deletions src/coreclr/debug/inc/dacdbiinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,7 @@ IDacDbiInterface : public IUnknown
// S_OK on success; otherwise, an appropriate failure HRESULT.
//
// Notes:
// The VMPTR this produces can be deconstructed by GetObjectContents.
// The VMPTR this produces wraps the target address that was passed in.
// This function will return a failure HRESULT if given a NULL or otherwise invalid pointer,
// but if given a valid address to an invalid pointer, it will produce
// a VMPTR_Object which points to invalid memory.
Comment thread
rcj1 marked this conversation as resolved.
Expand All @@ -1800,7 +1800,7 @@ IDacDbiInterface : public IUnknown
// S_OK on success; otherwise, an appropriate failure HRESULT.
//
// Notes:
// The VMPTR this produces can be deconstructed by GetObjectContents.
// The VMPTR this produces wraps the target address that was passed in.
// This will produce a VMPTR_Object regardless of whether the pointer is
// valid or not.
virtual HRESULT STDMETHODCALLTYPE GetObject(CORDB_ADDRESS ptr, OUT VMPTR_Object * pRetVal) = 0;
Expand Down Expand Up @@ -1893,22 +1893,6 @@ IDacDbiInterface : public IUnknown
//
virtual HRESULT STDMETHODCALLTYPE GetHandleAddressFromVmHandle(VMPTR_OBJECTHANDLE vmHandle, OUT CORDB_ADDRESS * pRetVal) = 0;

// Given a VMPTR to an Object, get the target address.
//
// Arguments:
// obj - the Object VMPTR to get the address from
// pRetVal - [out] The target address which obj is using.
//
// Return Value:
// S_OK on success; otherwise, an appropriate failure HRESULT.
//
// Notes:
// The VMPTR this consumes can be reconstructed using GetObject and
// providing the address stored in the returned TargetBuffer. This has
// undefined behavior for invalid VMPTR_Objects.

virtual HRESULT STDMETHODCALLTYPE GetObjectContents(VMPTR_Object obj, OUT TargetBuffer * pRetVal) = 0;

//
// Get the thread which owns the monitor lock on an object and the acquisition
// count
Expand Down Expand Up @@ -2253,24 +2237,24 @@ IDacDbiInterface : public IUnknown
// Returns true if the object is a type deriving from System.MulticastDelegate
//
// Arguments:
// vmObject - pointer to runtime object to query for.
// objectAddress - address of runtime object to query for.
// pResult - [out]
//
virtual HRESULT STDMETHODCALLTYPE IsDelegate(VMPTR_Object vmObject, OUT BOOL * pResult) = 0;
virtual HRESULT STDMETHODCALLTYPE IsDelegate(CORDB_ADDRESS objectAddress, OUT BOOL * pResult) = 0;

// Get the delegate type
virtual HRESULT STDMETHODCALLTYPE GetDelegateType(VMPTR_Object delegateObject, DelegateType *delegateType) = 0;
virtual HRESULT STDMETHODCALLTYPE GetDelegateType(CORDB_ADDRESS delegateObject, DelegateType *delegateType) = 0;

virtual HRESULT STDMETHODCALLTYPE GetDelegateFunctionData(
DelegateType delegateType,
VMPTR_Object delegateObject,
CORDB_ADDRESS delegateObject,
OUT VMPTR_Assembly *ppFunctionAssembly,
OUT mdMethodDef *pMethodDef) = 0;

virtual HRESULT STDMETHODCALLTYPE GetDelegateTargetObject(
DelegateType delegateType,
VMPTR_Object delegateObject,
OUT VMPTR_Object *ppTargetObj,
CORDB_ADDRESS delegateObject,
OUT CORDB_ADDRESS *ppTargetObj,
OUT VMPTR_AppDomain *ppTargetAppDomain) = 0;

virtual HRESULT STDMETHODCALLTYPE GetLoaderHeapMemoryRanges(OUT DacDbiArrayList<COR_MEMORY_RANGE> *pRanges) = 0;
Expand Down
13 changes: 6 additions & 7 deletions src/coreclr/inc/dacdbi.idl
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,7 @@ interface IDacDbiInterface : IUnknown
HRESULT IsWinRTModule([in] VMPTR_Module vmModule, [out] BOOL * pIsWinRT);
HRESULT GetHandleAddressFromVmHandle([in] VMPTR_OBJECTHANDLE vmHandle, [out] CORDB_ADDRESS * pRetVal);

// Object Contents and Monitor
HRESULT GetObjectContents([in] VMPTR_Object obj, [out] struct TargetBuffer * pRetVal);
// Monitor
HRESULT GetThreadOwningMonitorLock([in] VMPTR_Object vmObject, [out] struct MonitorLockInfo * pRetVal);
HRESULT EnumerateMonitorEventWaitList([in] VMPTR_Object vmObject, [in] FP_THREAD_ENUMERATION_CALLBACK fpCallback, [in] CALLBACK_DATA pUserData);

Expand Down Expand Up @@ -416,17 +415,17 @@ interface IDacDbiInterface : IUnknown
HRESULT EnableGCNotificationEvents([in] BOOL fEnable);

// Delegate
HRESULT IsDelegate([in] VMPTR_Object vmObject, [out] BOOL * pResult);
HRESULT GetDelegateType([in] VMPTR_Object delegateObject, [out] DelegateType * delegateType);
HRESULT IsDelegate([in] CORDB_ADDRESS objectAddress, [out] BOOL * pResult);
HRESULT GetDelegateType([in] CORDB_ADDRESS delegateObject, [out] DelegateType * delegateType);
HRESULT GetDelegateFunctionData(
[in] DelegateType delegateType,
[in] VMPTR_Object delegateObject,
[in] CORDB_ADDRESS delegateObject,
[out] VMPTR_Assembly * ppFunctionAssembly,
[out] mdMethodDef * pMethodDef);
HRESULT GetDelegateTargetObject(
[in] DelegateType delegateType,
[in] VMPTR_Object delegateObject,
[out] VMPTR_Object * ppTargetObj,
[in] CORDB_ADDRESS delegateObject,
[out] CORDB_ADDRESS * ppTargetObj,
[out] VMPTR_AppDomain * ppTargetAppDomain);

// Loader Heap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1574,9 +1574,6 @@ public int GetHandleAddressFromVmHandle(ulong vmHandle, ulong* pRetVal)
return hr;
}

public int GetObjectContents(ulong obj, DacDbiTargetBuffer* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectContents(obj, pRetVal) : HResults.E_NOTIMPL;

public int GetThreadOwningMonitorLock(ulong vmObject, DacDbiMonitorLockInfo* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadOwningMonitorLock(vmObject, pRetVal) : HResults.E_NOTIMPL;

Expand Down Expand Up @@ -2112,8 +2109,8 @@ public int EnableGCNotificationEvents(Interop.BOOL fEnable)
return hr;
}

public int IsDelegate(ulong vmObject, Interop.BOOL* pResult)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.IsDelegate(vmObject, pResult) : HResults.E_NOTIMPL;
public int IsDelegate(ulong objectAddress, Interop.BOOL* pResult)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.IsDelegate(objectAddress, pResult) : HResults.E_NOTIMPL;

public int GetDelegateType(ulong delegateObject, int* delegateType)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetDelegateType(delegateObject, delegateType) : HResults.E_NOTIMPL;
Expand Down
Loading
Loading