Gestore di eccezioni .NET globale nell'applicazione console


198

Domanda: Voglio definire un gestore di eccezioni globale per le eccezioni non gestite nella mia applicazione console. In asp.net, è possibile definirne uno in global.asax e nelle applicazioni / servizi di Windows, è possibile definirlo come di seguito

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

Ma come posso definire un gestore di eccezioni globale per un'applicazione console?
currentDomain sembra non funzionare (.NET 2.0)?

Modificare:

Argh, stupido errore.
In VB.NET, è necessario aggiungere la parola chiave "AddHandler" davanti a currentDomain, altrimenti non si vede l'evento UnhandledException in IntelliSense ...
Questo perché i compilatori VB.NET e C # trattano la gestione degli eventi in modo diverso.

Risposte:


283

No, questo è il modo corretto di farlo. Questo ha funzionato esattamente come dovrebbe, qualcosa su cui puoi lavorare forse:

using System;

class Program {
    static void Main(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

Tieni presente che non puoi rilevare eccezioni di tipo e di caricamento del file generate dal jitter in questo modo. Accadono prima che inizi il tuo metodo Main (). Per catturarli è necessario ritardare il jitter, spostare il codice rischioso in un altro metodo e applicare l'attributo [MethodImpl (MethodImplOptions.NoInlining)].


3
Ho implementato ciò che hai proposto qui, ma non voglio uscire dall'applicazione. Voglio solo registrarlo e continuare il processo (senza Console.ReadLine()o nessun altro disturbo del flusso del programma. Ma quello che ottengo è l'eccezione che si ripropone ancora e ancora e ancora.

3
@Shahrooz Jefri: non puoi continuare una volta ricevuta un'eccezione non gestita. Lo stack è incasinato e questo è il terminale. Se si dispone di un server, ciò che è possibile fare in UnhandledExceptionTrapper è riavviare il programma con gli stessi argomenti della riga di comando.
Stefan Steiger,

6
Lo fa sicuramente! Non stiamo parlando dell'evento Application.ThreadException qui.
Hans Passant,

4
Penso che la chiave per comprendere questa risposta e i commenti in risposta sia rendersi conto che questo codice dimostra ora di rilevare l'eccezione, ma non "gestirla" completamente come potresti in un normale blocco try / catch in cui hai la possibilità di continuare . Vedi la mia altra risposta per i dettagli. Se "gestisci" l'eccezione in questo modo, non hai alcuna opzione per continuare l'esecuzione quando si verifica un'eccezione su un altro thread. In tal senso, si può dire che questa risposta non "gestisce" completamente l'eccezione se per "gestire" intendi "processo e continua a funzionare".
BlueMonkMN

4
Per non parlare del fatto che ci sono molte tecnologie da eseguire in thread diversi. Ad esempio, Task Parallel Library (TPL) ha il suo modo di catturare le eccezioni non gestite. Quindi, dire che questo non funziona per tutte le situazioni è un po 'ridicolo, NON c'è un posto unico per tutto in C #, ma a seconda delle tecnologie che usi ci sono vari posti in cui puoi collegarti.
Doug,

23

Se si dispone di un'applicazione a thread singolo, è possibile utilizzare un semplice tentativo / catch nella funzione Main, tuttavia, ciò non copre le eccezioni che possono essere generate al di fuori della funzione Main, ad esempio su altri thread (come notato in altri Commenti). Questo codice dimostra come un'eccezione può causare la chiusura dell'applicazione anche se si è tentato di gestirla in Main (notare come il programma si chiude correttamente se si preme invio e si consente all'applicazione di uscire con grazia prima che si verifichi l'eccezione, ma se la si lascia eseguire , termina abbastanza infelice):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

È possibile ricevere una notifica di quando un altro thread genera un'eccezione per eseguire un po 'di pulizia prima che l'applicazione venga chiusa, ma per quanto ne so, non è possibile, da un'applicazione console, forzare l'applicazione a continuare l'esecuzione se non si gestisce l'eccezione sul thread da cui viene generato senza utilizzare alcune oscure opzioni di compatibilità per far sì che l'applicazione si comporti come avrebbe fatto con .NET 1.x. Questo codice dimostra come il thread principale può essere informato delle eccezioni provenienti da altri thread, ma terminerà comunque infelice:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

Quindi secondo me, il modo più pulito per gestirlo in un'applicazione console è garantire che ogni thread abbia un gestore di eccezioni a livello di root:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}

TRY CATCH non funziona in modalità di rilascio per errori imprevisti: /
Muflix

12

È inoltre necessario gestire le eccezioni dai thread:

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

Scusatemi, era per winforms, per tutti i thread che state usando in un'applicazione console dovrete racchiuderli in un blocco try / catch. I thread in background che incontrano eccezioni non gestite non causano la chiusura dell'applicazione.


1

Ho appena ereditato una vecchia applicazione console VB.NET e avevo bisogno di configurare un gestore di eccezioni globale. Poiché questa domanda menziona VB.NET alcune volte ed è taggata con VB.NET, ma tutte le altre risposte qui sono in C #, ho pensato di aggiungere anche la sintassi esatta per un'applicazione VB.NET.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

Ho usato il REMmarcatore di commento anziché la singola virgoletta qui perché Stack Overflow sembrava gestire meglio l'evidenziazione della sintassi REM.


-13

Quello che stai cercando dovrebbe funzionare secondo il documento MSDN per .Net 2.0. Puoi anche provare a provare / catturare proprio in main attorno al tuo punto di ingresso per l'app console.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

E ora la tua cattura gestirà tutto ciò che non è stato catturato ( nel thread principale ). Può essere grazioso e persino riavviare dove si trovava, se lo desideri, oppure puoi semplicemente lasciar morire l'app e registrare l'eccezione. Dovresti aggiungere infine un se vuoi fare una pulizia. Ogni thread richiederà la propria gestione delle eccezioni di alto livello simile alla principale.

Modificato per chiarire il punto sui thread come sottolineato da BlueMonkMN e mostrato in dettaglio nella sua risposta.


1
Sfortunatamente, le eccezioni possono ancora essere generate all'esterno del blocco Main (). Questo non è in realtà un "catch all" come potresti pensare. Vedi la risposta di @Hans.
Mike Atlas,

@Mike Innanzitutto ho detto che il modo in cui lo sta facendo è corretto e che potrebbe provare a provare / catturare principalmente. Non sono sicuro del perché tu (o qualcun altro) mi abbia dato un voto in giù quando stavo concordando con Hans solo fornendo un'altra risposta per la quale non mi aspettavo di ottenere un assegno. Questo non è proprio giusto, quindi dire che l'alternativa è sbagliata senza fornire alcuna prova su come un'eccezione che può essere rilevata dal processo AppDomain UnhandledException che un tentativo / cattura in Main non può catturare. Trovo maleducato dire che qualcosa non va senza provare perché è sbagliato, solo dire che è così, non lo rende.
Rodney S. Foley,

5
Ho pubblicato l'esempio che stai chiedendo. Per favore, sii responsabile e rimuovi i tuoi voti negativi irrilevanti dalle vecchie risposte di Mike, se non l'hai fatto. (Nessun interesse personale, semplicemente non mi piace vedere un simile abuso del sistema.)
BlueMonkMN

3
Eppure continui a giocare allo stesso "gioco" che fa, solo in modo peggiore perché è pura ritorsione, non basata sulla qualità di una risposta. Questo non è un modo per risolvere il problema, solo per peggiorare le cose. È particolarmente negativo quando ti vendi contro qualcuno che ha anche avuto una legittima preoccupazione per la tua risposta (come ho dimostrato).
BlueMonkMN,

3
Oh, aggiungerei anche che il voto negativo non è destinato a persone che "sono un completo idiota o violano le regole", ma piuttosto a giudicare la qualità di una risposta. Mi sembra che le risposte al voto negativo al fine di "commentare" la persona che le fornisce sia un abuso molto più ampio delle risposte al voto negativo basate sul contenuto della risposta stessa, indipendentemente dal fatto che quel voto sia accurato o meno. Non prenderlo / renderlo così personale.
BlueMonkMN,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.