.NET: ¿Cuál es la mejor manera de implementar un controlador de excepciones “catch all”?

Me pregunto cuál es la mejor manera de tener un “si todo lo demás falla, tómalo”.

Quiero decir, manejas la mayor cantidad posible de excepciones en tu aplicación, pero aún así existen errores, así que necesito tener algo que atrape todas las excepciones no manejadas para que pueda recostackr información y almacenarla en una base de datos o enviarlas. a un servicio web.

¿El evento AppDomain.CurrentDomain.UnhandledException captura todo? Incluso si la aplicación es multiproceso?

Nota al margen: Windows Vista expone las funciones API nativas que permiten que cualquier aplicación se recupere luego de un locking … ahora no puedo pensar en el nombre … pero prefiero no usarlo, ya que muchos de nuestros usuarios todavía están usando Windows XP.

Acabo de jugar con el comportamiento UnhandledException de AppDomain, (esta es la última etapa en la que se registra la excepción no controlada)

Sí, después de procesar los manejadores de eventos se terminará su aplicación y se muestra el desagradable “… progtwig parado de diálogo de trabajo”.

🙂 Todavía puedes evitar eso.

Revisa:

class Program { void Run() { AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Console.WriteLine("Press enter to exit."); do { (new Thread(delegate() { throw new ArgumentException("ha-ha"); })).Start(); } while (Console.ReadLine().Trim().ToLowerInvariant() == "x"); Console.WriteLine("last good-bye"); } int r = 0; void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Interlocked.Increment(ref r); Console.WriteLine("handled. {0}", r); Console.WriteLine("Terminating " + e.IsTerminating.ToString()); Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Name = "Dead thread"; while (true) Thread.Sleep(TimeSpan.FromHours(1)); //Process.GetCurrentProcess().Kill(); } static void Main(string[] args) { Console.WriteLine("..."); (new Program()).Run(); } } 

PS Do maneja el no manejado para Application.ThreadException (WinForms) o DispatcherUnhandledException (WPF) en el nivel superior.

En ASP.NET, utiliza la función Application_Error en el archivo Global.asax .

En WinForms, utiliza MyApplication_UnhandledException en el archivo ApplicationEvents

Ambas funciones se invocan si se produce una excepción no controlada en su código. Puede registrar la excepción y presentar un mensaje agradable al usuario desde estas funciones.

Para las aplicaciones Winform, además de AppDomain.CurrentDomain.UnhandledException también utilizo Application.ThreadException y Application.SetUnhandledExceptionMode (w / UnhandledExceptionMode.CatchException). Esta combinación parece atrapar todo.

En el hilo principal, tiene las siguientes opciones:

  • Aplicación de consola o servicio: AppDomain.CurrentDomain.UnhandledException
  • Aplicación WinForms: Application.ThreadException
  • Aplicación web: Application_Error de Global.asax

Para otros hilos:

  • Los hilos secundarios no tienen excepciones no manejadas; usa SafeThread
  • Subprocesos de trabajo: (timer, threadpool) ¡no hay ninguna red de seguridad!

Tenga en cuenta que estos eventos no manejan excepciones, simplemente los informan a la aplicación, a menudo cuando ya es demasiado tarde para hacer algo útil / sensato sobre ellos.

Las excepciones de registro son buenas, pero el monitoreo de las aplicaciones es mejor 😉

Advertencia: soy el autor del artículo SafeThread .

Para WinForms, no te olvides de adjuntar también al evento de excepción no controlada del Thread actual (especialmente si estás usando multi threading).

Algunos enlaces sobre mejores prácticas aquí y aquí y aquí (probablemente el mejor artículo sobre manejo de excepciones para .net)

También hay algo genial llamado ELMAH que registrará cualquier error de ASP.NET que ocurra en una aplicación web. Sé que estás preguntando acerca de una solución de aplicación de Winform, pero sentí que esto podría ser beneficioso para cualquiera que necesite este tipo de cosas en una aplicación web. Lo utilizamos donde trabajo y me ha ayudado mucho en la depuración (¡especialmente en los servidores de producción!)

Aquí hay algunas características que tiene (sacó de la página):

