2007-03-06 Mike Kestner <mkestner@novell.com>

* generator/Signal.cs : add try/catch blocks to native callback
	delegates so that exceptions are not propagated across the native
	boundary.  Now raises GLib.ExceptionManager.UnhandledException.
	* glib/ExceptionManager.cs : new class with UnhandledException
	event and a static method to raise it.
	* glib/Signal.cs : wrap the generic EventHandler callback delegate
	with try/catch blocks and raise the UnhandledException event.

svn path=/trunk/gtk-sharp/; revision=73840
This commit is contained in:
Mike Kestner 2007-03-06 20:10:15 +00:00
parent 6a00328dd1
commit 32de2832db
9 changed files with 251 additions and 30 deletions

View File

@ -1,3 +1,13 @@
2007-03-06 Mike Kestner <mkestner@novell.com>
* generator/Signal.cs : add try/catch blocks to native callback
delegates so that exceptions are not propagated across the native
boundary. Now raises GLib.ExceptionManager.UnhandledException.
* glib/ExceptionManager.cs : new class with UnhandledException
event and a static method to raise it.
* glib/Signal.cs : wrap the generic EventHandler callback delegate
with try/catch blocks and raise the UnhandledException event.
2007-03-05 Mike Kestner <mkestner@novell.com>
* gtk/Application.custom : set prgname in Init methods so

View File

