private static NSObject DoCreateNativeException(Exception e) { NSObject native; IntPtr nameBuffer = Marshal.StringToHGlobalAuto(e.GetType().ToString()); IntPtr reasonBuffer = Marshal.StringToHGlobalAuto(e.Message); IntPtr keyBuffer = Marshal.StringToHGlobalAuto(".NET exception"); GCHandle handle = new GCHandle(); try { // Create the name, reason, and userInfo objects. NSObject name = (NSObject) new Class("NSString").Call("alloc").Call("initWithUTF8String:", nameBuffer); NSObject reason = (NSObject) new Class("NSString").Call("alloc").Call("initWithUTF8String:", reasonBuffer); NSObject userInfo = (NSObject) new Class("NSMutableDictionary").Call("alloc").Call("init"); name.autorelease(); reason.autorelease(); userInfo.autorelease(); // Add the original System.Exception to userInfo. NSObject key = (NSObject) new Class("NSString").Call("alloc").Call("initWithUTF8String:", keyBuffer); key.autorelease(); try { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, e); byte[] data = stream.ToArray(); handle = GCHandle.Alloc(data, GCHandleType.Pinned); NSObject buffer = (NSObject) new Class("NSData").Call("dataWithBytes:length:", handle.AddrOfPinnedObject(), (uint)stream.Length); Unused.Value = userInfo.Call("setObject:forKey:", buffer, key); } } catch (Exception ee) { // Typically this will happen if e is not serializable. Console.Error.WriteLine("{0}", ee); Console.Error.Flush(); } // Create the NSException. native = (NSObject) new Class("NSException").Call("exceptionWithName:reason:userInfo:", name, reason, userInfo); } finally { Marshal.FreeHGlobal(nameBuffer); Marshal.FreeHGlobal(reasonBuffer); Marshal.FreeHGlobal(keyBuffer); if (handle.IsAllocated) { handle.Free(); } } return(native); }
/// <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); }