Concetti di base per l'utilizzo delle eccezioni gestite
In questo argomento viene descritta la gestione delle eccezioni nelle applicazioni gestite. Ovvero un'applicazione compilata con l'opzione del compilatore /clr .
Contenuto dell'argomento
Osservazioni:
Se si esegue la compilazione con l'opzione /clr, è possibile gestire le eccezioni CLR e la classe standard Exception fornisce molti metodi utili per l'elaborazione di eccezioni CLR ed è consigliabile come classe di base per le classi di eccezioni definite dall'utente.
L'intercettazione dei tipi di eccezione derivati da un'interfaccia non è supportata in /clr. Common Language Runtime non consente inoltre di intercettare le eccezioni di overflow dello stack; un'eccezione di overflow dello stack terminerà il processo.
Per altre informazioni sulle differenze nella gestione delle eccezioni nelle applicazioni gestite e non gestite, vedere Differenze nel comportamento di gestione delle eccezioni in Estensioni gestite per C++.
Generazione di eccezioni in /clr
L'espressione throw C++ viene estesa per generare un handle a un tipo CLR. Nell'esempio seguente viene creato un tipo di eccezione personalizzato e quindi viene generata un'istanza di tale tipo:
// clr_exception_handling.cpp
// compile with: /clr /c
ref struct MyStruct: public System::Exception {
public:
int i;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
throw pMyStruct;
}
Un tipo di valore deve essere sottoposto a boxing prima di essere generato:
// clr_exception_handling_2.cpp
// compile with: /clr /c
value struct MyValueStruct {
int i;
};
void GlobalFunction() {
MyValueStruct v = {11};
throw (MyValueStruct ^)v;
}
Try/Catch Blocks for CLR Extensions
La stessa try
/catch
struttura a blocchi può essere usata per rilevare sia le eccezioni CLR che le eccezioni native:
// clr_exception_handling_3.cpp
// compile with: /clr
using namespace System;
ref struct MyStruct : public Exception {
public:
int i;
};
struct CMyClass {
public:
double d;
};
void GlobalFunction() {
MyStruct^ pMyStruct = gcnew MyStruct;
pMyStruct->i = 11;
throw pMyStruct;
}
void GlobalFunction2() {
CMyClass c = {2.0};
throw c;
}
int main() {
for ( int i = 1; i >= 0; --i ) {
try {
if ( i == 1 )
GlobalFunction2();
if ( i == 0 )
GlobalFunction();
}
catch ( CMyClass& catchC ) {
Console::WriteLine( "In 'catch(CMyClass& catchC)'" );
Console::WriteLine( catchC.d );
}
catch ( MyStruct^ catchException ) {
Console::WriteLine( "In 'catch(MyStruct^ catchException)'" );
Console::WriteLine( catchException->i );
}
}
}
Output
In 'catch(CMyClass& catchC)'
2
In 'catch(MyStruct^ catchException)'
11
Ordine di rimozione per oggetti C++
La rimozione avviene per tutti gli oggetti C++ con distruttori che possono trovarsi nello stack di runtime tra la funzione di throwing e la funzione di gestione. Poiché i tipi CLR vengono allocati nell'heap, la rimozione non si applica a tali tipi.
L'ordine degli eventi per un'eccezione generata è il seguente:
Il runtime esamina lo stack cercando la clausola catch appropriata o nel caso di SEH, un filtro ad eccezione di SEH, per intercettare l'eccezione. Le clausole Catch vengono cercate prima in ordine lessicale e quindi in modo dinamico verso il basso nello stack di chiamate.
Una volta trovato il gestore corretto, lo stack viene scollegato fino a quel punto. Per ogni chiamata di funzione nello stack, i relativi oggetti locali vengono distrutti e __finally blocchi vengono eseguiti, dalla maggior parte degli elementi annidati verso l'esterno.
Dopo che lo stack è stato sbloccato, viene eseguita la clausola catch.
Rilevamento di tipi non gestiti
Quando viene generato un tipo di oggetto non gestito, viene eseguito il wrapping con un'eccezione di tipo SEHException. Quando si cerca la clausola appropriata catch
, esistono due possibilità.
Se viene rilevato un tipo C++ nativo, l'eccezione viene annullata e confrontata con il tipo rilevato. Questo confronto consente di intercettato in modo normale un tipo C++ nativo.
Tuttavia, se viene esaminata prima una
catch
clausola di tipo SEHException o una delle relative classi di base, la clausola intercetta l'eccezione. Pertanto, è necessario inserire tutte le clausole catch che intercettano i tipi C++ nativi prima di qualsiasi clausola catch dei tipi CLR.
Si noti che:
catch(Object^)
e
catch(...)
intercetta entrambi qualsiasi tipo generato, incluse le eccezioni SEH.
Se un tipo non gestito viene intercettato da catch(Object^), non eliminerà definitivamente l'oggetto generato.
Quando si generano o rilevano eccezioni non gestite, è consigliabile usare l'opzione del compilatore /EHsc anziché /EHs o /EHa.