关于C++中的结构化异常(SEH),我应该了解些什么?

53

每个 C++ 开发者都应该了解关于 Structured Exceptions 的重要点是什么?

5个回答

49

它们是Win32与Unix信号相当的东西,可以捕获诸如访问冲突、非法指令、除零等CPU异常。

使用正确的编译器选项(对于Visual C++是 /EHa),C++异常会使用与堆栈展开相同的机制,并且C++(用户)异常和SEH(操作系统)异常都能正常处理。

与C++异常不同,SEH没有类型,但所有SEH共享相同的数据结构,其中包含异常代码(原因)以及有关故障代码和CPU寄存器在故障时所保存内容的其他信息。有关此信息,请参见GetExceptionCodeGetExceptionInformation

此外,SEH还具有“first-chance”处理功能,允许您在展开销毁所有局部变量之前记录或以其他方式处理异常。


1
请注意,缺省情况下不再处理 SEH 异常(如 C++ 异常),因为 SEH 异常不能像 C++ 异常一样安全地处理。SEH 异常通常是访问冲突之类的问题,在此类情况下尝试进行 C++ 展开是不可能的,并且可能导致程序终止。 - Billy ONeal
5
将访问违规撤销并不会本质上不安全。当然,如果访问违规是由于内部数据结构的损坏引起的(特别是影响调用堆栈信息的堆栈溢出),那么撤销很可能会失败,但许多SEH异常实际上并不反映堆栈损坏,这一点也许占了大部分,即使不是全部。 - Ben Voigt
请阅读https://dev59.com/Rm855IYBdhLWcg3wUSgW,了解使用SEH处理被忽略的省略号异常(例如catch(...) {})可能产生的不良副作用。 - Markus Kull
1
对于C++开发人员非常重要:不要混合使用C++异常处理和SEH(除非您绝对知道自己在做什么,并且有充分的理由)。 - IInspectable

37

我最近遇到了一个问题,这个问题间接由SEH引起的,具体地说,是因为SEH的一个特性,我认为每个开发人员都应该知道:

当使用SEH时,析构函数不会被调用,因此如果你在析构函数中有清理代码,它将不会被清理。

我们的问题是由一个关键段包装器对象引起的,它在构造函数中带有Lock,在析构函数中带有Unlock。

我们遇到了死锁情况,无法弄清楚原因,在经过一周的代码查找、转储和调试后,我们终于明白了其中的原因:有一个异常被COM处理,导致临界区保持锁定状态。 我们在VS项目属性中更改了一个编译标志,告诉它即使对于SEH也要运行析构函数,这解决了问题。

因此,即使您的代码没有使用SEH,您可能正在使用一个使用SEH(如COM)的库,这可能导致意外的行为。


25

Win32™结构化异常处理深入解析是让您快速掌握SEH的参考文章,13年后仍然是最好的。

在MSDN上有一个专门介绍SEH与C++异常处理差异的主题。

当讨论SEH时,C++开发人员需要知道一些事情:

编写C/C++ SEH异常处理程序

__try 
{
   // guarded code
}
__except ( expression )
{
   // exception handler code
}

这不是C++异常处理,而是MS特定的扩展,用于直接钩入SEH。它与常见的C++异常有很大不同。要使用它们,您需要对SEH有很好的理解。

编写C/C++ SEH终止处理程序:Termination Handlers

__try {
   // guarded code
}
__finally {
   // termination code
}

和SEH处理程序一样,不要将其与C++异常语义混淆。您需要对SEH有很好的理解。

_set_se_translator:这是将SEH异常转换为C++类型异常的函数,当使用异步异常/EHa时。

最后,个人意见:C++开发人员应该了解SEH吗?在第一个新手.ecxr之后,您就会明白,当推力到极限时,C++异常只是为您方便而提供的假象。唯一发生的事情就是SEH。


11
__finally没有(expression) - Abyx
你的“Crash Course”链接已经失效。这里提供一个替代链接: https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/Win32SEH.chm/Win32SEH.htm - Elaskanator
当前链接:https://www-user.tu-chemnitz.de/~heha/hsn/chm/Win32SEH.chm/ - zhenguoli

25

他们应该知道,它们不是标准C++的一部分-它们是微软的发明,可以用于其他语言。


1
重要的一点是知道何时使用SEH,何时使用标准C++异常。首先,只选择一个系统-混合使用系统往往会有问题,需要深入了解两个系统才能实现良好。其次,在高层次上,SEH不仅限于C ++,而标准C ++异常也不仅限于Windows。如果这不决定你的决定,请选择标准异常,除非它们不足(有关SEH可以做什么的更多详细信息,请参见其他答案)。
引用自 Microsoft的文档(日期为08/13/2018)支持这个结论。

结构化异常处理(SEH)是Microsoft对C的扩展,用于优雅地处理某些异常代码情况,例如硬件故障。尽管Windows和Microsoft C ++支持SEH,但我们建议您使用ISO标准C ++异常处理,因为它使您的代码更具可移植性和灵活性。尽管如此,为了维护现有代码或特定类型的程序,您仍然可能需要使用SEH。

为什么扩展程序的作者建议在大多数情况下不使用它?可能是因为该扩展程序是为C语言编写的,而当前环境是C ++。这些语言相似,因此将SEH移植到C ++作为副产品可能很容易,尽管只有“特定类型的程序”才能真正受益。(或者可能出于其他原因;也许移植是在C ++标准化之前开始的。历史变得复杂。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接