  • Registro de casi todas las excepciones no controladas.
  • Una página web para ver de forma remota todo el registro de excepciones recodificadas.
  • Una página web para ver de forma remota todos los detalles de cualquier excepción registrada.
  • En muchos casos, puede revisar la pantalla amarilla original de la muerte que ASP.NET generó para una excepción determinada, incluso con el modo customErrors desactivado.
  • Una notificación por correo electrónico de cada error en el momento en que ocurre.
  • Una fuente RSS de los últimos 15 errores del registro.
  • Varias implementaciones de almacenamiento de respaldo para el registro, incluidas en memoria, Microsoft SQL Server y varias contribuidas por la comunidad.

Puede controlar la mayoría de las excepciones en ese controlador incluso en aplicaciones multiproceso, pero .NET (comenzando con 2.0) no le permitirá cancelar excepciones no controladas a menos que active el modo de compatibilidad 1.1. Cuando eso suceda, el AppDomain se cerrará sin importar qué. Lo mejor que puede hacer es iniciar la aplicación en un dominio de aplicación diferente para que pueda manejar esta excepción y crear un nuevo dominio de aplicación para reiniciar la aplicación.

Estoy usando el siguiente enfoque, que funciona y reduce en gran medida la cantidad de código (sin embargo, no estoy seguro de si hay una mejor manera o cuáles podrían ser las dificultades. Siempre que llame: le pregunto a los quys que darían menos sería cortés suficiente para aclarar sus acciones;)

 try { CallTheCodeThatMightThrowException() } catch (Exception ex) { System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace (); Utils.ErrorHandler.Trap ( ref objUser, st, ex ); } //eof catch 

Y aquí está el código ErrorHandler: solo para dejar en claro-: objUser – es el objeto que modela los usuarios (puede obtener información como nombre de dominio, departamento, región, etc. para fines de registro ILog logger – es el objeto de registro – por ejemplo, el realizando las actividades de registro StackTrace st – el objeto StackTrace que le proporciona información de depuración para su aplicación

 using System; using log4net; //or another logging platform namespace GenApp.Utils { public class ErrorHandler { public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex ) { if (ex is NullReferenceException) { //do stuff for this ex type } //eof if if (ex is System.InvalidOperationException) { //do stuff for this ex type } //eof if if (ex is System.IndexOutOfRangeException) { //do stuff for this ex type } //eof if if (ex is System.Data.SqlClient.SqlException) { //do stuff for this ex type } //eof if if (ex is System.FormatException) { //do stuff for this ex type } //eof if if (ex is Exception) { //do stuff for this ex type } //eof catch } //eof method }//eof class } //eof namesp 

En una aplicación GUI administrada, de forma predeterminada, las excepciones que se originan en el hilo de la GUI son manejadas por lo que está asignado a la Application.ThreadException.

Excepciones que se originan en los otros subprocesos son manejadas por AppDomain.CurrentDomain.UnhandledException.

Si desea que las excepciones de subprocesos de su GUI funcionen igual que las suyas (sin GUI), para que sean manejadas por AppDomain.CurrentDomain.UnhandledException, puede hacer esto:

 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); 

Una ventaja de capturar las excepciones de hilos de la GUI mediante ThreadException es que puede dar al usuario la opción de dejar que la aplicación continúe. Para asegurarse de que ningún archivo de configuración anule el comportamiento predeterminado, puede llamar a:

 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

Todavía eres vulnerable a las excepciones de dlls nativos con mal comportamiento. Si un dll nativo instala su propio controlador utilizando Win32 SetUnhandledExceptionFilter, se supone que guarda el puntero al filtro anterior y lo llama también. Si no hace eso, su manejador no será llamado.