@ -17,7 +17,7 @@ ASSEMBLIES = \
UPDATE_ASSEMBLIES = $(addprefix -assembly:lib/, $(ASSEMBLIES))
UPDATER = $(MONODOCER) -path:en $(UPDATE_ASSEMBLIES)
UPDATER = $(MONODOCER) -path:en -pretty $(UPDATE_ASSEMBLIES)
if ENABLE_MONODOC
SOURCESDIR=$(prefix)/lib/monodoc/sources
@ -36,11 +36,14 @@ gtk-sharp-docs.zip gtk-sharp-docs.tree: $(srcdir)/en/*/*.xml $(srcdir)/en/*.xml
$(MDASSEMBLER) --ecma $(srcdir)/en -o gtk-sharp-docs
get-assemblies:
echo "assumes gnome-sharp and gtk-sharp checkouts in same parent"
mkdir -p lib
cp $(top_builddir)/*/*.dll lib
cp $(top_builddir)/*/*.dll.config lib
cp $(top_builddir)/gconf/*/*.dll lib
cp $(top_builddir)/gconf/*/*.dll.config lib
cp $(top_builddir)/../gnome-sharp/*/*.dll lib
cp $(top_builddir)/../gnome-sharp/*/*.dll.config lib
cp $(top_builddir)/../gnome-sharp/gconf/*/*.dll lib
cp $(top_builddir)/../gnome-sharp/gconf/*/*.dll.config lib
update: get-assemblies
$(UPDATER)

View File

@ -0,0 +1,46 @@
<Type Name="ExceptionManager" FullName="GLib.ExceptionManager">
<TypeSignature Language="C#" Value="public class ExceptionManager" />
<AssemblyInfo>
<AssemblyName>glib-sharp</AssemblyName>
<AssemblyVersion>2.10.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Object</BaseTypeName>
</Base>
<Interfaces />
<Members>
<Member MemberName="RaiseUnhandledException">
<MemberSignature Language="C#" Value="public static void RaiseUnhandledException (Exception e, bool is_terminal);" />
<MemberType>Method</MemberType>
<ReturnValue>
<ReturnType>System.Void</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="e" Type="System.Exception" />
<Parameter Name="is_terminal" Type="System.Boolean" />
</Parameters>
<Docs>
<param name="e">Exception.</param>
<param name="is_terminal">If <see langword="true" />, the exception terminates the application.</param>
<summary>Raise Unhandled Exception method.</summary>
<remarks>This method is generally only useful to language bindings. If <paramref name="is_terminal" /> is set, or a user event handler requests application exit, this method does not return.</remarks>
</Docs>
</Member>
<Member MemberName="UnhandledException">
<MemberSignature Language="C#" Value="public static event GLib.UnhandledExceptionHandler UnhandledException;" />
<MemberType>Event</MemberType>
<ReturnValue>
<ReturnType>GLib.UnhandledExceptionHandler</ReturnType>
</ReturnValue>
<Docs>
<summary>UnhandledException event.</summary>
<remarks>Attach a <see cref="T:GLib.UnhandledExceptionHandler" /> delegate to this event to receive notification of Exceptions throw within managed callback delegates. If the <see cref="T:GLib.UnhandledExceptionArgs" /> contain information regarding whether the Exception is terminal and can be used to request termination of the application via the <see cref="M:GLib.UnhandledExceptionArgs.ExitApplication" /> property.</remarks>
</Docs>
</Member>
</Members>
<Docs>
<summary>Exception management class.</summary>
<remarks />
<since version="Gtk# 2.10" />
</Docs>
</Type>

View File

@ -0,0 +1,44 @@
<Type Name="UnhandledExceptionArgs" FullName="GLib.UnhandledExceptionArgs">
<TypeSignature Language="C#" Value="public class UnhandledExceptionArgs : UnhandledExceptionEventArgs" />
<AssemblyInfo>
<AssemblyName>glib-sharp</AssemblyName>
<AssemblyVersion>2.10.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.UnhandledExceptionEventArgs</BaseTypeName>
</Base>
<Interfaces />
<Members>
<Member MemberName=".ctor">
<MemberSignature Language="C#" Value="public UnhandledExceptionArgs (Exception e, bool is_terminal);" />
<MemberType>Constructor</MemberType>
<Parameters>
<Parameter Name="e" Type="System.Exception" />
<Parameter Name="is_terminal" Type="System.Boolean" />
</Parameters>
<Docs>
<param name="e">Exception.</param>
<param name="is_terminal">If <see langword="true" />, the application is terminating.</param>
<summary>Public constructor.</summary>
<remarks />
</Docs>
</Member>
<Member MemberName="ExitApplication">
<MemberSignature Language="C#" Value="public bool ExitApplication { set; get; };" />
<MemberType>Property</MemberType>
<ReturnValue>
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Docs>
<summary>ExitApplication property.</summary>
<value>If <see langword="true" />, the application will exit.</value>
<remarks>Indicates if an application wants to exit after event propagation is complete.</remarks>
</Docs>
</Member>
</Members>
<Docs>
<summary>UnhandledExceptionArgs event arguments.</summary>
<remarks>Event arguments for <see cref="T:GLib.UnhandledExceptionHandler" /> events.</remarks>
<since version="Gtk# 2.10" />
</Docs>
</Type>

View File

@ -0,0 +1,22 @@
<Type Name="UnhandledExceptionHandler" FullName="GLib.UnhandledExceptionHandler">
<TypeSignature Language="C#" Value="public delegate void UnhandledExceptionHandler(UnhandledExceptionArgs args);" />
<AssemblyInfo>
<AssemblyName>glib-sharp</AssemblyName>
<AssemblyVersion>2.10.0.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Delegate</BaseTypeName>
</Base>
<Parameters>
<Parameter Name="args" Type="GLib.UnhandledExceptionArgs" />
</Parameters>
<ReturnValue>
<ReturnType>System.Void</ReturnType>
</ReturnValue>
<Docs>
<param name="args">Event arguments.</param>
<summary>Reports unhandled exceptions.</summary>
<remarks>Attach to <see cref="M:GLib.EventManager.UnhandledException" /> event to receive notification of exceptions in managed callback delegates.</remarks>
<since version="Gtk# 2.10" />
</Docs>
</Type>

View File

@ -199,42 +199,58 @@ namespace GtkSharp.Generation {
sw.WriteLine ();
sw.WriteLine ("\t\tstatic " + retval.ToNativeType + " " + CallbackName + " (" + CallbackSig + ")");
sw.WriteLine("\t\t{");
sw.WriteLine("\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;");
sw.WriteLine("\t\t\tif (sig == null)");
sw.WriteLine("\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);");
sw.WriteLine();
sw.WriteLine("\t\t\t{0} args = new {0} ();", EventArgsQualifiedName);
sw.WriteLine("\t\t\ttry {");
sw.WriteLine("\t\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;");
sw.WriteLine("\t\t\t\tif (sig == null)");
sw.WriteLine("\t\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);");
sw.WriteLine();
if (parms.Count > 1)
sw.WriteLine("\t\t\targs.Args = new object[" + (parms.Count - 1) + "];");
sw.WriteLine("\t\t\t\targs.Args = new object[" + (parms.Count - 1) + "];");
string finish = "";
for (int idx = 1; idx < parms.Count; idx++) {
Parameter p = parms [idx];
IGeneratable igen = p.Generatable;
if (p.PassAs != "out") {
if (igen is ManualGen) {
sw.WriteLine("\t\t\tif (arg{0} == IntPtr.Zero)", idx);
sw.WriteLine("\t\t\t\targs.Args[{0}] = null;", idx - 1);
sw.WriteLine("\t\t\telse {");
sw.WriteLine("\t\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";");
sw.WriteLine("\t\t\t}");
sw.WriteLine("\t\t\t\tif (arg{0} == IntPtr.Zero)", idx);
sw.WriteLine("\t\t\t\t\targs.Args[{0}] = null;", idx - 1);
sw.WriteLine("\t\t\t\telse {");
sw.WriteLine("\t\t\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";");
sw.WriteLine("\t\t\t\t}");
} else
sw.WriteLine("\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";");
sw.WriteLine("\t\t\t\targs.Args[" + (idx - 1) + "] = " + p.FromNative ("arg" + idx) + ";");
}
if (p.PassAs != "")
finish += "\t\t\targ" + idx + " = " + igen.ToNativeReturn ("((" + p.CSType + ")args.Args[" + (idx - 1) + "])") + ";\n";
finish += "\t\t\t\targ" + idx + " = " + igen.ToNativeReturn ("((" + p.CSType + ")args.Args[" + (idx - 1) + "])") + ";\n";
}
sw.WriteLine("\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName);
sw.WriteLine("\t\t\thandler (GLib.Object.GetObject (arg0), args);");
sw.WriteLine (finish);
if (!IsVoid) {
sw.WriteLine ("\t\t\tif (args.RetVal == null)");
if (retval.CSType == "bool")
sw.WriteLine ("\t\t\t\treturn false;");
else
sw.WriteLine ("\t\t\t\tthrow new Exception(\"args.RetVal unset in callback\");");
sw.WriteLine("\t\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName);
sw.WriteLine("\t\t\t\thandler (GLib.Object.GetObject (arg0), args);");
sw.WriteLine("\t\t\t} catch (Exception e) {");
sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, false);");
sw.WriteLine("\t\t\t}");
sw.WriteLine("\t\t\treturn " + table.ToNativeReturn (retval.CType, "((" + retval.CSType + ")args.RetVal)") + ";");
if (IsVoid && finish.Length == 0) {
sw.WriteLine("\t\t}\n");
return;
}
sw.WriteLine("\n\t\t\ttry {");
sw.Write (finish);
if (!IsVoid) {
if (retval.CSType == "bool") {
sw.WriteLine ("\t\t\t\tif (args.RetVal == null)");
sw.WriteLine ("\t\t\t\t\treturn false;");
}
sw.WriteLine("\t\t\t\treturn " + table.ToNativeReturn (retval.CType, "((" + retval.CSType + ")args.RetVal)") + ";");
}
sw.WriteLine("\t\t\t} catch (Exception) {");
sw.WriteLine ("\t\t\t\tException ex = new Exception (\"args.RetVal or 'out' property unset or set to incorrect type in " + EventHandlerQualifiedName + " callback\");");
sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (ex, true);");
sw.WriteLine ("\t\t\t\t// NOTREACHED: above call doesn't return.");
sw.WriteLine ("\t\t\t\tthrow ex;");
sw.WriteLine("\t\t\t}");
sw.WriteLine("\t\t}");
sw.WriteLine();
}

73
glib/ExceptionManager.cs Normal file
View File

@ -0,0 +1,73 @@
// GLib.Application.cs - static Application class
//
// Authors: Mike Kestner <mkestner@novell.com>
//
// Copyright (c) 2007 Novell, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General
// Public License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
namespace GLib {
using System;
public delegate void UnhandledExceptionHandler (UnhandledExceptionArgs args);
public class UnhandledExceptionArgs : System.UnhandledExceptionEventArgs {
bool exit_app = false;
public UnhandledExceptionArgs (Exception e, bool is_terminal) : base (e, is_terminal) {}
public bool ExitApplication {
get {
return exit_app;
}
set {
if (value)
exit_app = value;
}
}
}
public class ExceptionManager {
private ExceptionManager () {}
public static event UnhandledExceptionHandler UnhandledException;
public static void RaiseUnhandledException (Exception e, bool is_terminal)
{
if (UnhandledException == null) {
Console.Error.WriteLine ("Exception in Gtk# callback delegate");
Console.Error.WriteLine (" Note: Applications can use GLib.ExceptionManager.UnhandledException to handle the exception.");
Console.Error.WriteLine (e);
Environment.Exit (1);
}
UnhandledExceptionArgs args = new UnhandledExceptionArgs (e, is_terminal);
try {
UnhandledException (args);
} catch (Exception ex) {
Console.Error.WriteLine (ex);
Environment.Exit (1);
}
if (is_terminal || args.ExitApplication)
Environment.Exit (1);
}
}
}

View File

@ -25,6 +25,7 @@ sources = \
DelegateWrapper.cs \
DestroyNotify.cs \
EnumWrapper.cs \
ExceptionManager.cs \
FileUtils.cs \
GException.cs \
GString.cs \

View File

@ -173,12 +173,18 @@ namespace GLib {
static void voidObjectCallback (IntPtr handle, IntPtr gch)
{
Signal sig = ((GCHandle) gch).Target as Signal;
if (sig == null)
throw new Exception ("Unknown signal class GC handle received.");
try {
Signal sig = ((GCHandle) gch).Target as Signal;
if (sig == null) {
ExceptionManager.RaiseUnhandledException (new Exception ("Unknown signal class GC handle received."), false);
return;
}
EventHandler handler = (EventHandler) sig.Handler;
handler (Object.GetObject (handle), EventArgs.Empty);
EventHandler handler = (EventHandler) sig.Handler;
handler (Object.GetObject (handle), EventArgs.Empty);
} catch (Exception e) {
ExceptionManager.RaiseUnhandledException (e, false);
}
}
static voidObjectDelegate event_handler_delegate;