Catching C# exception from callback used inside C++ back in C#

Viewed 56

I'm currently working with code resembling sth like this:

[DllImport("<native-dlllibrary>.dll")]
internal unsafe static extern void SetCallback([...], <delegateType> callback);

// This method calls provided callback in case of error
// synchronously 
DllImport("<native-dlllibrary>.dll")]
internal unsafe static extern void SomeOperation([...]);

[...]

private void ActualCallback([...])
    => throw new Exception();

[...]

{
    SetCallback([...], callback);
    try
    {
        SomeOperation([...]);
    }
    catch(Exception e)
    {
        [...] // process exception
    }
}

The dll library is pure C/C++ (no C++/CLI), so the exception should ideally flow C# (callback) -> C/C++ (SomeOperation) -> C# (try catch block), however when it is thrown program pretty much freezes. Is it possible for that exception to reach the final C# try catch block (out of the box without workarounds)? I know that catching non-CLS compliant exceptions is possible (Can you catch a native exception in C# code?), but in this case I'm throwing a CLS compliant exception passing native boundary.

1 Answers

Throwing an exception across a managed-to-native boundary and expecting it to reach the native-to-managed boundary is a recipe for disaster. The C++ code would have to be have been written to take into account SEH exceptions and safely unwind, which a lot of C++ code isn't.

Instead, it's probably easier to have the callback save the excption and then rethrow it. You may want the callback to return a bool as to whether to continue the native function.

Exception _ex;

private bool ActualCallback([...])
{
    try
    {
        // do stuff
        if (someCondition)
            throw new Exception();
        // more stuff
        return true;
    }
    catch(Exception ex)
    {
        _ex = ex;
        return false;
    }
}

private void DoStuffNatively()
{
    _ex = null;
    SetCallback([...], callback);
    try
    {
        SomeOperation([...]);
        if(_ex != null)
            System.Runtime.ExceptionServices.ExceptionDispatchInfoCapture(_ex).Throw();
    }
    catch(Exception e)
    {
        [...] // process exception
    }
}
Related