结构化异常处理 (C/C++)
尽管 Windows 和 Visual C++ 支持结构化异常处理 (SEH),建议您使用 ISO C++ 标准异常处理,因为它使代码更具可移植的灵活。 但是,现有代码或特定类型的程序,可能仍然必须使用 SEH。
语法
try-except-statement :
__try compound-statement
__except ( expression ) compound-statement
备注
用 SEH,如果执行意外终止,可以确保诸如内存块和文件资源是正确的。 也可以处理特定的问题,例如,内存不足,当使用不依赖于 goto 语句或返回代码精心设计的测试结构简洁的代码。
本文中引用的 try-except和try-finally是基于对 C 语言的扩展。 它们支持SEH通过使应用程序去获得程序的控制,否则将终止执行活动。 虽然SEH使用 C++ 源文件,它不是对 C++ 专门设计的。 如果用 /EH 选项来在C++程序中SEH和某些修改符一起使用,那么本地对象的析构函数将被调用,但其他执行行为可能不是你所期望的。(对于图,请参见本文后面的 示例。)在大多数情况下,建议使用Visual C++ 支持的基于标准 C++ 异常处理 而不是SEH。 使用 C++ 异常处理,可以确保代码具有更强的可移植性,因此,可以用任何类型的异常处理。
如果 C 模块中使用了SEH,可以 C++ 异常处理的 C++ 模块混合使用。 有关信息,请参见异常处理差异。
有两种SEH结构:
处理程序的这两种类型是不同的,但是它们通过“展开堆栈”的程序紧密相关。异常发生时,Windows 将查找最新安装的当前处于活动状态的异常处理程序。 该处理程序可以执行三种操作之一:
不能识别异常和其他处理程序。
异常被识别出来,但被驳回。
识别和处理该异常。
该函数发生异常时,识别该异常的异常处理程序可能不在运行的函数中。 在某些情况下,它可能位于函数堆栈上更高层的地方。 当前运行的函数,其他所有在堆栈帧上的函数都被终止。 在此过程中,堆栈 “展开”:即,终止函数的局部变量从堆栈中清除,除非它们是 static。
在它展开堆栈时,操作系统调用你写的每个处理函数终止的函数。 使用终止处理,可以清除资源,否则将会因异常终止而继续开启。 如果输入了临界区,可以在终止处理程序中退出。 如果该程序将关闭,可以执行其他管理任务如关闭和移除临时文件。
有关更多信息,请参见:
示例
如上文所述,如果你在C++程序中使用了SEH,并且用 /EH 选项用某种编辑器 ,如 /EHsc 和 /EHa 本地对象的析构函数将被调用。 但是,行为在执行时可能不是预期无论是否同时使用了 C++ 异常。 下面的示例演示这些行为差异。
#include <stdio.h>
#include <Windows.h>
#include <exception>
class TestClass
{
public:
~TestClass()
{
printf("Destroying TestClass!\r\n");
}
};
__declspec(noinline) void TestCPPEX()
{
#ifdef CPPEX
printf("Throwing C++ exception\r\n");
throw std::exception("");
#else
printf("Triggering SEH exception\r\n");
volatile int *pInt = 0x00000000;
*pInt = 20;
#endif
}
__declspec(noinline) void TestExceptions()
{
TestClass d;
TestCPPEX();
}
int main()
{
__try
{
TestExceptions();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf("Executing SEH __except block\r\n");
}
return 0;
}
如果使用 /EHsc 编译此代码,但本地测试控件 CPPEX 是未定义的, TestClass 不执行析构函数,并输出如下所示:
如果使用 /EHsc 编译代码,并使用 /DCPPEX 定义 CPPEX (以便 C++ 异常的抛出), TestClass 析构函数执行和输出如下所示:
如果使用 /EHa 编译代码,TestClass 析构函数执行不论是否有异常发生使用 std::throw 或使用SEH触发异常 (不论是否定义CPPEX )。 输出如下所示:
有关详细信息,请参阅/EH(异常处理模型)。