2009-06-25 6 views
59

Ho una piccola applicazione WinForms che utilizza un oggetto BackgroundWorker per eseguire un'operazione di lunga durata.Eccezioni non gestite in BackgroundWorker

L'operazione in background genera eccezioni occasionali, in genere quando qualcuno ha un file aperto che viene ricreato.

Indipendentemente dal fatto che il codice venga eseguito dall'IDE o non .NET, viene visualizzata una finestra di dialogo di errore che informa l'utente che si è verificata un'eccezione non gestita. Compilare il codice utilizzando la configurazione di rilascio non cambia neanche questo.

Secondo MSDN:

Se l'operazione solleva un'eccezione che il codice non gestisce, il BackgroundWorker cattura l'eccezione e lo passa al gestore di eventi RunWorkerCompleted, dove è esposta la proprietà Error System.ComponentModel .. ::. RunWorkerCompletedEventArgs. Se si esegue il debugger di Visual Studio, il debugger si interromperà al punto nel gestore di eventi DoWork in cui è stata sollevata l'eccezione non gestita.

Mi aspetto che queste eccezioni vengano generate occasionalmente e vorrei gestirle nell'evento RunWorkerCompleted piuttosto che in DoWork. Il mio codice funziona correttamente e l'errore viene gestito correttamente all'interno dell'evento RunWorkerCompleted ma non riesco, per tutta la vita, a capire come interrompere la finestra di dialogo di errore .NET che si lamenta del verificarsi dell'eccezione non gestita.

Non si presume che lo sfondo abbia rilevato automaticamente l'errore in questione? Non è quello che afferma la documentazione MSDN? Che cosa devo fare per informare .NET che questo errore è gestito da mentre ancora consente all'eccezione di propagarsi nella proprietà Error di RunWorkerCompletedEventArgs?

risposta

104

Quello che stai descrivendo non è il comportamento definito di BackgroundWorker. Stai facendo qualcosa di sbagliato, sospetto.

Ecco un piccolo esempio che dimostra BackgroundWorker mangia eccezioni in DoWork, e le rende disponibili a voi in RunWorkerCompleted:

var worker = new BackgroundWorker(); 
worker.DoWork += (sender, e) => 
    { 
     throw new InvalidOperationException("oh shiznit!"); 
    }; 
worker.RunWorkerCompleted += (sender, e) => 
    { 
     if(e.Error != null) 
     { 
      MessageBox.Show("There was an error! " + e.Error.ToString()); 
     } 
    }; 
worker.RunWorkerAsync(); 

Le mie capacità di debug psichiche stanno rivelando il problema per me: Si accede e.Risultare nel gestore RunWorkerCompleted - se esiste un errore e.Error, è necessario gestirlo senza accedere a e.Result. Ad esempio, il codice seguente è cattivo, cattivo, cattivo, e un'eccezione in fase di esecuzione:

var worker = new BackgroundWorker(); 
worker.DoWork += (sender, e) => 
    { 
     throw new InvalidOperationException("oh shiznit!"); 
    }; 
worker.RunWorkerCompleted += (sender, e) => 
    { 
     // OH NOOOOOOOES! Runtime exception, you can't access e.Result if there's an 
     // error. You can check for errors using e.Error. 
     var result = e.Result; 
    }; 
worker.RunWorkerAsync(); 

Ecco una corretta attuazione del gestore di eventi RunWorkerCompleted:

private void RunWorkerCompletedHandler(object sender, RunWorkerCompletedEventArgs e) 
{ 
    if (e.Error == null) 
    { 
     DoSomethingWith(e.Result); // Access e.Result only if no error occurred. 
    } 
} 

VOILA, hai vinto' t ricevere eccezioni di runtime.

+0

+1 Buon punto. Il mio esempio ha evidenziato le specifiche di gestione dell'errore, ma il mio codice causerebbe effettivamente un'altra eccezione se un'eccezione non veniva mai colpita nel metodo DoWork. –

+0

Mi permetto di dissentire .... Sto anche cercando di capire come la classe BGW si diverte ... a volte la mia app mi consente di accedere alla mia area utente su Win7 ea volte no. Quando indago ulteriormente, ho scoperto che l'accesso negato è perché la cartella non esiste. Trovo che a volte l'errore viene generato nella mia app e talvolta no. – IbrarMumtaz

+0

@Ibrar, stai sempre verificando l'errore e.Error nel tuo gestore di eventi RunWorkerCompleted? Assicurati di controllare prima di fare qualsiasi cosa. Tutte le eccezioni rilevabili saranno riportate qui. –

1

[Modifica]

Judah ha un ottimo punto. Il mio esempio ha evidenziato le specifiche di gestione dell'errore, ma il mio codice causerebbe effettivamente un'altra eccezione se un'eccezione non veniva mai colpita nel metodo DoWork. Questo esempio è OK a causa del fatto che stiamo mostrando in modo specifico le capacità di gestione degli errori di BackgroundWorker. Tuttavia, se non stai controllando il parametro error su null, questo potrebbe essere il tuo problema.

[/ Modifica]

non vedo gli stessi risultati. Puoi pubblicare un piccolo codice? Ecco il mio codice.

private void Form1_Load(object sender, EventArgs e) 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    worker.RunWorkerAsync(); 
} 

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    // Will cause another exception if an exception didn't occur. 
    // We should be checking to see if e.Error is not "null". 
    textBox1.Text = "Error? " + e.Error; 
} 

void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     if (i < 5) 
     { 
      Thread.Sleep(100); 
     } 
     else 
     { 
      throw new Exception("BOOM"); 
     } 
    } 
} 

uscita del programma:

errore? System.Exception: BOOM a BackgroundException.Form1.worker_DoWork (Object mittente, DoWorkEventArgs e) in D: \ workspace \ Sandbox \ BackgroundException \ BackgroundException \ Form1.cs: linea a System.ComponentModel.BackgroundWorker.OnDoWork 43 (DoWorkEventArgs e) a System.ComponentModel.BackgroundWorker.WorkerThreadStart (Object argomento)

Un interessante articolo che sembra simile alla tua domanda. Ha una sezione sulla gestione delle eccezioni.

http://www.developerdotstar.com/community/node/671

34

vorrei aggiungere alla MSDN text:

Se l'operazione solleva un'eccezione che il codice non gestisce, il BackgroundWorker cattura l'eccezione e lo passa al gestore di eventi RunWorkerCompleted, dove è esposto come il Proprietà Error di System.ComponentModel .. ::. RunWorkerCompletedEventArgs. Se si esegue il debugger di Visual Studio, il debugger si interromperà nel punto nel gestore eventi DoWork in cui è stata sollevata l'eccezione non gestita.

... E il debugger riporterà l'eccezione come "~ Eccezione è stata gestita dal codice utente"

Soluzione: Non eseguire nel debugger e funziona come previsto: Exception catturato in e.error.

+3

Grazie per quello. Quello che vorrei davvero sapere è perché mai si comporta così. Rende impossibile eseguire il debug della gestione delle eccezioni dell'evento 'RunWorkerCompleted'. –

+0

Se avessero appena messo quel bit sul debugger nella documentazione ... Così vero. Grazie. – andersop

+1

@yu_ominae: Non impossibile. Ogni volta che il debugger si interrompe sull'eccezione, puoi effettivamente premere F5 (Continua) per consentire a BackgroundWorker di rilevare l'eccezione e l'esecuzione continuerà come previsto in 'RunWorkerCompleted'. –

2

Questa è una vecchia domanda, ma l'ho trovata mentre cercavo su Google gli stessi sintomi. Pubblicando questo nel caso in cui qualcun altro lo trovi per lo stesso motivo.

La risposta di Giuda è corretta, ma non è l'unica ragione per cui può apparire la finestra di dialogo "Eccezione non gestita nel codice utente". Se viene generata un'eccezione dall'interno di un costruttore sul thread in background, quell'eccezione causerà immediatamente la finestra di dialogo e non verrà passata all'evento RunWorkerCompleted. Se sposti il ​​codice offensivo al di fuori di qualsiasi costruttore (con qualsiasi altro metodo), funziona come previsto.

+0

Ho provato a lanciare un'eccezione in un costruttore e il 'RunWorkerCompleted' lo ha catturato. Forse l'hai provato con un'applicazione Console? Secondo [questa risposta] (http://stackoverflow.com/a/10299830/939213), le app della console si comportano diversamente. – ispiro

+0

È possibile. Le eccezioni specifiche con cui stavo riscontrando questo problema mi davano errori "Il tipo initializer per xyz ha gettato un'eccezione", non sono sicuro di quali circostanze specifiche produrrebbero ecc. – Rich

+0

Thread precedente ma ricordavo il resto di questa storia - TypeInitializationException è quello che stavo pensando, e viene lanciato se si verifica un'eccezione all'interno di un costruttore * static *. – Rich

0

Ho avuto lo stesso problema e stavo già applicando la risposta di Giuda prima ho trovato questo argomento dopo alcuni googlando.

Bene, la risposta di Giuda è parzialmente corretta. Ho trovato una risposta migliore here

Il debugger sta funzionando bene, se si esegue l'applicazione in "condizioni del mondo reale", RunWorkerCompleted gestisce l'eccezione come previsto e anche il comportamento dell'applicazione è previsto.

Spero che questa risposta aiuti.

+0

[Questa risposta precedente] (http://stackoverflow.com/a/5244908/1497596) riguarda ciò che accade nel debugger di Visual Studio. – DavidRR