Repository: dotnet/runtime
File: src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs (C#)

Code before:

        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MngdSafeArrayMarshaler_ConvertContentsToManaged")]
        private static partial void ConvertContentsToManaged(IntPtr pMarshalState, ObjectHandleOnStack pManagedHome, IntPtr pNativeHome);

#pragma warning disable IDE0060 // Remove unused parameter. These APIs need to match a the shape of a "managed" marshaler.
        internal static void ClearNative(IntPtr pMarshalState, in object pManagedHome, IntPtr pNativeHome)
        {
            ClearNative(pMarshalState, pNativeHome);
        }
#pragma warning restore IDE0060

        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MngdSafeArrayMarshaler_ClearNative")]
        private static partial void ClearNative(IntPtr pMarshalState, IntPtr pNativeHome);
    }  // class MngdSafeArrayMarshaler
#endif // FEATURE_COMINTEROP

    internal static unsafe partial class MngdRefCustomMarshaler
    {
        [UnmanagedCallersOnly]
        internal static void ConvertContentsToNative(ObjectHandleOnStack marshaler, ObjectHandleOnStack pManagedHome, IntPtr* pNativeHome, ObjectHandleOnStack exception)
        {
            try
            {
                ICustomMarshaler customMarshaler = (ICustomMarshaler)marshaler.Value!;
                object managedHome = pManagedHome.Value!;
                ConvertContentsToNative(customMarshaler, in managedHome, pNativeHome);
            }
            catch (Exception ex)
            {
                exception.Value = ex;
            }
        }

        internal static void ConvertContentsToNative(ICustomMarshaler marshaler, in object pManagedHome, IntPtr* pNativeHome)
        {
            // COMPAT: We never pass null to MarshalManagedToNative.
            if (pManagedHome is null)
            {
                *pNativeHome = IntPtr.Zero;
                return;
            }

            *pNativeHome = marshaler.MarshalManagedToNative(pManagedHome);
        }

        [UnmanagedCallersOnly]
        internal static void ConvertContentsToManaged(ObjectHandleOnStack marshaler, ObjectHandleOnStack pManagedHome, IntPtr* pNativeHome, ObjectHandleOnStack exception)
        {
            try
            {
                ICustomMarshaler customMarshaler = (ICustomMarshaler)marshaler.Value!;

Review comment (bug):
We may need something more elaborate like `catch (Exception ex) PropagateExceptionToNativeCode(e)` to avoid regressing diagnostic experience. It may be needed only in some cases, not sure.

For example, the Main method is called via CallDescWorker today. If we wrap the main method in a UCO that has try/catch iike this, the crash dumps will end up with unwond stack that will make investigation of unhandled exceptions in crash dumps much harder.

cc @janvorli 

Code after:

        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MngdSafeArrayMarshaler_ConvertContentsToManaged")]
        private static partial void ConvertContentsToManaged(IntPtr pMarshalState, ObjectHandleOnStack pManagedHome, IntPtr pNativeHome);

#pragma warning disable IDE0060 // Remove unused parameter. These APIs need to match a the shape of a "managed" marshaler.
        internal static void ClearNative(IntPtr pMarshalState, in object pManagedHome, IntPtr pNativeHome)
        {
            ClearNative(pMarshalState, pNativeHome);
        }
#pragma warning restore IDE0060

        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MngdSafeArrayMarshaler_ClearNative")]
        private static partial void ClearNative(IntPtr pMarshalState, IntPtr pNativeHome);
    }  // class MngdSafeArrayMarshaler
#endif // FEATURE_COMINTEROP

    internal static unsafe partial class MngdRefCustomMarshaler
    {
        [UnmanagedCallersOnly]
        internal static void ConvertContentsToNative(ICustomMarshaler* pMarshaler, object* pManagedHome, IntPtr* pNativeHome, Exception* pException)
        {
            try
            {
                ConvertContentsToNative(*pMarshaler, in *pManagedHome, pNativeHome);
            }
            catch (Exception ex)
            {
                *pException = ex;
            }
        }

        internal static void ConvertContentsToNative(ICustomMarshaler marshaler, in object pManagedHome, IntPtr* pNativeHome)
        {
            // COMPAT: We never pass null to MarshalManagedToNative.
            if (pManagedHome is null)
            {
                *pNativeHome = IntPtr.Zero;
                return;
            }

            *pNativeHome = marshaler.MarshalManagedToNative(pManagedHome);
        }

        [UnmanagedCallersOnly]
        internal static void ConvertContentsToManaged(ICustomMarshaler* pMarshaler, object* pManagedHome, IntPtr* pNativeHome, Exception* pException)
        {
            try
            {
                ConvertContentsToManaged(*pMarshaler, ref *pManagedHome, pNativeHome);
            }
            catch (Exception ex)