Skip to content

Commit 541c68e

Browse files
authored
Respect ISupportErrorInfo for HRESULT to Exception throwing conversion (#117690)
1 parent a2025e8 commit 541c68e

File tree

20 files changed

+234
-139
lines changed

20 files changed

+234
-139
lines changed

src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,26 +1351,20 @@ internal static void DestroyCleanupList(ref CleanupWorkListElement? pCleanupWork
13511351

13521352
internal static Exception GetHRExceptionObject(int hr)
13531353
{
1354-
Exception? ex = null;
1355-
GetHRExceptionObject(hr, ObjectHandleOnStack.Create(ref ex));
1356-
ex!.InternalPreserveStackTrace();
1357-
return ex!;
1354+
Exception ex = Marshal.GetExceptionForHR(hr)!;
1355+
ex.InternalPreserveStackTrace();
1356+
return ex;
13581357
}
13591358

1360-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_GetHRExceptionObject")]
1361-
private static partial void GetHRExceptionObject(int hr, ObjectHandleOnStack throwable);
1362-
13631359
#if FEATURE_COMINTEROP
1364-
internal static Exception GetCOMHRExceptionObject(int hr, IntPtr pCPCMD, object pThis)
1360+
internal static Exception GetCOMHRExceptionObject(int hr, IntPtr pCPCMD, IntPtr pUnk)
13651361
{
1366-
Exception? ex = null;
1367-
GetCOMHRExceptionObject(hr, pCPCMD, ObjectHandleOnStack.Create(ref pThis), ObjectHandleOnStack.Create(ref ex));
1368-
ex!.InternalPreserveStackTrace();
1369-
return ex!;
1362+
RuntimeMethodHandle handle = RuntimeMethodHandle.FromIntPtr(pCPCMD);
1363+
RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(handle.GetMethodInfo());
1364+
Exception ex = Marshal.GetExceptionForHR(hr, declaringType.GUID, pUnk)!;
1365+
ex.InternalPreserveStackTrace();
1366+
return ex;
13701367
}
1371-
1372-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_GetCOMHRExceptionObject")]
1373-
private static partial void GetCOMHRExceptionObject(int hr, IntPtr pCPCMD, ObjectHandleOnStack pThis, ObjectHandleOnStack throwable);
13741368
#endif // FEATURE_COMINTEROP
13751369

13761370
[ThreadStatic]

src/coreclr/vm/corelib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ DEFINE_METHOD(BUFFER, MEMCOPYGC, BulkMoveWithWriteBar
10391039
DEFINE_CLASS(STUBHELPERS, StubHelpers, StubHelpers)
10401040
DEFINE_METHOD(STUBHELPERS, GET_DELEGATE_TARGET, GetDelegateTarget, SM_Delegate_RetIntPtr)
10411041
#ifdef FEATURE_COMINTEROP
1042-
DEFINE_METHOD(STUBHELPERS, GET_COM_HR_EXCEPTION_OBJECT, GetCOMHRExceptionObject, SM_Int_IntPtr_Obj_RetException)
1042+
DEFINE_METHOD(STUBHELPERS, GET_COM_HR_EXCEPTION_OBJECT, GetCOMHRExceptionObject, SM_Int_IntPtr_IntPtr_RetException)
10431043
DEFINE_METHOD(STUBHELPERS, GET_COM_IP_FROM_RCW, GetCOMIPFromRCW, SM_Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr)
10441044
#endif // FEATURE_COMINTEROP
10451045
DEFINE_METHOD(STUBHELPERS, SET_LAST_ERROR, SetLastError, SM_RetVoid)

src/coreclr/vm/dllimport.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -736,14 +736,9 @@ class ILStubState : public StubState
736736
#ifdef FEATURE_COMINTEROP
737737
if (SF_IsForwardCOMStub(m_dwStubFlags))
738738
{
739-
// Make sure that the RCW stays alive for the duration of the call. Note that if we do HRESULT
740-
// swapping, we'll pass 'this' to GetCOMHRExceptionObject after returning from the target so
741-
// GC.KeepAlive is not necessary.
742-
if (!SF_IsHRESULTSwapping(m_dwStubFlags))
743-
{
744-
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
745-
pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
746-
}
739+
// Make sure that the RCW stays alive for the duration of the call.
740+
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
741+
pcsDispatch->EmitCALL(METHOD__GC__KEEP_ALIVE, 1, 0);
747742
}
748743
#endif // FEATURE_COMINTEROP
749744

@@ -761,7 +756,7 @@ class ILStubState : public StubState
761756
if (SF_IsCOMStub(m_dwStubFlags))
762757
{
763758
m_slIL.EmitLoadStubContext(pcsDispatch, m_dwStubFlags);
764-
m_slIL.EmitLoadRCWThis(pcsDispatch, m_dwStubFlags);
759+
pcsDispatch->EmitLDLOC(m_slIL.GetTargetInterfacePointerLocalNum());
765760

766761
pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_COM_HR_EXCEPTION_OBJECT, 3, 1);
767762
}

src/coreclr/vm/metasig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@
169169

170170

171171
// static methods:
172-
DEFINE_METASIG_T(SM(Int_IntPtr_Obj_RetException, i I j, C(EXCEPTION)))
172+
DEFINE_METASIG_T(SM(Int_IntPtr_IntPtr_RetException, i I I, C(EXCEPTION)))
173173
DEFINE_METASIG_T(SM(Type_CharPtr_RuntimeAssembly_Bool_Bool_IntPtr_RetRuntimeType, P(u) C(ASSEMBLY) F F I, C(CLASS)))
174174
DEFINE_METASIG_T(SM(Type_RetIntPtr, C(TYPE), I))
175175
DEFINE_METASIG(SM(RefIntPtr_IntPtr_IntPtr_Int_RetObj, r(I) I I i, j))

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,7 @@ static const Entry s_QCall[] =
518518
DllImportEntry(StubHelpers_ProfilerBeginTransitionCallback)
519519
DllImportEntry(StubHelpers_ProfilerEndTransitionCallback)
520520
#endif
521-
DllImportEntry(StubHelpers_GetHRExceptionObject)
522521
#if defined(FEATURE_COMINTEROP)
523-
DllImportEntry(StubHelpers_GetCOMHRExceptionObject)
524522
DllImportEntry(StubHelpers_GetCOMIPFromRCWSlow)
525523
DllImportEntry(ObjectMarshaler_ConvertToNative)
526524
DllImportEntry(ObjectMarshaler_ConvertToManaged)

src/coreclr/vm/stubhelpers.cpp

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -543,81 +543,6 @@ extern "C" void QCALLTYPE StubHelpers_ProfilerEndTransitionCallback(MethodDesc*
543543
}
544544
#endif // PROFILING_SUPPORTED
545545

546-
extern "C" void QCALLTYPE StubHelpers_GetHRExceptionObject(HRESULT hr, QCall::ObjectHandleOnStack result)
547-
{
548-
QCALL_CONTRACT;
549-
550-
BEGIN_QCALL;
551-
552-
GCX_COOP();
553-
554-
OBJECTREF oThrowable = NULL;
555-
GCPROTECT_BEGIN(oThrowable);
556-
557-
// GetExceptionForHR uses equivalant logic as COMPlusThrowHR
558-
GetExceptionForHR(hr, &oThrowable);
559-
result.Set(oThrowable);
560-
561-
GCPROTECT_END();
562-
563-
END_QCALL;
564-
}
565-
566-
#ifdef FEATURE_COMINTEROP
567-
extern "C" void QCALLTYPE StubHelpers_GetCOMHRExceptionObject(
568-
HRESULT hr,
569-
MethodDesc* pMD,
570-
QCall::ObjectHandleOnStack pThis,
571-
QCall::ObjectHandleOnStack result)
572-
{
573-
QCALL_CONTRACT;
574-
575-
BEGIN_QCALL;
576-
577-
GCX_COOP();
578-
579-
struct
580-
{
581-
OBJECTREF oThrowable;
582-
OBJECTREF oref;
583-
} gc;
584-
gc.oThrowable = NULL;
585-
gc.oref = NULL;
586-
GCPROTECT_BEGIN(gc);
587-
588-
IErrorInfo* pErrorInfo = NULL;
589-
if (pMD != NULL)
590-
{
591-
// Retrieve the interface method table.
592-
MethodTable* pItfMT = CLRToCOMCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT;
593-
594-
// get 'this'
595-
gc.oref = ObjectToOBJECTREF(pThis.Get());
596-
597-
// Get IUnknown pointer for this interface on this object
598-
IUnknown* pUnk = ComObject::GetComIPFromRCW(&gc.oref, pItfMT);
599-
if (pUnk != NULL)
600-
{
601-
// Check to see if the component supports error information for this interface.
602-
IID ItfIID;
603-
pItfMT->GetGuid(&ItfIID, TRUE);
604-
pErrorInfo = GetSupportedErrorInfo(pUnk, ItfIID);
605-
606-
DWORD cbRef = SafeRelease(pUnk);
607-
LogInteropRelease(pUnk, cbRef, "IUnk to QI for ISupportsErrorInfo");
608-
}
609-
}
610-
611-
// GetExceptionForHR will handle lifetime of IErrorInfo.
612-
GetExceptionForHR(hr, pErrorInfo, &gc.oThrowable);
613-
result.Set(gc.oThrowable);
614-
615-
GCPROTECT_END();
616-
617-
END_QCALL;
618-
}
619-
#endif // FEATURE_COMINTEROP
620-
621546
extern "C" void QCALLTYPE StubHelpers_MarshalToManagedVaList(va_list va, VARARGS* pArgIterator)
622547
{
623548
QCALL_CONTRACT;

src/coreclr/vm/stubhelpers.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ extern "C" void* QCALLTYPE StubHelpers_ProfilerBeginTransitionCallback(MethodDes
4646
extern "C" void QCALLTYPE StubHelpers_ProfilerEndTransitionCallback(MethodDesc* pTargetMD);
4747
#endif
4848

49-
extern "C" void QCALLTYPE StubHelpers_GetHRExceptionObject(HRESULT hr, QCall::ObjectHandleOnStack result);
50-
5149
#ifdef FEATURE_COMINTEROP
52-
extern "C" void QCALLTYPE StubHelpers_GetCOMHRExceptionObject(HRESULT hr, MethodDesc *pMD, QCall::ObjectHandleOnStack pThis, QCall::ObjectHandleOnStack result);
53-
5450
extern "C" IUnknown* QCALLTYPE StubHelpers_GetCOMIPFromRCWSlow(QCall::ObjectHandleOnStack pSrc, MethodDesc* pMD, void** ppTarget);
5551

5652
extern "C" void QCALLTYPE ObjectMarshaler_ConvertToNative(QCall::ObjectHandleOnStack pSrcUNSAFE, VARIANT* pDest);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class OleAut32
10+
{
11+
[LibraryImport(Libraries.OleAut32)]
12+
internal static partial int GetErrorInfo(uint dwReserved, out IntPtr ppErrorInfo);
13+
}
14+
}

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,9 @@
21572157
<Compile Include="$(CommonPath)Interop\Windows\Ole32\Interop.PropVariantClear.cs">
21582158
<Link>Common\Interop\Windows\Ole32\Interop.PropVariantClear.cs</Link>
21592159
</Compile>
2160+
<Compile Include="$(CommonPath)Interop\Windows\OleAut32\Interop.GetErrorInfo.cs">
2161+
<Link>Common\Interop\Windows\OleAut32\Interop.GetErrorInfo.cs</Link>
2162+
</Compile>
21602163
<Compile Include="$(CommonPath)Interop\Windows\Secur32\Interop.GetUserNameExW.cs">
21612164
<Link>Common\Interop\Windows\Secur32\Interop.GetUserNameExW.cs</Link>
21622165
</Compile>

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,63 @@ public static IntPtr GetHINSTANCE(Module m)
653653
return GetExceptionForHRInternal(errorCode, errorInfo);
654654
}
655655

656+
public static Exception? GetExceptionForHR(int errorCode, in Guid iid, IntPtr pUnk)
657+
{
658+
if (errorCode >= 0)
659+
{
660+
return null;
661+
}
662+
663+
return GetExceptionForHRInternal(errorCode, in iid, pUnk);
664+
}
665+
666+
private static unsafe Exception? GetExceptionForHRInternal(int errorCode, in Guid iid, IntPtr pUnk)
667+
{
668+
const IntPtr NoErrorInfo = -1; // Use -1 to indicate no error info available
669+
670+
// Normally, we would check if the interface supports IErrorInfo first. However,
671+
// built-in COM calls GetErrorInfo first to clear the error info, so we follow
672+
// that pattern here.
673+
IntPtr errorInfo = NoErrorInfo;
674+
675+
#if TARGET_WINDOWS
676+
Interop.OleAut32.GetErrorInfo(0, out errorInfo);
677+
if (errorInfo == IntPtr.Zero)
678+
{
679+
errorInfo = NoErrorInfo;
680+
}
681+
682+
// If there is error info and we have a pointer to the interface,
683+
// we check if it supports ISupportErrorInfo.
684+
if (errorInfo != NoErrorInfo && pUnk != IntPtr.Zero)
685+
{
686+
Guid IID_ISupportErrorInfo = new(0xDF0B3D60, 0x548F, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19);
687+
int hr = QueryInterface(pUnk, in IID_ISupportErrorInfo, out IntPtr supportErrorInfo);
688+
if (hr == 0)
689+
{
690+
// Check if the target interface is supported.
691+
// ISupportErrorInfo.InterfaceSupportsErrorInfo slot
692+
fixed (Guid* piid = &iid)
693+
{
694+
hr = ((delegate* unmanaged[MemberFunction]<IntPtr, Guid*, int>)(*(*(void***)supportErrorInfo + 3)))(supportErrorInfo, piid);
695+
}
696+
Release(supportErrorInfo);
697+
}
698+
699+
// If ISupportErrorInfo isn't supported or the target interface doesn't support IErrorInfo,
700+
// release the error info and mark it as NoErrorInfo to avoid querying for IErrorInfo again.
701+
if (hr != 0)
702+
{
703+
Release(errorInfo);
704+
errorInfo = NoErrorInfo;
705+
}
706+
}
707+
#endif
708+
709+
// If the error info is valid, its lifetime will be handled by GetExceptionForHRInternal().
710+
return GetExceptionForHRInternal(errorCode, errorInfo);
711+
}
712+
656713
#if !CORECLR
657714
#pragma warning disable IDE0060
658715
private static Exception? GetExceptionForHRInternal(int errorCode, IntPtr errorInfo)
@@ -865,6 +922,14 @@ public static void ThrowExceptionForHR(int errorCode, IntPtr errorInfo)
865922
}
866923
}
867924

925+
public static void ThrowExceptionForHR(int errorCode, in Guid iid, IntPtr pUnk)
926+
{
927+
if (errorCode < 0)
928+
{
929+
throw GetExceptionForHR(errorCode, in iid, pUnk)!;
930+
}
931+
}
932+
868933
public static IntPtr SecureStringToBSTR(SecureString s)
869934
{
870935
if (s is null)

0 commit comments

Comments
 (0)