ti-enxame.com

Como tratar exceções não tratadas? (Encerre o aplicativo x Mantenha-o vivo)

Qual é a melhor prática quando ocorrem exceções sem tratamento em um aplicativo de desktop?

Eu estava pensando em mostrar uma mensagem ao usuário, para que ele possa entrar em contato com o suporte. Eu recomendaria ao usuário reiniciar o aplicativo, mas não forçá-lo. Semelhante ao discutido aqui: x.stackexchange.com - Qual é a melhor maneira de lidar com erros inesperados de aplicativos?

O projeto é um aplicativo .NET WPF, portanto, a proposta descrita pode ser assim (Observe que este é um exemplo simplificado. Provavelmente faria sentido ocultar os detalhes da exceção até o usuário clicar em "Mostrar Detalhes" e fornecer algumas funcionalidades para reporte facilmente o erro):

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...
    }
}

Na implementação (Comandos do ViewModels ou manipulador de eventos de eventos externos), eu capturava apenas a exceção exógena específica e deixava todas as outras exceções (exceções de cabeça para osso e desconhecidas) chegarem ao "manipulador de último recurso" descrito acima. Para uma definição de exceções exageradas e exageradas, dê uma olhada em: Eric Lippert - exceções irritantes

Faz sentido deixar o usuário decidir se o aplicativo deve ser encerrado? Quando o aplicativo é encerrado, você certamente não tem um estado inconsistente. Por outro lado, o usuário pode perder dados não salvos ou não pode mais parar nenhum processo externo iniciado até que o aplicativo seja reiniciado.

Ou é a decisão de encerrar o aplicativo em exceções não tratadas, dependendo do tipo de aplicativo que você está gravando? É apenas uma troca entre "robustez" vs. "correção", como descrito em Code Complete, Second Edition

Para contextualizar qual tipo de aplicação estamos falando: A aplicação é usada principalmente para controlar instrumentos químicos de laboratório e mostrar os resultados medidos ao usuário. Para isso, os aplicativos WPF se comunicam com alguns serviços (serviços locais e remotos). O aplicativo WPF não se comunica diretamente com os instrumentos.

30
Jonas Benz

Você deve esperar que seu programa seja encerrado por mais motivos do que apenas uma exceção não tratada de qualquer maneira, como uma falta de energia ou um processo em segundo plano diferente que trava todo o sistema. Portanto, eu recomendaria encerrar e reiniciar o aplicativo, mas com algumas medidas para mitigar as consequências de tal reinicialização e minimizar a possível perda de dados.

Comece analisando os seguintes pontos:

  • Quantos dados podem realmente ser perdidos em caso de encerramento do programa?

  • Quão grave é essa perda realmente para o usuário? Os dados perdidos podem ser reconstruídos em menos de 5 minutos ou estamos falando de perder um dia de trabalho?

  • Quanto esforço é necessário para implementar alguma estratégia de "backup intermediário"? Não descarte isso porque "o usuário precisaria inserir um motivo de alteração" em uma operação de salvamento regular, como você escreveu em um comentário. É melhor pensar em algo como um arquivo ou estado temporário, que pode ser recarregado após uma falha do programa automaticamente. Muitos tipos de software de produtividade fazem isso (por exemplo, o MS Office e o LibreOffice têm um recurso de "salvamento automático" e recuperação de falhas).

  • Caso os dados estejam errados ou corrompidos, o usuário pode ver isso facilmente (talvez após a reinicialização do programa)? Em caso afirmativo, você pode oferecer uma opção para permitir que o usuário salve os dados (com algumas chances de que eles estejam corrompidos), force uma reinicialização, recarregue-os e deixe o usuário verificar se os dados estão corretos. Certifique-se de não substituir a última versão que foi salva regularmente (em vez disso, grave em um local/arquivo temporário) para evitar danificar a versão antiga.

Se essa estratégia de "backup intermediário" for uma opção sensata, depende, em última análise, do aplicativo e de sua arquitetura, e da natureza e estrutura dos dados envolvidos. Mas se o usuário perder menos de 10 minutos de trabalho e esse acidente ocorrer uma vez por semana ou mais raramente, provavelmente não investirei muito nisso.

47
Doc Brown

Depende até certo ponto do aplicativo que você está desenvolvendo, mas, em geral, eu diria que, se o seu aplicativo encontrar uma exceção não tratada, será necessário encerrá-lo.

Por quê?

Como você não pode mais ter confiança no estado do aplicativo.

Definitivamente, forneça uma mensagem útil ao usuário, mas você deverá finalizar o aplicativo.

Dado o seu contexto, eu definitivamente gostaria que o aplicativo fosse encerrado. Você não deseja que o software em execução no laboratório produza saída corrompida e, como não pensou em lidar com a exceção, não tem idéia do motivo pelo qual ela foi lançada e o que está acontecendo.

30
Matthew

Considerando que isso se destina a um laboratório químico e que seu aplicativo não controla os instrumentos diretamente, mas através de outros serviços:

Forçar encerramento após mostrar a mensagem. Após uma exceção não tratada, seu aplicativo está em um estado desconhecido. Pode enviar comandos errados. Pode até invocar demônios nasais . Um comando incorreto pode potencialmente desperdiçar reagentes caros ou trazer perigo para equipamentos ou pessoas .

Mas você pode fazer outra coisa: recupere normalmente após reiniciar . Presumo que seu aplicativo não reduz esses serviços em segundo plano quando falha. Nesse caso, você pode facilmente recuperar o estado deles. Ou, se você tiver mais estado, considere salvá-lo. Em um armazenamento que possui provisões para atomicidade e integridade dos dados (talvez SQLite?).

Editar:

Conforme declarado nos comentários, o processo que você controla pode exigir alterações com rapidez suficiente para que o usuário não tenha tempo para reagir. Nesse caso, considere reiniciar o aplicativo silenciosamente, além da recuperação normal do estado.

12
Jan Dorniak

Tentar geralmente responder a essa pergunta no nível superior do programa não é uma jogada inteligente.

Se algo borbulhou por todo o caminho, e em nenhum momento da arquitetura do aplicativo alguém considerou esse caso, não há generalizações que você possa fazer sobre quais ações são ou não são seguras.

Portanto, não, definitivamente não é um design geralmente aceitável permitir ao usuário escolher se o aplicativo tenta ou não se recuperar, porque o aplicativo e os desenvolvedores demonstrativamente não fizeram a devida diligência necessária para descobrir se isso é possível ou sábio .

No entanto, se o aplicativo tiver partes de alto valor de sua lógica ou comportamento que foram projetadas com esse tipo de recuperação de falhas em mente, e for possível aproveitá-las nesse caso, faça isso de qualquer maneira - nesse caso , pode ser aceitável solicitar ao usuário para ver se ele deseja tentar a recuperação ou se deseja apenas encerrar e começar de novo.

Esse tipo de recuperação geralmente não é necessário ou aconselhável para todos (ou mesmo para a maioria) dos programas, mas, se você estiver trabalhando em um programa para o qual esse grau de integridade operacional é necessário, pode ser uma circunstância em que apresentar esse tipo de Solicitar a um usuário seria algo sensato a ser feito.

Em vez de qualquer lógica especial de recuperação de falhas - Não, não faça isso. Você literalmente não tem idéia do que acontecerá; se o fizesse, teria capturado a exceção mais adiante e resolvido a questão.

8
Iron Gremlin

O problema com "exceções excepcionais", ou seja, exceções que você não previu, é que você não sabe em que estado o programa está. Por exemplo, tentar salvar os dados do usuário pode realmente destrói ainda mais dados .

Por esse motivo, você deve encerrar o aplicativo.

Existe uma ideia muito interessante chamada Crash-only Software por George Candea e Armando Fox . A idéia é que, se você projetar seu software de forma que a única maneira de fechá-lo seja travá-lo e a única maneira de iniciá-lo seja se recuperar de um travamento, seu software será mais resiliente e a recuperação de erros caminhos de código serão muito mais minuciosamente testados e exercitados.

Eles tiveram essa ideia depois de perceberem que alguns sistemas iniciaram mais rapidamente após uma falha do que após um desligamento ordenado.

Um bom exemplo, embora não seja mais relevante, são algumas versões mais antigas do Firefox que não apenas iniciam mais rapidamente quando se recuperam de uma falha, mas também têm uma melhor experiência de inicialização dessa maneira ! Nessas versões, se você desligasse o Firefox normalmente, ele fecharia todas as guias abertas e iniciaria com uma única guia vazia. Enquanto que, ao se recuperar de uma falha, ela restaura as guias abertas no momento da falha. (E essa foi a única maneira de fechar o Firefox sem perder o contexto atual de navegação.) Então, o que as pessoas fizeram? Eles simplesmente nunca fechavam o Firefox e sempre sempre pkill -KILL firefoxed.

Existe ma boa redação sobre software somente para falhas de Valerie Aurora no Linux Weekly News . Os comentários também merecem uma leitura. Por exemplo, alguém nos comentários aponta com razão que essas idéias não são novas e são de fato mais ou menos equivalentes aos princípios de design de aplicativos baseados em Erlang/OTP. E, é claro, olhando para isso hoje, mais 10 anos após o artigo de Valerie e 15 anos após o artigo original, podemos notar que o atual hype dos microsserviços está reinventando essas mesmas idéias mais uma vez. O design moderno do data center em escala de nuvem também é um exemplo de granularidade mais grossa. (Qualquer computador pode travar a qualquer momento sem afetar o sistema.)

No entanto, não basta apenas deixar seu software travar. Tem que ser projetado para isso. Idealmente, seu software seria dividido em componentes pequenos e independentes, cada um com falha independente. Além disso, o "mecanismo de falha" deve estar fora do componente que está sendo travado.

3
Jörg W Mittag

A maneira correta de lidar com a maioria das exceções deve ser invalidar qualquer objeto que possa estar em um estado corrompido como conseqüência e continuar a execução se objetos invalidados não impedirem isso. Por exemplo, o paradigma seguro para atualizar um recurso seria:

acquire lock
try
  update guarded resource
if exception
  invalidate lock
else
  release lock
end try

Se ocorrer uma exceção inesperada durante a atualização do recurso protegido, presume-se que o recurso esteja em um estado corrompido e o bloqueio seja invalidado, independentemente de a exceção ser de um tipo que, de outra forma, seria benigno.

Infelizmente, os protetores de recursos implementados por IDisposable/using serão liberados sempre que o bloco protegido sair, sem nenhuma maneira de saber se o bloco saiu normalmente ou de forma anormal. Portanto, mesmo que haja critérios bem definidos para quando continuar após uma exceção, não há como saber quando eles se aplicam.

1
supercat

Você pode usar a abordagem que todo aplicativo iOS e MacOS segue: Uma exceção não capturada desativa o aplicativo imediatamente. Além disso, muitos erros, como a matriz fora dos limites ou apenas o estouro aritmético em aplicativos mais recentes, fazem o mesmo. Nenhum aviso.

Na minha experiência, muitos usuários não prestam atenção, mas apenas toque no ícone do aplicativo novamente.

Obviamente, você precisa garantir que tal falha não leve a perda significativa de dados e definitivamente não leve a erros caros. Mas um alerta "Seu aplicativo falhará agora. Ligue para o suporte se isso o incomodar "não está ajudando ninguém.

0
gnasher729