Como posso fazer algo que capture todas as exceções 'não tratadas' em um aplicativo WinForms?

87

Até agora, eu apenas coloquei um bloco try / catch em torno de Application.Runno Program.csponto de entrada do programa. Isso captura todas as exceções bem o suficiente no modo de depuração, mas quando executo o programa sem o modo de depuração, as exceções não são mais tratadas. Recebo a caixa de exceção não tratada.

Eu não quero que isso aconteça Quero que todas as exceções sejam detectadas ao executar no modo de não depuração. O programa tem vários threads e, de preferência, todas as exceções deles são capturadas pelo mesmo manipulador; Quero registrar exceções no banco de dados. Alguém tem algum conselho sobre como fazer isso?

Isaac Bolinger
fonte

Respostas:

112

Dê uma olhada no exemplo da documentação ThreadException :

public static void Main(string[] args)
{
   // Add the event handler for handling UI thread exceptions to the event.
    Application.ThreadException += new     
  ThreadExceptionEventHandler(ErrorHandlerForm.Form1_UIThreadException);

  // Set the unhandled exception mode to force all Windows Forms errors
  // to go through our handler.
  Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

  // Add the event handler for handling non-UI thread exceptions to the event. 
  AppDomain.CurrentDomain.UnhandledException += new       
  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

Você também pode querer não capturar exceções durante a depuração, pois isso facilita a depuração. É uma espécie de hack, mas para isso você pode envolver o código acima com

 if (!AppDomain.CurrentDomain.FriendlyName.EndsWith("vshost.exe")) { ... }

Para evitar a captura de exceções durante a depuração.

Can Gencer
fonte
1
Fiz um trabalhador em segundo plano e, no manipulador de eventos dowork, causei intencionalmente uma exceção de referência nula. No entanto, ele não foi capturado pelo AppDomain.CurrentDomain.UnhandledException, apesar de definir estes: Application.ThreadException + = new System.Threading.ThreadExceptionEventHandler (Application_ThreadException); Application.SetUnhandledExceptionMode (UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException + = new UnhandledExceptionEventHandler (CurrentDomain_UnhandledException);
Isaac Bolinger
4
@IsaacB, o trabalhador em segundo plano captura as exceções por conta própria. Você pode verificar a exceção no RunWorkerCompleted até mesmo, observando a propriedade RunCompletedEventArgs.Error.
Can Gencer,
1
Você pode testar o tratamento de exceções para threads adicionais, colocando-o no OnLoad do seu formulário principal. new Thread (() => {lançar nova Exception ();}). Start ();
Can Gencer,
Infelizmente, lidar com UnhandledException não impedirá o encerramento do aplicativo :(
Nazar Grynko
7
Em vez do hack FriendlyName.EndsWith, tente Debugger.IsAttached, que é mais limpo.
fundida de
27

No NET 4, certas exceções não são mais detectadas por padrão; estes tendem a ser exceções que indicam um estado corrompido (possivelmente fatal) do executável, como AccessViolationException.

Tente usar a tag [HandleProcessCorruptedStateExceptions] antes de seu método principal, por exemplo

using System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions

[HandleProcessCorruptedStateExceptions]
public static int Main()
{
    try
    {
        // Catch any exceptions leaking out of the program
        CallMainProgramLoop();
    }
    catch (Exception e) // We could be catching anything here
    {
        System.Console.WriteLine(e.Message);
        return 1;
    }
    return 0;
  } 
Carlos P
fonte
Posso usar AppDomain.CurrentDomain.UnhandledExceptione Application.ThreadExceptiontambém com [HandleProcessCorruptedStateExceptions]tag?
Kiquenet 01/03/2017
18

Um bom exemplo pode ser encontrado em http://www.csharp-examples.net/catching-unhandled-exceptions/ Basicamente, mude seu principal para:

static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Application.Run(new Form1());
    }

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        MessageBox.Show(e.Exception.Message, "Unhandled Thread Exception");
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        MessageBox.Show((e.ExceptionObject as Exception).Message, "Unhandled UI Exception");
    }
Seb
fonte
9

Você pode usar a biblioteca NBug para isso. Com configuração mínima como esta:

NBug.Settings.Destination1 = "Type=Mail;[email protected];[email protected];SmtpServer=smtp.mycompany.com;";
AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;
Application.ThreadException += NBug.Handler.ThreadException;

Você pode começar a coletar informações sobre todos os bugs não tratados em seu aplicativo, mesmo quando ele está implantado nos clientes. Se você não quiser usar uma biblioteca de terceiros, você deve anexar aos eventos abaixo:

// These two should come before enabling visual styles or running the application
AppDomain.CurrentDomain.UnhandledException += ...
Application.ThreadException += ...
...
Application.Run(new Form1());
Teoman Soygul
fonte
1
De nada. Use o fórum de discussão do projeto NBug se você tiver mais perguntas ( nbusy.com/forum/f11 ) ou use a tag [nbug] aqui.
Teoman Soygul
Obviamente, você também pode inscrever um manipulador de eventos "normal" para o evento UnhandledException. Consulte msdn.microsoft.com/en-us/library/…
neo2862
Pessoal no Win7 + VS10, se eu me inscrever nesses eventos, a inscrição não funciona, em vez disso, a caixa de diálogo normal do Windows Vista / 7 aparece Check Online for a SolutionOu Close the Program... etc. Mas se eu NÃO assinar, recebo a exceção genérica normal do .NET não tratada Janela. Isso acontece nas compilações Release e Debug, também tentei configurar Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);Não altera nada.
gideon
@giddy, após tratar das exceções você deve sair do aplicativo com Environment.Exit (1); se você não quiser que a janela de erro seja exibida.
Teoman Soygul
@Teo obrigado pela sua resposta. Quero que meu próprio formulário de erro apareça e depois quero que o aplicativo saia. Mas a inscrição do evento nunca é executada, ela apenas mostra a caixa de diálogo genérica do Win Vista / 7 quando encontra exceções. Mas se eu não assinar, a caixa de diálogo de exceção não tratada genérica do .NET aparecerá!
gideon