Cómo capturar cualquier error que se pueda producir en una aplicación desarrollada en C Sharp (C#) y no haya sido expresamente capturado. Este método será muy útil para evitar que aparezca la típica ventana de error no capturado en aplicación .Net, mostrando datos hasta del código fuente de la aplicación (como el nombre del método que ha producido el error). De esta forma podremos o bien mostrar un error personalizado o bien guardar el error en un fichero de log y no mostrarlo al usuario.
Cuando en alguna parte de nuestro código C# no hemos controlado una posible excepción (error), si se produce, se mostrará una ventana por defecto al usuario indicando el error. En muchas ocasiones esto es contraproducente, dado que mostramos al usuario una ventana «extraña» para él, con la que no sabe qué hacer, si continuar o si salir:

Además, si el usuario pulsa en «Detalles» puede obtener información que no debemos mostrar sobre nuestro código fuente, como por ejemplo el nombre del método y los parámetros que admite que produce el error:

Para capturar cualquier excepción no capturada en nuestra aplicación y así poder mostrar un mensaje personalizado al usuario (o realizar cualquier otra acción), abriremos el fichero de inicio de la aplicación, donde suele estar la clase Program:

En static void Main(), al principio:

Añadiremos las siguientes líneas:
1 2 3 4 |
//Para capturar todas las excepciones no capturadas con try..catch en procesos que no son de la la interfaz gráfica Application.ThreadException += new ThreadExceptionEventHandler(Tarea_Error_No_Interfaz); //Para capturar todas las excepciones no capturadas con try..catch en la interfaz gráfica de la aplicación AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Tarea_Error_Interfaz); |
En la clase internal static class Program, definiremos los métodos que llamamos si se produce un error no capturado Tarea_Error_No_Interfaz y Tarea_Error_Interfaz, quedando:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Tarea a realizar cuando se produce una excepción no capturada con try..catch en proceso que no es de la la interfaz gráfica static void Tarea_Error_No_Interfaz(object sender, ThreadExceptionEventArgs e) { Log.EsLog(mensaje: "Error no capturado en subproceso que no es de la interfaz gráfica: " + e.Exception.Message, mostrarConsola: false, mostrarVisual: true); } // Tarea a realizar cuando se produce una excepción no capturada con try..catch en proceso que es de la interfaz gráfica static void Tarea_Error_Interfaz(object sender, UnhandledExceptionEventArgs e) { Log.EsLog(mensaje: "Error no capturado en subproceso de la interfaz gráfica: " + (e.ExceptionObject as Exception), mostrarConsola: false, mostrarVisual: true); } |
En el ejemplo anterior, al producirse un error que no hemos capturado expresamente en nuestra aplicación C#, escribimos el error en un fichero y también lo mostramos al usuario, pero con mensaje personalizado. Para crear esta clase de log, añadiremos una nueva clase a nuestra app C#, con el nombre Log.cs:

Y le agregaremos el siguiente contenido:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
using System; using System.IO; using System.Windows.Forms; namespace ErrorNoControladoProyectoA { class Log { //Guardar mensaje en fichero de texto o también mostrarlo por consola public static void EsLog(string mensaje, Boolean mostrarConsola, Boolean mostrarVisual) { if (mostrarVisual) MostrarMensajeVisual(mensaje); mensaje = QuitarSaltos(mensaje); if (mostrarConsola) MonstrarMensajeConsola(mensaje); EscribirLineaFichero(mensaje); } //Muestra el mensaje por consola MS-DOS (solo para aplicaciones de consola) private static void MonstrarMensajeConsola(string mensaje) { Console.WriteLine($"{DateTime.Now:dd/MM/yyyy hh:mm:ss} {mensaje}"); } //Muestra una ventana visual con el mensaje private static void MostrarMensajeVisual(string mensaje) { MessageBox.Show(mensaje,"Aviso...", MessageBoxButtons.OK, MessageBoxIcon.Error); } //Escribe el mensaje en un fichero en la carpeta del ejecutable private static void EscribirLineaFichero(string mensaje) { try { FileStream fs = new FileStream(@AppDomain.CurrentDomain.BaseDirectory + "estado.log", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter m_streamWriter = new StreamWriter(fs); m_streamWriter.BaseStream.Seek(0, SeekOrigin.End); m_streamWriter.BaseStream.Seek(0, SeekOrigin.End); m_streamWriter.WriteLine($"{DateTime.Now:dd/MM/yyyy hh:mm:ss} {mensaje}"); m_streamWriter.Flush(); m_streamWriter.Close(); } catch { //Silenciosa } } //Quita posibles saltos de línea del mensaje private static string QuitarSaltos(string mensaje) { mensaje = mensaje.Replace(Environment.NewLine, " | "); mensaje = mensaje.Replace("\r\n", " | ").Replace("\n", " | ").Replace("\r", " | "); return mensaje; } } } |
El código C# completo de ejemplo del fichero Program.cs que contiene la clase principal de la aplicación Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System; using System.Threading; using System.Windows.Forms; namespace ErrorNoControladoProyectoA { internal static class Program { /// <summary> /// Punto de entrada principal para la aplicación. /// </summary> [STAThread] static void Main() { // Para capturar todas las excepciones no capturadas con try..catch en procesos que no son de la la interfaz gráfica Application.ThreadException += new ThreadExceptionEventHandler(Tarea_Error_No_Interfaz); // Para capturar todas las excepciones no capturadas con try..catch en la interfaz gráfica de la aplicación AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Tarea_Error_Interfaz); // Inicio normal de la aplicación Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new formErrorNoControlado()); } // Tarea a realizar cuando se produce una excepción no capturada con try..catch en proceso que no es de la la interfaz gráfica static void Tarea_Error_No_Interfaz(object sender, ThreadExceptionEventArgs e) { Log.EsLog(mensaje: "Error no capturado en subproceso que no es de la interfaz gráfica: " + e.Exception.Message, mostrarConsola: false, mostrarVisual: true); } // Tarea a realizar cuando se produce una excepción no capturada con try..catch en proceso que es de la interfaz gráfica static void Tarea_Error_Interfaz(object sender, UnhandledExceptionEventArgs e) { Log.EsLog(mensaje: "Error no capturado en subproceso de la interfaz gráfica: " + (e.ExceptionObject as Exception), mostrarConsola: false, mostrarVisual: true); } } } |
Si ahora volvemos a ejecutar el botón que produce un error no capturado, nos devolverá este mensaje personalizado:

Además de haber guardado el error en un fichero de log, para depuración por parte de los desarrolladores:
