/// <summary>Converts an <c>NSException</c> into a managed exception and throws it.</summary> /// <param name = "instance">The <a href ="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsexception_Class/Reference/Reference.html">NSException</a> /// pointer.</param> /// <remarks>If the <c>NSException</c> wraps a managed exception then /// <c>TargetInvocationException</c> is thrown instead with the inner exception /// set to the original exception.</remarks> public static void Raise(IntPtr instance) { NSObject obj = new NSObject(instance); if (obj.isMemberOfClass(new Class("NSException"))) { // See if the userInfo contains a .NET exception. NSObject userInfo = (NSObject)obj.Call("userInfo"); if (userInfo != null && (IntPtr)userInfo != IntPtr.Zero) { IntPtr keyBuffer = Marshal.StringToHGlobalAuto(".NET exception"); NSObject key = (NSObject) new Class("NSString").Call("alloc").Call("initWithUTF8String:", keyBuffer); key.autorelease(); Marshal.FreeHGlobal(keyBuffer); NSObject data = (NSObject)userInfo.Call("objectForKey:", key); if (data != null && !data.IsNil()) { // If it does then get the serialized exception bytes, IntPtr ptr = (IntPtr)data.Call("bytes"); uint bytes = (uint)data.Call("length"); // copy them into a managed buffer, byte[] buffer = new byte[bytes]; Marshal.Copy(ptr, buffer, 0, (int)bytes); // and raise the original exception. using (MemoryStream stream = new MemoryStream(buffer)) { BinaryFormatter formatter = new BinaryFormatter(); Exception e = (Exception)formatter.Deserialize(stream); throw new TargetInvocationException("Exception has been thrown by the (managed) target of an Objective-C method call.", e); // yes TargetInvocationException sucks, but it preserves the original stack crawl... } } } } throw new CocoaException(obj); }