¿Cuál es la mejor práctica cuando se producen excepciones no controladas en una aplicación de escritorio?
Estaba pensando en mostrar un mensaje al usuario, para que pueda ponerse en contacto con el soporte. Recomendaría al usuario reiniciar la aplicación, pero no forzarla. Similar a lo que se discute aquí: ux.stackexchange.com - ¿Cuál es la mejor manera de manejar errores inesperados de la aplicación?
El proyecto es una aplicación .NET WPF, por lo que la propuesta descrita podría verse así (tenga en cuenta que este es un ejemplo simplificado. Probablemente tendría sentido ocultar los detalles de la excepción hasta que el usuario haga clic en "Mostrar detalles" y proporcione alguna funcionalidad para informar fácilmente el error):
public partial class App : Application
{
public App()
{
DispatcherUnhandledException += OnDispatcherUnhandledException;
}
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
LogError(e.Exception);
MessageBoxResult result = MessageBox.Show(
$"Please help us fix it and contact [email protected]. Exception details: {e.Exception}" +
"We recommend to restart the application. " +
"Do you want to stop the application now? (Warning: Unsaved data gets lost).",
"Unexpected error occured.", MessageBoxButton.YesNo);
// Setting 'Handled' to 'true' will prevent the application from terminating.
e.Handled = result == MessageBoxResult.No;
}
private void LogError(Exception ex)
{
// Log to a log file...
}
}
En la implementación (Comandos de ViewModels o controlador de eventos externos), solo capturaría la excepción exógena específica y dejaría que todas las demás excepciones (excepciones descabelladas y desconocidas) se propaguen al "controlador de último recurso" descrito anteriormente. Para obtener una definición de excepciones exógenas y descabelladas, eche un vistazo a: Eric Lippert - Excepciones irritantes
¿Tiene sentido dejar que el usuario decida si la aplicación debe terminarse? Cuando finaliza la aplicación, seguramente no tiene un estado incoherente ... Por otro lado, el usuario puede perder datos no guardados o ya no puede detener ningún proceso externo iniciado hasta que se reinicie la aplicación.
¿O es la decisión si debe terminar la aplicación en excepciones no manejadas, dependiendo del tipo de aplicación que está escribiendo? ¿Es solo una compensación entre "robustez" versus "corrección" como se describe en Code Complete, Segunda edición
Para darle un poco de contexto de qué tipo de aplicación estamos hablando: la aplicación se utiliza principalmente para controlar instrumentos de laboratorio químico y mostrar los resultados medidos al usuario. Para hacerlo, las aplicaciones WPF se comunican con algunos servicios (servicios locales y remotos). La aplicación WPF no se comunica directamente con los instrumentos.
fuente
Respuestas:
De todos modos, debe esperar que su programa finalice por más razones que una simple excepción no controlada, como una falla de energía o un proceso en segundo plano diferente que bloquea todo el sistema. Por lo tanto, recomendaría finalizar y reiniciar la aplicación, pero con algunas medidas para mitigar las consecuencias de dicho reinicio y minimizar la posible pérdida de datos .
Comience analizando los siguientes puntos:
¿Cuántos datos pueden perderse en caso de finalización del programa?
¿Qué tan grave es realmente una pérdida para el usuario? ¿Se pueden reconstruir los datos perdidos en menos de 5 minutos, o estamos hablando de perder un día de trabajo?
¿Cuánto esfuerzo es implementar alguna estrategia de "respaldo intermedio"? No descarte esto porque "el usuario tendría que ingresar un motivo de cambio" en una operación de guardado regular, como escribió en un comentario. Mejor piense en algo como un archivo o estado temporal, que se puede volver a cargar después de que un programa se bloquee automáticamente. Muchos tipos de software de productividad hacen esto (por ejemplo, MS Office y LibreOffice tienen una función de "autoguardado" y recuperación de fallos).
En caso de que los datos estén incorrectos o dañados, ¿puede el usuario ver esto fácilmente (tal vez después de reiniciar el programa)? En caso afirmativo, puede ofrecer una opción para permitir que el usuario guarde los datos (con una pequeña posibilidad de que esté dañado), luego forzar un reinicio, volver a cargarlo y dejar que el usuario verifique si los datos se ven bien. Asegúrese de no sobrescribir la última versión que se guardó regularmente (en su lugar, escriba en una ubicación / archivo temporal) para evitar corromper la versión anterior.
Si dicha estrategia de "respaldo intermedio" es una opción sensata, depende en última instancia de la aplicación y su arquitectura, y de la naturaleza y estructura de los datos involucrados. Pero si el usuario pierde menos de 10 minutos de trabajo, y tal falla ocurre una vez a la semana o incluso más raramente, probablemente no invertiría demasiado en esto.
fuente
Depende en cierta medida de la aplicación que esté desarrollando, pero en general, diría que si su aplicación encuentra una excepción no controlada, debe terminarla.
¿Por qué?
Porque ya no puede confiar en el estado de la aplicación.
Definitivamente, proporcione un mensaje útil al usuario, pero finalmente debe finalizar la aplicación.
Dado su contexto, definitivamente me gustaría que la aplicación finalice. No desea que el software que se ejecuta en un laboratorio produzca resultados corruptos y, dado que no pensó en manejar la excepción, no tiene idea de por qué se lanzó y qué está sucediendo.
fuente
Teniendo en cuenta que esto está destinado a un laboratorio químico y que su aplicación no controla los instrumentos directamente, sino a través de otros servicios:
Forzar la terminación después de mostrar el mensaje. Después de una excepción no controlada, su aplicación se encuentra en un estado desconocido. Podría enviar comandos erróneos. Incluso puede invocar demonios nasales . Un comando erróneo podría desperdiciar reactivos costosos o poner en peligro el equipo o las personas. .
Pero puede hacer otra cosa: recuperarse con gracia después de reiniciar . Supongo que su aplicación no elimina esos servicios en segundo plano cuando se bloquea. En ese caso, puede recuperar fácilmente el estado de ellos. O, si tiene más estado, considere guardarlo. En un almacenamiento que tiene disposiciones para la atomicidad e integridad de los datos (¿tal vez SQLite?).
Editar:
Como se indicó en los comentarios, el proceso que controlas puede requerir cambios lo suficientemente rápido como para que el usuario no tenga tiempo de reaccionar. En ese caso, debe considerar reiniciar silenciosamente la aplicación además de la recuperación de estado elegante.
fuente
Tratar de responder generalmente esta pregunta en el nivel superior del programa no es una jugada inteligente.
Si algo ha surgido por completo, y en ningún momento de la arquitectura de la aplicación alguien consideró este caso, no tiene generalizaciones que pueda hacer sobre qué acciones son o no seguras de tomar.
Entonces, no, definitivamente no es un diseño generalmente aceptable para permitir que el usuario elija si la aplicación intenta recuperarse o no, porque la aplicación y los desarrolladores demostrativamente no han realizado la debida diligencia necesaria para averiguar si eso es posible o incluso sabio .
Sin embargo, si la aplicación tiene porciones de alto valor de su lógica o comportamiento que se han diseñado teniendo en cuenta este tipo de recuperación de fallas, y es posible aprovecharlas en este caso, entonces, por todos los medios, hágalo. En ese caso , puede ser aceptable pedirle al usuario que vea si desea intentar la recuperación, o si desea simplemente cerrarlo y comenzar de nuevo.
Este tipo de recuperación no es generalmente necesaria o aconsejable para todos (o incluso para la mayoría) de los programas, pero, si está trabajando en un programa para el que se requiere este grado de integridad operativa, esa podría ser una circunstancia en la que presentar este tipo de preguntarle a un usuario sería algo sensato.
En virtud de cualquier lógica especial de recuperación de fallas: No, no haga esto. Literalmente no tienes idea de lo que sucederá; si lo hicieras, habrías captado la excepción más abajo y la manejarías.
fuente
El problema con las "excepciones excepcionales", es decir, las excepciones que no ha previsto, es que no sabe en qué estado se encuentra el programa. Por ejemplo, tratar de guardar los datos del usuario podría destruir aún más datos .
Por esa razón, debe finalizar la aplicación.
Hay una idea muy interesante llamada Software solo para accidentes de George Candea y Armando Fox . La idea es que si diseña su software de tal manera que la única forma de cerrarlo es bloquearlo y la única forma de iniciarlo es recuperarse de un bloqueo, entonces su software será más resistente y la recuperación de errores las rutas de código se probarán y ejercitarán mucho más a fondo.
Se les ocurrió esta idea después de notar que algunos sistemas comenzaron más rápido después de un bloqueo que después de un apagado ordenado.
Un buen ejemplo, aunque ya no es relevante, son algunas versiones anteriores de Firefox que no solo comienzan más rápido al recuperarse de un bloqueo, sino que también tienen una mejor experiencia de inicio . En esas versiones, si cierra Firefox normalmente, cerraría todas las pestañas abiertas y comenzaría con una sola pestaña vacía. Mientras que al recuperarse de un bloqueo, restauraría las pestañas abiertas en el momento del bloqueo. (Y esa era la única forma de cerrar Firefox sin perder su contexto de navegación actual). Entonces, ¿qué hicieron las personas? Simplemente nunca cerraron Firefox y en su lugar siempre lo
pkill -KILL firefox
editaban.Hay una buena reseña sobre el software de solo bloqueo de Valerie Aurora en Linux Weekly News . Los comentarios también merecen una lectura. Por ejemplo, alguien en los comentarios señala con razón que esas ideas no son nuevas y, de hecho, son más o menos equivalentes a los principios de diseño de las aplicaciones basadas en Erlang / OTP. Y, por supuesto, al ver esto hoy, otros 10 años después del de Valerie y 15 años después del artículo original, podríamos notar que la exageración actual del micro servicio está reinventando esas mismas ideas una vez más. El diseño moderno del centro de datos a escala de la nube también es un ejemplo de una granularidad más gruesa. (Cualquier computadora puede fallar en cualquier momento sin afectar el sistema).
Sin embargo, no es suficiente dejar que su software se bloquee. Tiene que estar diseñado para ello. Idealmente, su software se dividiría en componentes pequeños e independientes que cada uno puede bloquearse de forma independiente. Además, el "mecanismo de bloqueo" debe estar fuera del componente que se está bloqueando.
fuente
La forma correcta de manejar la mayoría de las excepciones debería ser invalidar cualquier objeto que pueda estar en un estado corrupto como consecuencia, y continuar la ejecución si los objetos invalidados no lo impiden. Por ejemplo, el paradigma seguro para actualizar un recurso sería:
Si se produce una excepción inesperada al actualizar el recurso protegido, se debe suponer que el recurso está en un estado corrupto y se debe invalidar el bloqueo, independientemente de si la excepción es de un tipo que de otro modo sería benigno.
Desafortunadamente, los guardias de recursos implementados a través de
IDisposable
/using
se liberarán cada vez que el bloque protegido salga, sin ninguna forma de saber si el bloque salió de manera normal o anormal. Por lo tanto, aunque debe haber criterios bien definidos sobre cuándo continuar después de una excepción, no hay forma de saber cuándo se aplican.fuente
Puede utilizar el enfoque que siguen todas las aplicaciones de iOS y MacOS: una excepción no detectada desactiva la aplicación de inmediato. Además, muchos errores, como la matriz fuera de los límites o simplemente el desbordamiento aritmético en las aplicaciones más nuevas, hacen lo mismo. Sin advertencia.
En mi experiencia, muchos usuarios no se dan cuenta pero simplemente vuelven a tocar el ícono de la aplicación.
Obviamente, debe asegurarse de que dicho bloqueo no conduzca a una pérdida de datos significativa y definitivamente no conduzca a errores costosos. Pero una alerta "Tu aplicación se bloqueará ahora. Llame al soporte si le molesta ”no está ayudando a nadie.
fuente