Forum Xamarin.iOS

Handling exceptions coming from managed code in Objective-C

FelixDeimelFelixDeimel ATMember ✭✭

Hi,

I'm using Xamarin.Mac to expose a large C#-based library to a native Objective-C application.
I do this by exposing wrappers objects of my managed classes decorated with [Register] and [Export] to the Objective-C runtime.

I know this is an unsupported scenario and there are other ways to embed the mono runtime in native applications. However, since the managed library I'm exposing to the Objective-C runtime is rather large and complex, I found it nearly impossible or just way to complicated to do this using this method: http://www.mono-project.com/docs/advanced/embedding/
Furthermore, the way I set it up using Xamarin.Mac works really well for me except for one thing: Exception handling.

The problem is that I can't figure out how to handle an exception thrown on the managed side in Objective-C. If I wrap my ObjC call that ends up being executed in managed code with @try @catch, the catch block is never hit and the application just crashes.
This is even the case when creating an NSException object in managed code and throwing it via [NSException raise] (Using PerformSelector or objc_msgSend since there's no binding built into Xamarin.Mac).

Another thing I tried was to construct the NSException object in managed code, pass it to a helper function in the native library and call [NSException raise] there. Same issue.

If however I raise an exception in ObjC without any managed code involved, I can handle it just fine using @try @catch so there seems to be some magic happening behind the scenes to marshal exceptions to the managed world when the call stack includes a managed function.

So, my question is: Is there any way I can handle an exception coming from the managed world in native ObjC code? While I could understand why it's not possible to handle a managed exception in native code, I can't accept that it's not possible to handle an NSException raised from either managed or native code just because there's a managed function involved in the call stack.

many thx,
felix

Answers

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    So, I've having a little bit of trouble following how you managed to get things working with your approach, but you should take a look at:

    https://github.com/abock/EmbeddedXamarinMac

    That is an approach I know customers have used successfully (thought unsupported) in the past.

    I don't believe you can safely throw objective-c exceptions from inside c# code and expect things to go well. In many ways, exceptions are like goto jumps, where we transfer control from one chunk of code to another. If you are deep inside mono, executing C# code, jumping into objective-c without cleaning up and putting things back in order will likely be unrecoverable.

    I believe the last param to mono_runtime_invoke is handle to a MonoObject for the managed exception. If you pass in a place for it to fill in and throw a C# exception, does it come back valid?

  • FelixDeimelFelixDeimel ATMember ✭✭

    Hi @ChrisHamons,

    thx for getting back to me.

    I know about the embedding sample you mentioned. Unfortunately it's more or less the same approach as the one I mentioned being too complicated.

    My approach is to not deal at all with the C-based mono interfaces (MonoObject, etc.) but instead use the features already present in Xamarin.Mac to expose C# classes to the Objective-C runtime.
    Like mentioned, I do this by simply decorating my classes with [Register] and making sure they inherit from NSObject. Then I mark all the methods I want to expose to the Objective-C runtime with [Export] and a matching selector.

    On the Objective-C side, I use NSClassFromString to get at the classes and allocate objects. Then I use protocols to describe their interfaces.
    That way I can call into managed code from Objective-C without having to deal with the mono runtime on a lower level. It also enables the use of Xamarin.Mac built-in marshalers for strings, etc.

    Regarding my question, I found a viable workaround by using a block-based interface to kind of re-build a Try-Catch-Finally block.

    Here's how this looks on the C# side: https://gist.github.com/lemonmojo/f8b7f3bd80f264e65293cd0a5cbaa825
    And the matching Objective-C protocol: https://gist.github.com/lemonmojo/98c9534de6a3f101b9a8261bb1a81a81

    Note that unlike other classes in my wrapper framework this one can't be initialized from Objective-C as it will always be created on the managed side.

    cheers,
    felix

Sign In or Register to comment.