Ausnahme Marshalling in xamarin. IOSException Marshaling in Xamarin.iOS

Xamarin. IOS enthält neue Ereignisse zur Reaktion auf Ausnahmen, insbesondere in nativem Code.Xamarin.iOS contains new events to help respond to exceptions, particularly in native code.

Sowohl verwalteter Code als auch Ziel-C verfügen über Unterstützung für Lauf Zeit Ausnahmen (try/catch/endlich-Klauseln).Both managed code and Objective-C have support for runtime exceptions (try/catch/finally clauses).

Ihre Implementierungen unterscheiden sich jedoch, was bedeutet, dass die Laufzeitbibliotheken (die Mono-Laufzeit und die Ziel-C-Laufzeitbibliotheken) Probleme verursachen, wenn Sie Ausnahmen behandeln und dann Code ausführen müssen, der in anderen Sprachen geschrieben wurde.However, their implementations are different, which means that the runtime libraries (the Mono runtime and the Objective-C runtime libraries) have problems when they have to handle exceptions and then run code written in other languages.

In diesem Dokument werden die Probleme, die auftreten können, und die möglichen Lösungen erläutert.This document explains the problems that can occur, and the possible solutions.

Sie enthält auch ein Beispiel Projekt, das AusnahmeMarshalling, das verwendet werden kann, um verschiedene Szenarien und deren Lösungen zu testen.It also includes a sample project, Exception Marshaling, which can be used to test different scenarios and their solutions.

ProblemProblem

Das Problem tritt auf, wenn eine Ausnahme ausgelöst wird. während der Stapel Auflösung wird ein Frame gefunden, der nicht mit dem Typ der ausgelösten Ausnahme identisch ist.The problem occurs when an exception is thrown, and during stack unwinding a frame is encountered which does not match the type of exception that was thrown.

Ein typisches Beispiel hierfür für xamarin. IOS oder xamarin. Mac ist, wenn eine Native API eine "Ziel-c"-Ausnahme auslöst und diese "Ziel-c"-Ausnahme irgendwie behandelt werden muss, wenn der Stapel Entwicklungsprozess einen verwalteten Frame erreicht.A typical example of this for Xamarin.iOS or Xamarin.Mac is when a native API throws an Objective-C exception, and then that Objective-C exception must somehow be handled when the stack unwinding process reaches a managed frame.

Die Standardaktion besteht darin, nichts zu Unternehmen.The default action is to do nothing. Im obigen Beispiel bedeutet dies, dass die Ziel-C-Laufzeit verwaltete Frames entladen soll.For the sample above, this means letting the Objective-C runtime unwind managed frames. Das ist problematisch, da die Ziel-C-Laufzeit nicht weiß, wie verwaltete Frames entladen werden. Beispielsweise werden keine- catch oder- finally Klauseln in diesem Frame ausgeführt.This is problematic, because the Objective-C runtime does not know how to unwind managed frames; for example it won't execute any catch or finally clauses in that frame.

Fehlerhafter CodeBroken code

Beachten Sie das folgende Codebeispiel:Consider the following code example:

var dict = new NSMutableDictionary ();
dict.LowLevelSetObject (IntPtr.Zero, IntPtr.Zero); 

Dadurch wird eine "Ziel-C nsinvalidargumentexception" in nativem Code ausgelöst:This will throw an Objective-C NSInvalidArgumentException in native code:

NSInvalidArgumentException *** setObjectForKey: key cannot be nil

Die Stapel Überwachung sieht etwa folgendermaßen aus:And the stack trace will be something like this:

0   CoreFoundation          __exceptionPreprocess + 194
1   libobjc.A.dylib         objc_exception_throw + 52
2   CoreFoundation          -[__NSDictionaryM setObject:forKey:] + 1015
3   libobjc.A.dylib         objc_msgSend + 102
4   TestApp                 ObjCRuntime.Messaging.void_objc_msgSend_IntPtr_IntPtr (intptr,intptr,intptr,intptr)
5   TestApp                 Foundation.NSMutableDictionary.LowlevelSetObject (intptr,intptr)
6   TestApp                 ExceptionMarshaling.Exceptions.ThrowObjectiveCException ()

