异常处理与MiniDump详解(3) SEH(Structured Exception Handling)
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
一、 综述
SEH--Structured Exception Handling,是Windows操作系统使用的异常处理方式。
对于SEH,有点需要说明的是,SEH是属于操作系统的特性,不为特定语言设计,但是实际上,作为操作系统的特性,几乎就等同与面向C语言设计,这点很好理解,就像Win32 API,Linux下的系统调用,都是操作系统的特性吧,实际还是为C做的。但是,作为为C语言设计的东西,实际上可调用的方式又多了,汇编,C++对于调用C语言的接口都是比较方便的。
二、 基础篇
还是简单介绍一下SEH的使用,但是不准备太详细的介绍了,具体的详细介绍见参考中提及的书目。关于SEH的基本应用,《Windows核心编程》绝对是最佳读物(其实个人一直认为《Windows核心编程》是Windows编程领域必看的第二本书,第一本是《Programming Windows》。关于SEH更深入的一点的知识可能就要参考一些能用汇编讲解的书籍了,《Windows用户态程序高效排错》算是其中讲的不错的一本。
首先,SEH也有像C++异常一样的语法,及类try-catch语法,在SEH中为__try-except语法,抛出异常从throw改为RaiseException,在MSDN中的语法描述为:
__try
{
// guarded code
}
__except ( expression )
{
// exception handler code
}
见一个实际使用的例子:
例1:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
RaiseException(0, 0, 0, NULL);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
cout <<"Exception Raised." <<endl;
}
cout <<"Continue running" <<endl;
}
这可能是最简单的SEH的例子了,输出如下:
Exception Raised.
Continue running
这个例子和普通C++异常的try-catch类似,也很好理解。只不过catch换成了except。
因为C语言没有智能指针,那么就不能缺少finally的异常语法,与JAVA,Python等语言中的也类似,(这是C++中没有的)finally语法的含义就是无论如何(不管是正常还是异常),此句总是会执行,常用于资源释放。
例2:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
__try
{
__try
{
RaiseException(0, 0, 0, NULL);
}
__finally
{
cout <<"finally here." <<endl;
}
}
__except(1)
{
}
__try
{
__try
{
int i;
}
__finally
{
cout <<"finally here." <<endl;
}
}
__except(1)
{
}
cout <<"Continue running" <<endl;
getchar();
}
这个实例看起来过于奇怪,因为没有将各个try-finally放入独立的模块之中,但是说明了问题:
1. finally的语句总是会执行,无论是否异常finally here总是会输出。
2. finally仅仅是一条保证finally语句执行的块,并不是异常处理的handle语句(与except不同),所以,假如光是有finally语句块的话,实际效果就是异常会继续向上抛出。(异常处理过程也还是继续)
3. finally执行后还可以用except继续处理异常,但是SEH奇怪的语法在于finally与except无法同时使用,不然会报编译错误。
如下例:
__try
{
RaiseException(0, 0, 0, NULL);
}
__except(1)
{
}
__finally
{
cout <<"finally here." <<endl;
}
VS2005会报告
error C3274: __finally 没有匹配的try
这点其实很奇怪,难道因为SEH设计过于老了?-_-!因为在现在的语言中finally都是允许与except(或类似的块,比如catch)同时使用的。C#,JAVA,Python都是如此,甚至在MS为C++做的托管扩展中都是允许的。如下例:(来自MSDN中对finally keyword [C++]的描述)
using namespace System;
ref class MyException: public System::Exception{};
void ThrowMyException() {
throw gcnew MyException;
}
int main() {
try {
ThrowMyException();
}
catch ( MyException^ e ) {
Console::WriteLine( "in catch" );
Console::WriteLine( e->GetType() );
}
finally {
Console::WriteLine( "in finally" );
}
}
当你不习惯使用智能指针的时候常常会觉得这样会很好用。关于finally异常语法和智能指针的使用可以说是各有长短,这里提供刘未鹏的一种解释,(见参考5的RAII部分,文中比较的虽然是JAVA,C#,但是实际SEH也是类似JAVA的)大家参考参考。
SEH中还提供了一个比较特别的关键字,__leave,MSDN中解释如下
Allows for immediate termination of the __try block without causing abnormal termination and its performance penalty.
简而言之就是类似goto语句的抛出异常方式,所谓的没有性能损失是什么意思呢?看看下面的例子:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
int i = 0;
__try
{
__leave;
i = 1;