使用托管异常中的基本概念

本主题讨论托管应用程序中的异常处理。 即,使用 /clr 编译器选项编译的应用程序。

本主题内容

备注

如果使用 /clr 选项进行编译,则可以处理 CLR 异常,同时标准 Exception 类提供许多有用的方法来处理 CLR 异常,建议将其用作用户定义的异常类的基类。

/clr 下不支持捕获派生自接口的异常类型。 此外,公共语言运行时不允许捕获堆栈溢出异常;堆栈溢出异常将终止进程。

有关托管和非托管应用程序中异常处理差异的详细信息,请参阅 C++ 托管扩展下的异常处理行为的差异

在 /clr 下引发异常

将 C++ 引发表达式扩展为引发 CLR 类型的句柄。 以下示例创建一个自定义异常类型,然后引发该类型的实例:

// 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;
}

在引发值类型之前必须将其装箱:

// clr_exception_handling_2.cpp
// compile with: /clr /c
value struct MyValueStruct {
   int i;
};

void GlobalFunction() {
   MyValueStruct v = {11};
   throw (MyValueStruct ^)v;
}

CLR 扩展的 Try/Catch 块

同一个 try/catch 块结构可用于捕获 CLR 异常和本机异常:

// 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 );
      }
   }
}

输出

In 'catch(CMyClass& catchC)'
2
In 'catch(MyStruct^ catchException)'
11

C++ 对象的展开顺序

在引发函数和处理函数之间的运行时堆栈上可能存在析构函数的所有 C++ 对象都会展开。 由于 CLR 类型是在堆上分配的,因此展开不适用于此类型。

对于引发的异常,其事件顺序如下所示:

  1. 运行时在堆栈上查找相应的 catch 子句(如果是 SEH,则为 SEH 的 except 筛选器),以捕获异常。 首先按词法顺序搜索 Catch 子句,然后动态向下搜索调用堆栈。

  2. 找到正确的处理程序后,会将堆栈展开到该点。 对于堆栈上的每个函数调用,都将从最深的嵌套开始向外析构其本地对象并最终执行块。

  3. 展开堆栈后,将执行 catch 子句。

捕获非托管类型

引发非托管对象类型时,将使用 SEHException 类型的异常包装该对象类型。 搜索相应的 catch 子句时,有两种可能性。

  • 如果遇到本机 C++ 类型,则会解包异常,并将其与遇到的类型进行比较。 通过比较能够以正常方式捕获本机 C++ 类型。

  • 但是,如果首先检查 SEHException 类型的 catch 子句或其任何基类,则子句将截获异常。 因此,应将用于捕获本机 C++ 类型的所有 catch 子句置于任何 CLR 类型的 catch 子句之前。

请注意:

catch(Object^)

catch(...)

将同时捕获任何引发的类型,包括 SEH 异常。

如果 catch(Object^) 捕获了非托管类型,它不会销毁引发的对象。

引发或捕获非托管异常时,建议使用 /EHsc 编译器选项,而不使用 /EHs 或 /EHa

另请参阅

异常处理
safe_cast
异常处理