Frames 0-3 sind systemeigene Frames, und der Stapel entwinder in der Ziel-C-Laufzeit kann diese Frames entladen.Frames 0-3 are native frames, and the stack unwinder in the Objective-C runtime can unwind those frames. Insbesondere führt Sie alle Ziele-C-oder- @catch @finally Klauseln aus.In particular, it will execute any Objective-C @catch or @finally clauses.

Der Entlader des Ziel-C-Stapels ist jedoch nicht in der Lage, die verwalteten Frames (Frames 4-6) ordnungsgemäß zu entladen, da die Frames entladen werden, aber verwaltete Ausnahme Logik nicht ausgeführt wird.However, the Objective-C stack unwinder is not capable of properly unwinding the managed frames (frames 4-6), in that the frames will be unwound, but managed exception logic will not be executed.

Dies bedeutet, dass es in der Regel nicht möglich ist, diese Ausnahmen auf folgende Weise abzufangen:Which means that it's usually not possible to catch these exceptions in the following manner:

try {
    var dict = new NSMutableDictionary ();
    dict.LowLevelSetObject (IntPtr.Zero, IntPtr.Zero);
} catch (Exception ex) {
    Console.WriteLine (ex);
} finally {
    Console.WriteLine ("finally");
}

Dies liegt daran, dass der Entlader des Ziel-C-Stapels die verwaltete Klausel nicht kennt catch und keine der finally Klauseln ausgeführt wird.This is because the Objective-C stack unwinder does not know about the managed catch clause, and neither will the finally clause be executed.

Wenn das obige Codebeispiel effektiv ist , liegt dies daran, dass "Ziel-c" eine Methode für die Benachrichtigung über nicht behandelte Ziel-c-Ausnahmen, NSSetUncaughtExceptionHandler , die von xamarin. IOS und xamarin. Mac verwendet werden, und zu diesem Zeitpunkt versucht, alle Ziel-c-Ausnahmen in verwaltete Ausnahmen zu konvertieren.When the above code sample is effective, it is because Objective-C has a method of being notified of unhandled Objective-C exceptions, NSSetUncaughtExceptionHandler, which Xamarin.iOS and Xamarin.Mac use, and at that point tries to convert any Objective-C exceptions to managed exceptions.

SzenarienScenarios

Szenario 1: Abfangen von Ziel-C-Ausnahmen mit einem verwalteten catch-HandlerScenario 1 - catching Objective-C exceptions with a managed catch handler

Im folgenden Szenario ist es möglich, Ziel-C-Ausnahmen mithilfe von verwalteten catch Handlern abzufangen:In the following scenario, it is possible to catch Objective-C exceptions using managed catch handlers:

  1. Eine "Ziel-C"-Ausnahme wird ausgelöst.An Objective-C exception is thrown.
  2. Die Ziel-C-Laufzeit führt den Stapel durch (aber nicht entladen), sucht nach einem systemeigenen @catch Handler, der die Ausnahme behandeln kann.The Objective-C runtime walks the stack (but does not unwind it), looking for a native @catch handler that can handle the exception.
  3. Die Ziel-C-Laufzeit findet keine Handler @catch , ruft NSGetUncaughtExceptionHandler auf und ruft den Handler auf, der von xamarin. IOS/xamarin. Mac installiert wurde.The Objective-C runtime doesn't find any @catch handlers, calls NSGetUncaughtExceptionHandler, and invokes the handler installed by Xamarin.iOS/Xamarin.Mac.
  4. Der Handler von xamarin. IOS/xamarin. Mac konvertiert die "Ziel-C"-Ausnahme in eine verwaltete Ausnahme und löst Sie aus.Xamarin.iOS/Xamarin.Mac's handler will convert the Objective-C exception into a managed exception, and throw it. Da die Ziel-c-Laufzeit den Stapel nicht entladen hat (nur durchlaufen), ist der aktuelle Frame derselbe, in dem die Ziel-c-Ausnahme ausgelöst wurde.Since the Objective-C runtime didn't unwind the stack (only walked it), the current frame is the same one where the Objective-C exception was thrown.

Ein anderes Problem tritt auf, weil die Mono-Laufzeit nicht weiß, wie Sie die Frame-C-Frames ordnungsgemäß entladen.Another problem occurs here, because the Mono runtime does not know how to unwind Objective-C frames properly.

