todas - que tipos de excepciones existen en c#
¿Atrapa globalmente las excepciones en una aplicación WPF? (6)
Además de lo que otros mencionaron aquí, tenga en cuenta que la combinación de Application.DispatcherUnhandledException
(y sus similares ) con
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
en el app.config
evitará que la excepción de subprocesos secundarios cierre la aplicación.
Estamos teniendo una aplicación WPF donde partes de ella pueden generar excepciones en tiempo de ejecución. Me gustaría capturar globalmente cualquier excepción no manejada y registrarlas, pero de lo contrario, continuar la ejecución del programa como si no hubiera pasado nada (como VB''s On Error Resume Next
).
¿Es esto posible en C #? Y si es así, ¿dónde exactamente tendría que poner el código de manejo de excepciones?
Actualmente no puedo ver ningún punto en el que pueda envolver un try
/ catch
y que pueda detectar todas las excepciones que puedan ocurrir. E incluso entonces me habría dejado lo que fuera ejecutado por la captura. ¿O estoy pensando en direcciones horriblemente equivocadas aquí?
ETA: Debido a que muchas personas a continuación lo señalaron: la aplicación no es para controlar plantas de energía nuclear. Si falla, no es un gran problema, pero las excepciones aleatorias que están relacionadas principalmente con la IU son una molestia en el contexto en el que se utilizaría. Hubo (y probablemente todavía hay) algunos de ellos y, como utiliza una arquitectura de complementos y puede ser extendido por otros (también estudiantes en ese caso; por lo tanto, no hay desarrolladores con experiencia que puedan escribir código completamente libre de errores).
En cuanto a las excepciones que quedan atrapadas: las registro en un archivo de registro, incluido el seguimiento completo de la pila. Ese fue el punto central de ese ejercicio. Solo para contrarrestar a esas personas que estaban tomando mi analogía con el ORD de VB demasiado literalmente.
Sé que ignorar ciegamente ciertas clases de errores es peligroso y podría dañar mi instancia de aplicación. Como se dijo antes, este programa no es de misión crítica para nadie. Nadie en su sano juicio apostaría a la supervivencia de la civilización humana en ello. Es simplemente una pequeña herramienta para probar ciertos enfoques de diseño. Ingeniería de software.
Para el uso inmediato de la aplicación, no hay muchas cosas que puedan suceder en una excepción:
- Sin excepción, manejo - diálogo de error y salida de la aplicación. El experimento debe repetirse, aunque es probable que con otro tema. No se han registrado errores, lo que es desafortunado.
- Manejo genérico de excepciones: error benigno atrapado, sin daño. Este debe ser el caso común juzgado por todos los errores que estuvimos viendo durante el desarrollo. Ignorar este tipo de errores no debería tener consecuencias inmediatas; las estructuras de datos centrales se prueban lo suficientemente bien como para que sobrevivan fácilmente a esto.
- Manejo genérico de excepciones: error grave atrapado, posiblemente se bloquee en un momento posterior. Esto puede suceder raramente. Nunca lo hemos visto hasta ahora. El error se registra de todos modos y un bloqueo podría ser inevitable. Así que esto es conceptualmente similar al primer caso. Excepto que tenemos una traza de pila. Y en la mayoría de los casos el usuario ni siquiera se dará cuenta.
En cuanto a los datos del experimento generados por el programa: un error grave en el peor de los casos no causará que se registren datos. Los cambios sutiles que cambian el resultado del experimento aunque sea ligeramente son bastante improbables. E incluso en ese caso, si los resultados parecen dudosos, se registró el error; uno todavía puede tirar ese punto de datos si es un valor atípico total.
Para resumir: Sí, me considero todavía al menos parcialmente sano y no considero una rutina de manejo de excepciones global que haga que el programa se ejecute para ser necesariamente totalmente malvado. Como se dijo dos veces antes, tal decisión podría ser válida, dependiendo de la aplicación. En este caso, se consideró una decisión válida y no una completa y absoluta mentira. Para cualquier otra aplicación, esa decisión puede parecer diferente. Pero, por favor, no me acusen ni a mí ni a las otras personas que trabajaron en ese proyecto para potencialmente volar el mundo solo porque ignoramos los errores.
Nota al margen: Hay exactamente un usuario para esa aplicación. No es algo como Windows u Office que se usa por millones de personas, ya que el costo de tener excepciones para el usuario sería muy diferente en primer lugar.
Aquí está el ejemplo completo usando NLog
using NLog;
using System;
using System.Windows;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public App()
{
var currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
logger.Error("UnhandledException caught : " + ex.Message);
logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
}
}
}
Código de ejemplo usando NLog que detectará las excepciones lanzadas desde todos los subprocesos en el dominio de aplicación , desde el subproceso de la IU y desde las funciones asíncronas :
App.xaml.cs:
public partial class App : Application
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
SetupExceptionHandling();
}
private void SetupExceptionHandling()
{
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");
DispatcherUnhandledException += (s, e) =>
LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");
TaskScheduler.UnobservedTaskException += (s, e) =>
LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
}
private void LogUnhandledException(Exception exception, string source)
{
string message = $"Unhandled exception ({source})";
try
{
System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
}
catch (Exception ex)
{
_logger.Error(ex, "Exception in LogUnhandledException");
}
finally
{
_logger.Error(exception, message);
}
}
Como "VB''s On Error Resume Next?" Eso suena un poco aterrador. La primera recomendación es no hacerlo. La segunda recomendación es no hacerlo y no pensar en ello. Necesitas aislar mejor tus faltas. En cuanto a cómo abordar este problema, depende de cómo esté estructurado el código. Si está utilizando un patrón como MVC o similar, esto no debería ser demasiado difícil y definitivamente no requeriría una excepción global de tragar. En segundo lugar, busque una buena biblioteca de registro como log4net o use el rastreo. Necesitaríamos conocer más detalles, como los tipos de excepciones de los que está hablando y qué partes de su aplicación pueden dar lugar a excepciones.
Evento AppDomain.UnhandledException
Este evento proporciona notificación de excepciones no capturadas. Permite que la aplicación registre información sobre la excepción antes de que el controlador predeterminado del sistema informe la excepción al usuario y finalice la aplicación.
public App()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception) args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
}
Si el evento UnhandledException se controla en el dominio de aplicación predeterminado, se genera allí para cualquier excepción no controlada en cualquier hilo, sin importar en qué dominio de la aplicación se inició el hilo. Si el hilo comenzó en un dominio de aplicación que tiene un controlador de eventos para UnhandledException, El evento se plantea en ese dominio de aplicación. Si ese dominio de aplicación no es el dominio de aplicación predeterminado, y también hay un controlador de eventos en el dominio de aplicación predeterminado, el evento se genera en ambos dominios de aplicaciones.
Por ejemplo, supongamos que un hilo comienza en el dominio de aplicación "AD1", llama a un método en el dominio de aplicación "AD2", y desde allí llama a un método en el dominio de aplicación "AD3", donde lanza una excepción. El primer dominio de aplicación en el que se puede generar el evento UnhandledException es "AD1". Si ese dominio de aplicación no es el dominio de aplicación predeterminado, el evento también puede generarse en el dominio de aplicación predeterminado.
Utilice el Application.DispatcherUnhandledException Event
. Vea esta pregunta para un resumen (vea la respuesta de Drew Noakes ).
Tenga en cuenta que todavía habrá excepciones que impidan una reanudación exitosa de su aplicación, como después de un desbordamiento de pila, memoria agotada o pérdida de conectividad de red mientras intenta guardar en la base de datos.