Structured Exception Handling (C/C++)
结构化异常处理 (SEH) 是 Microsoft 对 C 和 C++ 语言的一个扩展,用于适当地处理某些异常代码情况,例如硬件故障。 尽管 Windows 和 Microsoft C++ 支持 SEH,但我们建议在 C++ 代码中使用 ISO 标准 C++ 异常处理。 它提高了代码的可移植性和灵活性。 但是,为了维护现有代码,或者对于特定类型的程序,你仍可能必须使用 SEH。
Microsoft 专用:
语法
try-except-statement
:
__try
compound-statement
__except
(
filter-expression
)
compound-statement
try-finally-statement
:
__try
compound-statement
__finally
compound-statement
备注
使用 SEH,你可以确保在执行意外终止时,可以正确地释放资源(如内存块和文件)。 你还可以处理特定问题,例如,没有足够的内存,方法是使用简洁的结构化代码,该代码不依赖于 goto
语句或返回代码的详尽测试。
这篇文章中引用的 try-except
和 try-finally
语句是 C 和 C++ 语言的 Microsoft 扩展。 它们通过使应用程序可以在事件后获得对程序的控制(否则事件将终止执行)来支持 SEH。 尽管 SEH 使用 C++ 源文件,但它并不是专为 C++ 设计的。 如果通过使用 /EHa
或 /EHsc
选项,在编译的 C++ 程序中使用 SEH,则会调用本地对象的析构函数,但其他执行行为可能不是你所预期的。 有关说明,请参阅本文后面的示例。 在大多数情况下,建议不要使用 ISO 标准 C++ 异常处理。 使用 C++ 异常处理可以确保你的代码更具可移植性,并且你可以处理任何类型的异常。
如果你有使用 SEH 的 C 代码,可以将它与使用 C++ 异常处理的 C++ 代码混合使用。 有关信息,请参阅在 C++ 中处理结构化异常。
有两种 SEH 机制:
异常处理程序或
__except
块,可以根据filter-expression
值响应或消除异常。 有关详细信息,请参阅try-except
语句。终止处理程序或
__finally
块,无论异常是否导致终止,都始终调用这两者。 有关详细信息,请参阅try-finally
语句。
这两种类型的处理程序是不同的,但会通过称为“展开堆栈”的过程紧密关联。 发生结构化异常时,Windows 将查找当前处于活动状态的最新安装的异常处理程序。 该处理程序可以执行以下三个操作之一:
无法识别异常并将控件传递给其他处理程序 (
EXCEPTION_CONTINUE_SEARCH
)。识别异常,但将其消除 (
EXCEPTION_CONTINUE_EXECUTION
)。识别异常并处理它 (
EXCEPTION_EXECUTE_HANDLER
)。
识别异常的异常处理程序可能不在异常发生时正在运行的函数中。 它可能在堆栈上高得多的函数中。 当前正在运行的函数和堆栈帧上的所有其他函数都将终止。 在此过程中,堆栈会展开。 也就是说,已终止函数的局部非静态变量会从堆栈中清除。
当它展开堆栈时,操作系统将调用你为每个函数编写的任何终止处理程序。 通过使用终止处理程序清理资源,否则资源将由于异常终止而保持打开状态。 如果你已输入了关键部分,可以在终止处理程序中退出它。 程序将要关闭时,你可以执行其他维护任务,如关闭和删除临时文件。
后续步骤
示例
正如前文所述,如果你在 C++ 程序中使用 SEH,并通过使用 /EHa
或 /EHsc
选项对其进行编译,则会调用本地对象的析构函数。 但是,如果你也正在使用 C++ 异常,则执行过程中的行为可能不是你所预期的。 此示例演示了这些行为差异。
#include <stdio.h>
#include <Windows.h>
#include <exception>
class TestClass
{
public:
~TestClass()
{
printf("Destroying TestClass!\n");
}
};
__declspec(noinline) void TestCPPEX()
{
#ifdef CPPEX
printf("Throwing C++ exception\n");
throw std::exception("");
#else
printf("Triggering SEH exception\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\n");
}
return 0;
}
如果你使用 /EHsc
来编译此代码,但未定义本地测试控件宏 CPPEX
,则不运行 TestClass
析构函数。 输出如下所示:
Triggering SEH exception
Executing SEH __except block
如果你使用 /EHsc
来编译代码,且使用 /DCPPEX
定义了 CPPEX
(以致引发 C++ 异常),则析构函数 TestClass
运行,输出如下所示:
Throwing C++ exception
Destroying TestClass!
Executing SEH __except block
如果使用 /EHa
编译代码,TestClass
析构函数会执行,不管是使用标准 C++ throw
表达式还是通过使用 SEH 引发了异常。 也就是说,不管是否定义了 CPPEX
。 输出如下所示:
Throwing C++ exception
Destroying TestClass!
Executing SEH __except block
有关详细信息,请参阅 /EH
(异常处理模型)。
结束 Microsoft 专用
另请参阅
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