Wenn xamarin. IOS ' nicht abgefangener Ziel-C-Ausnahme Rückruf aufgerufen wird, sieht der Stapel wie folgt aus:When Xamarin.iOS' uncaught Objective-C exception callback is called, the stack is like this:

 0 libxamarin-debug.dylib   exception_handler(exc=name: "NSInvalidArgumentException" - reason: "*** setObjectForKey: key cannot be nil")
 1 CoreFoundation           __handleUncaughtException + 809
 2 libobjc.A.dylib          _objc_terminate() + 100
 3 libc++abi.dylib          std::__terminate(void (*)()) + 14
 4 libc++abi.dylib          __cxa_throw + 122
 5 libobjc.A.dylib          objc_exception_throw + 337
 6 CoreFoundation           -[__NSDictionaryM setObject:forKey:] + 1015
 7 libxamarin-debug.dylib   xamarin_dyn_objc_msgSend + 102
 8 TestApp                  ObjCRuntime.Messaging.void_objc_msgSend_IntPtr_IntPtr (intptr,intptr,intptr,intptr)
 9 TestApp                  Foundation.NSMutableDictionary.LowlevelSetObject (intptr,intptr) [0x00000]
10 TestApp                  ExceptionMarshaling.Exceptions.ThrowObjectiveCException () [0x00013]

Hier sind die einzigen verwalteten Frames Frames 8-10, aber die verwaltete Ausnahme wird in Frame 0 ausgelöst.Here, the only managed frames are frames 8-10, but the managed exception is thrown in frame 0. Dies bedeutet, dass die Mono-Laufzeit die systemeigenen Frames 0-7 entladen muss. Dies führt zu einem Problem, das dem oben beschriebenen Problem entspricht: Obwohl die Mono-Laufzeit die systemeigenen Frames entladen wird, werden keine Ziele-C- @catch oder- @finally Klauseln ausgeführt.This means that the Mono runtime must unwind the native frames 0-7, which causes a problem equivalent to the problem discussed above: although the Mono runtime will unwind the native frames, it won't execute any Objective-C @catch or @finally clauses.

Codebeispiel:Code example:

-(id) setObject: (id) object forKey: (id) key
{
    @try {
        if (key == nil)
            [NSException raise: @"NSInvalidArgumentException"];
    } @finally {
        NSLog (@"This will not be executed");
    }
}

Und die- @finally Klausel wird nicht ausgeführt, da die Mono-Laufzeit, die diesen Frame entlädt, nicht weiß.And the @finally clause will not be executed because the Mono runtime that unwinds this frame does not know about it.

Eine Variation hierfür besteht darin, eine verwaltete Ausnahme in verwaltetem Code auszulösen und dann durch systemeigene Frames zu entwickeln, um zur ersten verwalteten Klausel zu gelangen catch :A variation of this is to throw a managed exception in managed code, and then unwinding through native frames to get to the first managed catch clause:

class AppDelegate : UIApplicationDelegate {
    public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
    {
        throw new Exception ("An exception");
    }
    static void Main (string [] args)
    {
        try {
            UIApplication.Main (args, null, typeof (AppDelegate));
        } catch (Exception ex) {
            Console.WriteLine ("Managed exception caught.");
        }
    }
}

Die verwaltete UIApplication:Main Methode ruft die native UIApplicationMain -Methode auf, und dann führt IOS eine Menge System eigener Codeausführung aus, bevor Sie die verwaltete- AppDelegate:FinishedLaunching Methode aufruft, wobei beim Auslösen der verwalteten Ausnahme immer noch viele Native Rahmen im Stapel angezeigt werden:The managed UIApplication:Main method will call the native UIApplicationMain method, and then iOS will do a lot of native code execution before eventually calling the managed AppDelegate:FinishedLaunching method, with still a lot of native frames on the stack when the managed exception is thrown:

 0: TestApp                 ExceptionMarshaling.IOS.AppDelegate:FinishedLaunching (UIKit.UIApplication,Foundation.NSDictionary)
 1: TestApp                 (wrapper runtime-invoke) <Module>:runtime_invoke_bool__this___object_object (object,intptr,intptr,intptr) 
 2: libmonosgen-2.0.dylib   mono_jit_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>, error=<unavailable>)
 3: libmonosgen-2.0.dylib   do_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>, error=<unavailable>)
 4: libmonosgen-2.0.dylib   mono_runtime_invoke [inlined] mono_runtime_invoke_checked(method=<unavailable>, obj=<unavailable>, params=<unavailable>, error=0xbff45758)
 5: libmonosgen-2.0.dylib   mono_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>)
 6: libxamarin-debug.dylib  xamarin_invoke_trampoline(type=<unavailable>, self=<unavailable>, sel="application:didFinishLaunchingWithOptions:", iterator=<unavailable>), context=<unavailable>)
 7: libxamarin-debug.dylib  xamarin_arch_trampoline(state=0xbff45ad4)
 8: libxamarin-debug.dylib  xamarin_i386_common_trampoline
 9: UIKit                   -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
