'My C++ library cannot catch the exceptions that it throws, on certain machines

I have a simple C#.NET Windows Forms application. It dynamically loads a C++ DLL (made with RAD Studio) and calls its methods.

Sometimes the library throws exceptions and catches them, so that it returns a meaningful error to my application. On some machines this behaviour makes the whole application crash, because apparently the exception is not catched by the library (and neither by my application). How can it be possible?

Apllication and DLL have 64-bit architecture. There are no problems if I build in 32-bit.

The machine where it works have Windows Server 2008 R2 Enterprise.

The machines where it doesn't work has Windows Server 2019 Datacenter and Windows 10.

This is my application:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace TestMyLib
{
    public partial class FMain : Form
    {
        public static readonly string LIBRARY_NAME = "MyLib.dll";
        private IntPtr handle;

        private void btnMM489_Click(object sender, EventArgs ea)
        {
            try
            {
                //load library
                string libPath = Path.Combine(Directory.GetCurrentDirectory(), "lib", LIBRARY_NAME);
                handle = NativeDlls.LoadLibraryEx(libPath, IntPtr.Zero, NativeDlls.LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH);
                if (handle == IntPtr.Zero)
                    throw new Exception(string.Format("Failed to load library '{0}'. {1}", LIBRARY_NAME, Marshal.GetLastWin32Error()));

                //call some lib functions
                //...

                //call a function (_FailingFunction)
                //that we know will launch an exception inside its try/catch
                Type delegateFunType = typeof(_FailingFunction);
                IntPtr funAddr = NativeDlls.GetProcAddress(handle, delegateFunType.Name);
                dynamic fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);

                //********* THIS LINE MAKES THE APPLICATION CRASH *************
                fun.Invoke();

                //...

                MessageBox.Show("Success!");
            }
            catch (ExternalException e) { MessageBox.Show(e.ErrorCode + Environment.NewLine + e.Message + Environment.NewLine + e.StackTrace); }
            catch (Exception e) { MessageBox.Show(e.Message + Environment.NewLine + e.StackTrace); }
        }

        //Given delegate type, return the pointer to the DLL function,
        //which can be used to invoke such function
        private dynamic GetFunctionPtr(Type delegateFunType)
        {
            IntPtr funAddr = NativeDlls.GetProcAddress(handle, delegateFunType.Name);
            if (funAddr == IntPtr.Zero)
                throw new Exception(string.Format("Failed to load function {0} from library {1}: error code {2}",
                    delegateFunType.Name, LIBRARY_NAME, Marshal.GetLastWin32Error()));
            var fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);
            return fun;
        }

        //delegates to MyLib.dll methods
        //...
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate bool _FailingFunction();

        public FMain() { InitializeComponent(); }
    }

    /// <summary>
    /// Static class containing some Windows API methods for handling
    /// native (unmanaged) DLLs.
    /// </summary>
    public static class NativeDlls
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

        [Flags]
        public enum LoadLibraryFlags : uint { LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 }

        [DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
    }
}

As you see, it invokes function _FailingFunction of my library. Such function throws an obvious exception and should catch it:

bool FailingFunction()
{
    try
    {
        //****** THROW EXCEPTION! *******
        throw Exception("An error occured!");
        return true;
    }
    catch (Exception &E)
    {
        //****** HERE WE SHOULD CATCH IT... *******
        return false;
    }
}   

In the Windows Events Viewer the crash generates this event:

Applicazione: TestMyLib.exe
Versione framework: v4.0.30319
Descrizione: il processo è stato terminato a causa di un'eccezione non gestita.
Informazioni sull'eccezione: System.AccessViolationException
   in DynamicClass.CallSite.Target(System.Runtime.CompilerServices.Closure, System.Runtime.CompilerServices.CallSite, System.Object, Int32, System.String, System.String, System.Text.StringBuilder, Int32)
   in System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid6[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](System.Runtime.CompilerServices.CallSite, System.__Canon, Int32, System.__Canon, System.__Canon, System.__Canon, Int32)
   in TestMyLib.FMain.btnMM489_Click(System.Object, System.EventArgs)
   in System.Windows.Forms.Control.OnClick(System.EventArgs)
   in System.Windows.Forms.Button.OnClick(System.EventArgs)
   in System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs)
   in System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32)
   in System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
   in System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)
   in System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)
   in System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
   in System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
   in System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
   in System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
   in System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
   in TestMyLib.Program.Main()

and this event:

Nome dell'applicazione che ha generato l'errore: TestMyLib.exe, versione: 1.0.1.2, timestamp: 0xf5550cd3
Nome del modulo che ha generato l'errore: KERNELBASE.dll, versione: 10.0.17763.2183, timestamp: 0x8e097f91
Codice eccezione: 0xc0000005
Offset errore 0x0000000000039329
ID processo che ha generato l'errore: 0x846c
Ora di avvio dell'applicazione che ha generato l'errore: 0x01d82e13c8a73c68
Percorso dell'applicazione che ha generato l'errore: C:\Users\Me\Desktop\TestMyLib\TestMyLib.exe
Percorso del modulo che ha generato l'errore: C:\Windows\System32\KERNELBASE.dll
ID segnalazione: 250ac398-95c1-4ef7-b999-c69d50653af1
Nome completo pacchetto che ha generato l'errore: 
ID applicazione relativo al pacchetto che ha generato l'errore: 


Solution 1:[1]

Error c0000005 is Windows speak for 'access violation' (aka SIGBUS) and is caused by trying to read from or write to an invalid address.

So that's one thing we see from your logs, but maybe that's a red-herring and certainly doesn't seem to be related to the code you posted. But you can catch it - if you find you need to - but not with try ... catch. Instead, you need to use Windows' Structured Exception Handling (SEH), which you will find described in detail here. Basically, what you need to do is:;

__try
{
    some_code_i_dont_trust ();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
    handle_the_low_level_exception ();     // you might `throw` here, for example
}

You should also consult that link to decide what compiler switches you want to use, as these affect the way that the compiler cleans up resources for you (or not) when an exception is thrown. More on that here.

It's actually not obvious to me what the best approach is, but if you wrap all potential hardware exceptions in __try ... __except then, from what Hans Passant posted, you're probably best off not using /EHa.

Divide-by-zero is actually also a 'low level' (aka hardware) exception, so can be trapped and handled in the same way. Again, try ... catch won't see it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Paul Sanders