10: UIKit                   -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:]
11: UIKit                   -[UIApplication _runWithMainScene:transitionContext:completion:]
12: UIKit                   __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3124
13: UIKit                   -[UIApplication workspaceDidEndTransaction:]
14: FrontBoardServices      __37-[FBSWorkspace clientEndTransaction:]_block_invoke_2
15: FrontBoardServices      __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke
16: FrontBoardServices      __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
17: FrontBoardServices      -[FBSSerialQueue _performNext]
18: FrontBoardServices      -[FBSSerialQueue _performNextFromRunLoopSource]
19: FrontBoardServices      FBSSerialQueueRunLoopSourceHandler
20: CoreFoundation          __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
21: CoreFoundation          __CFRunLoopDoSources0
22: CoreFoundation          __CFRunLoopRun
23: CoreFoundation          CFRunLoopRunSpecific
24: CoreFoundation          CFRunLoopRunInMode
25: UIKit                   -[UIApplication _run]
26: UIKit                   UIApplicationMain
27: TestApp                 (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
28: TestApp                 UIKit.UIApplication:Main (string[],intptr,intptr)
29: TestApp                 UIKit.UIApplication:Main (string[],string,string)
30: TestApp                 ExceptionMarshaling.IOS.Application:Main (string[])

Die Frames 0-1 und 27-30 werden verwaltet, während alle dazwischen liegen.Frames 0-1 and 27-30 are managed, while all those in between are native. Wenn Mono diese Frames durchläuft, werden keine Ziel-C- @catch oder- @finally Klauseln ausgeführt.If Mono unwinds through these frames, no Objective-C @catch or @finally clauses will be executed.

Szenario 2: nicht in der Lage, Ziel-C-Ausnahmen abzufangenScenario 2 - not able to catch Objective-C exceptions

Im folgenden Szenario ist es nicht möglich, Ziel-c-Ausnahmen mithilfe von verwalteten Handlern abzufangen, catch weil die Ausnahme "Ziel-c" auf andere Weise behandelt wurde:In the following scenario, it is not possible to catch Objective-C exceptions using managed catch handlers because the Objective-C exception was handled in another way:

  1. Eine "Ziel-C"-Ausnahme wird ausgelöst.An Objective-C exception is thrown.
  2. Die Ziel-C-Laufzeit führt den Stapel durch (aber nicht entladen), sucht nach einem systemeigenen @catch Handler, der die Ausnahme behandeln kann.The Objective-C runtime walks the stack (but does not unwind it), looking for a native @catch handler that can handle the exception.
  3. Die Ziel-C-Laufzeit findet einen @catch Handler, entlädt den Stapel und beginnt mit der Ausführung des @catch Handlers.The Objective-C runtime finds a @catch handler, unwinds the stack, and starts executing the @catch handler.

Dieses Szenario wird häufig in xamarin. IOS-apps gefunden, da es im Haupt Thread normalerweise Code wie den folgenden gibt:This scenario is commonly found in Xamarin.iOS apps, because on the main thread there is usually code like this:

void UIApplicationMain ()
{
    @try {
        while (true) {
            ExecuteRunLoop ();
        }
    } @catch (NSException *ex) {
        NSLog (@"An unhandled exception occured: %@", exc);
        abort ();
    }
}

Dies bedeutet, dass es im Haupt Thread nie wirklich zu einer unbehandelten Ziel-c-Ausnahme kommt und dass der Rückruf, der die Ziel-c-Ausnahmen in verwaltete Ausnahmen konvertiert, nie aufgerufen wird.This means that on the main thread there's never really an unhandled Objective-C exception, and thus our callback that converts Objective-C exceptions to managed exceptions is never called.

Dies ist auch beim Debuggen von xamarin. Mac-apps auf einer früheren macOS-Version üblich, als xamarin. Mac unterstützt, da die Überprüfung der meisten UI-Objekte im Debugger versucht, Eigenschaften abzurufen, die Selektoren entsprechen, die auf der ausführenden Plattform nicht vorhanden sind (da xamarin. Mac Unterstützung für eine höhere macOS-Version bietetThis is also quite common when debugging Xamarin.Mac apps on an earlier macOS version than Xamarin.Mac supports because inspecting most UI objects in the debugger will try to fetch properties that correspond to selectors that don't exist on the executing platform (because Xamarin.Mac includes support for a higher macOS version). Das aufrufen solcher Selektoren löst eine NSInvalidArgumentException ("unbekannte Auswahl an...") aus, die letztendlich dazu führt, dass der Prozess abstürzt.Calling such selectors will throw an NSInvalidArgumentException ("Unrecognized selector sent to ..."), which eventually causes the process to crash.

Zusammenfassend lässt sich sagen, dass entweder die Ziel-C-Laufzeit oder die Mono-Lauf Zeitrahmen-entladen, die nicht für die Verarbeitung programmiert sind, zu nicht definiertem Verhalten führen können, wie z. b. Abstürze, Speicher Verluste und andere Typen unvorhersehbarer Verhalten (falsch).To summarize, having either the Objective-C runtime or the Mono runtime unwind frames that they are not programmed to handle can lead to undefined behaviors, such as crashes, memory leaks, and other types of unpredictable (mis)behaviors.

## LösungSolution

In xamarin. IOS 10 und xamarin. Mac 2,10 haben wir Unterstützung für das Abfangen von verwalteten und Ziel-C-Ausnahmen für jede verwaltete native Grenze sowie für das umrechnen dieser Ausnahme in den anderen Typ hinzugefügt.In Xamarin.iOS 10 and Xamarin.Mac 2.10, we've added support for catching both managed and Objective-C exceptions on any managed-native boundary, and for converting that exception to the other type.

In Pseudo Code sieht es in etwa wie folgt aus:In pseudo-code, it looks something like this:

[DllImport ("libobjc.dylib")]
static extern void objc_msgSend (IntPtr handle, IntPtr selector);

static void DoSomething (NSObject obj)
{
    objc_msgSend (obj.Handle, Selector.GetHandle ("doSomething"));
}

Der P/Aufruf für objc_msgSend wird abgefangen, und dieser wird stattdessen aufgerufen:The P/Invoke to objc_msgSend is intercepted, and this is called instead:

void
xamarin_dyn_objc_msgSend (id obj, SEL sel)
{
    @try {
        objc_msgSend (obj, sel);
    } @catch (NSException *ex) {
        convert_to_and_throw_managed_exception (ex);
    }
}

Und Ähnliches gilt für den umgekehrten Fall (Mars Hallen von verwalteten Ausnahmen zu Ziel-C-Ausnahmen).And something similar is done for the reverse case (marshaling managed exceptions to Objective-C exceptions).

Das Abfangen von Ausnahmen auf der verwalteten nativen Grenze ist nicht kostenfrei, daher ist es nicht immer standardmäßig aktiviert:Catching exceptions on the managed-native boundary is not cost-free, so it's not always enabled by default:

  • Xamarin. IOS/tvos: das Abfangen von Ziel-C-Ausnahmen ist im Simulator aktiviert.Xamarin.iOS/tvOS: interception of Objective-C exceptions is enabled in the simulator.
  • Xamarin. watchos: die Abfang Funktion wird in allen Fällen erzwungen, da das Ausführen von verwalteten Frames durch die Ziel-C-Laufzeitumgebung das Garbage Collector verwechselt und entweder nicht mehr reagiert.Xamarin.watchOS: interception is enforced in all cases, because letting the Objective-C runtime unwind managed frames will confuse the garbage collector, and either make it hang or crash.
  • Xamarin. Mac: das Abfangen von Ziel-C-Ausnahmen ist für Debugbuilds aktiviert.Xamarin.Mac: interception of Objective-C exceptions is enabled for debug builds.

Im Abschnitt " buildzeitflags " wird erläutert, wie die Abfang Funktion aktiviert wird, wenn Sie nicht standardmäßig aktiviert ist.The Build-time flags section explains how to enable interception when it's not enabled by default.

EreignisseEvents

Wenn eine Ausnahme abgefangen wird, werden zwei neue Ereignisse ausgelöst: Runtime.MarshalManagedException und Runtime.MarshalObjectiveCException .There are two new events that are raised once an exception is intercepted: Runtime.MarshalManagedException and Runtime.MarshalObjectiveCException.

An beide Ereignisse wird ein EventArgs Objekt mit der ursprünglichen Ausnahme, die ausgelöst wurde (die Exception -Eigenschaft), und einer- ExceptionMode Eigenschaft, mit der definiert wird, wie die Ausnahme gemarshallt werden soll, übermittelt.Both events are passed an EventArgs object that contains the original exception that was thrown (the Exception property), and an ExceptionMode property to define how the exception should be marshaled.

Die- ExceptionMode Eigenschaft kann im-Ereignishandler geändert werden, um das Verhalten gemäß einer benutzerdefinierten Verarbeitung im-Handler zu ändern.The ExceptionMode property can be changed in the event handler to change the behavior according to any custom processing done in the handler. Ein Beispiel wäre, den Prozess abzubrechen, wenn eine bestimmte Ausnahme auftritt.One example would be to abort the process if a certain exception occurs.

Das Ändern der ExceptionMode Eigenschaft gilt für das einzelne Ereignis und wirkt sich nicht auf Ausnahmen aus, die in der Zukunft abgefangen werden.Changing the ExceptionMode property applies to the single event, it does not affect any exceptions intercepted in the future.

Die folgenden Modi sind verfügbar:The following modes are available:

  • Default: Der Standardwert variiert je nach Plattform.Default: The default varies by platform. Dies ist ThrowObjectiveCException der Fall, wenn sich die GC im kooperativen Modus (watchos) befindet, und UnwindNativeCode andernfalls (IOS/watchos/macOS).It is ThrowObjectiveCException if the GC is in cooperative mode (watchOS), and UnwindNativeCode otherwise (iOS / watchOS / macOS). Der Standardwert kann sich in Zukunft ändern.The default may change in the future.
  • UnwindNativeCode: Dies ist das vorherige (undefinierte) Verhalten.UnwindNativeCode: This is the previous (undefined) behavior. Diese Option ist nicht verfügbar, wenn die GC im kooperativen Modus verwendet wird (Dies ist die einzige Option für watchos; daher ist dies keine gültige Option für watchos), aber Sie ist die Standardoption für alle anderen Plattformen.This is not available when using the GC in cooperative mode (which is the only option on watchOS; thus, this is not a valid option on watchOS), but it's the default option for all other platforms.
  • ThrowObjectiveCException: Konvertieren der verwalteten Ausnahme in eine "Ziel-c"-Ausnahme und Auslösen der "Ziel-c"-Ausnahme.ThrowObjectiveCException: Convert the managed exception into an Objective-C exception and throw the Objective-C exception. Dies ist die Standardeinstellung für watchos.This is the default on watchOS.
  • Abort: Abbrechen Sie den Prozess.Abort: Abort the process.
  • Disable: Deaktiviert das Abfangen von Ausnahmen, daher ist es nicht sinnvoll, diesen Wert im Ereignishandler festzulegen, aber sobald das Ereignis ausgelöst wird, ist es zu spät, es zu deaktivieren.Disable: Disables the exception interception, so it doesn't make sense to set this value in the event handler, but once the event is raised it's too late to disable it. Falls festgelegt, verhält es sich wie UnwindNativeCode .In any case, if set, it will behave as UnwindNativeCode.

Für das Mars Hallen von Ziel-C-Ausnahmen zu verwaltetem Code sind die folgenden Modi verfügbar:For marshaling Objective-C exceptions to managed code, the following modes are available:

  • Default: Der Standardwert variiert je nach Plattform.Default: The default varies by platform. Dies ist ThrowManagedException der Fall, wenn sich die GC im kooperativen Modus (watchos) befindet, und UnwindManagedCode andernfalls (IOS/tvos/macOS).It is ThrowManagedException if the GC is in cooperative mode (watchOS), and UnwindManagedCode otherwise (iOS / tvOS / macOS). Der Standardwert kann sich in Zukunft ändern.The default may change in the future.
  • UnwindManagedCode: Dies ist das vorherige (undefinierte) Verhalten.UnwindManagedCode: This is the previous (undefined) behavior. Diese Option ist nicht verfügbar, wenn die GC im kooperativen Modus verwendet wird (Dies ist der einzige gültige GC-Modus auf watchos; Dies ist daher keine gültige Option für watchos), aber dies ist die Standardeinstellung für alle anderen Plattformen.This is not available when using the GC in cooperative mode (which is the only valid GC mode on watchOS; thus this is not a valid option on watchOS), but it's the default for all other platforms.
  • ThrowManagedException: Konvertiert die "Ziel-C"-Ausnahme in eine verwaltete Ausnahme und löst die verwaltete Ausnahme aus.ThrowManagedException: Convert the Objective-C exception to a managed exception and throw the managed exception. Dies ist die Standardeinstellung für watchos.This is the default on watchOS.
  • Abort: Abbrechen Sie den Prozess.Abort: Abort the process.
  • Disable:D isables das Abfangen von Ausnahmen, ist es nicht sinnvoll, diesen Wert im Ereignishandler festzulegen, aber sobald das Ereignis ausgelöst wird, ist es zu spät, es zu deaktivieren.Disable:Disables the exception interception, so it doesn't make sense to set this value in the event handler, but once the event is raised, it's too late to disable it. Falls festgelegt, wird der Prozess abgebrochen.In any case if set, it will abort the process.

Um zu sehen, wann eine Ausnahme gemarshallt wird, können Sie dies tun:So, to see every time an exception is marshaled, you can do this:

Runtime.MarshalManagedException += (object sender, MarshalManagedExceptionEventArgs args) =>
{
    Console.WriteLine ("Marshaling managed exception");
    Console.WriteLine ("    Exception: {0}", args.Exception);
    Console.WriteLine ("    Mode: {0}", args.ExceptionMode);
    
};
Runtime.MarshalObjectiveCException += (object sender, MarshalObjectiveCExceptionEventArgs args) =>
{
    Console.WriteLine ("Marshaling Objective-C exception");
    Console.WriteLine ("    Exception: {0}", args.Exception);
    Console.WriteLine ("    Mode: {0}", args.ExceptionMode);
};

BuildzeitflagsBuild-Time Flags

Es ist möglich, die folgenden Optionen an mberührungs (für xamarin. IOS-Apps) und MMP (bei xamarin. Mac-Apps) zu übergeben, die bestimmen, ob die Ausnahme Abfang Funktion aktiviert ist, und die Standardaktion festlegen, die auftreten soll:It's possible to pass the following options to mtouch (for Xamarin.iOS apps) and mmp (for Xamarin.Mac apps), which will determine if exception interception is enabled, and set the default action that should occur:

  • --marshal-managed-exceptions=

    • default
    • unwindnativecode
    • throwobjectivecexception
    • abort
    • disable
  • --marshal-objectivec-exceptions=

    • default
    • unwindmanagedcode
    • throwmanagedexception
    • abort
    • disable

Mit Ausnahme von disable sind diese Werte mit den Werten identisch, die ExceptionMode an das MarshalManagedException -Ereignis und das-Ereignis übermittelt werden MarshalObjectiveCException .Except for disable, these values are identical to the ExceptionMode values that are passed to the MarshalManagedException and MarshalObjectiveCException events.

disableMit der Option wird die Abfang Funktion größtenteils deaktiviert, außer wenn kein Ausführungs Aufwand hinzugefügt wird.The disable option will mostly disable interception, except we'll still intercept exceptions when it does not add any execution overhead. Die marshallingereignisse werden für diese Ausnahmen weiterhin ausgelöst, wobei der Standardmodus für die ausführende Plattform der Standardmodus ist.The marshaling events are still raised for these exceptions, with the default mode being the default mode for the executing platform.

EinschränkungenLimitations

objc_msgSendBei dem Versuch, Ziel-C-Ausnahmen abzufangen, werden nur P/Aufrufe an die-Funktions Familie abgefangen.We only intercept P/Invokes to the objc_msgSend family of functions when trying to catch Objective-C exceptions. Dies bedeutet, dass ein P/Aufrufen einer anderen C-Funktion, die dann alle Ziel-c-Ausnahmen auslöst, weiterhin das alte und nicht definierte Verhalten findet (Dies wird in Zukunft möglicherweise verbessert).This means that a P/Invoke to another C function, which then throws any Objective-C exceptions, will still run into the old and undefined behavior (this may be improved in the future).