C++ EH Exception是Windows系统VC++里对c++语言的throw的分类和定义,它的代码就是0xe06d7363。在VC++里其本质也是SEH结构化异常机制。在我们分析用户崩溃的例子中经常会遇到它。一般情况下,遇到它,是我们代码里用throw抛出异常后没有处理导致程序崩溃。下面分析一下它的原理。

我们借助一段代码来跟踪和分析

class MyException
{
public:
int nErr;
char *szMessage;
public:
MyException(void)
:nErr()
, szMessage(NULL)
{ } MyException(int nerr,char *szMess)
:nErr(nerr)
, szMessage(szMess)
{ } ~MyException(void)
{ }
};
int _tmain(int argc, _TCHAR* argv[])
{
try
    {
        MyException me(1, "test exception");
        throw me;
    }
    catch (MyException me1)
    {
        printf("err=%s\n",me1.szMessage);
    }
}

将上述代码在VS2013里编译调试,转到汇编

:     try
: {
mov dword ptr [ebp-],
: MyException me(, "test exception");
0136179E push 1367858h
013617A3 push
013617A5 lea ecx,[ebp-1Ch]
013617A8 call MyException::MyException (013610D2h)
013617AD mov dword ptr [ebp-104h],eax
013617B3 mov byte ptr [ebp-],
: throw me;
013617B7 mov eax,dword ptr [ebp-1Ch]
013617BA mov dword ptr [ebp-0FCh],eax
013617C0 mov ecx,dword ptr [ebp-18h]
013617C3 mov dword ptr [ebp-0F8h],ecx
013617C9 push 1369084h
013617CE lea edx,[ebp-0FCh]
013617D4 push edx
013617D5 call __CxxThrowException@8 (0136111Dh)
: }

我们可以看到,throw 首先通过下面的代码

013617B7  mov         eax,dword ptr [ebp-1Ch]
013617BA mov dword ptr [ebp-0FCh],eax
013617C0 mov ecx,dword ptr [ebp-18h]
013617C3 mov dword ptr [ebp-0F8h],ecx

复制了一份异常对象MyException me,然后取了这份拷贝的地址作为参数传给了__CxxThrowException函数,同时将异常类型信息也传递了过去

013617CE  lea         edx,[ebp-0FCh]
013617D4 push edx

接着调用了__CxxThrowException函数,我们进入看看

下面是改函数的代码

/////////////////////////////////////////////////////////////////////////////
//
// _CxxThrowException - implementation of 'throw'
//
// Description:
// Builds the NT Exception record, and calls the NT runtime to initiate
// exception processing.
//
// Why is pThrowInfo defined as _ThrowInfo? Because _ThrowInfo is secretly
// snuck into the compiler, as is the prototype for _CxxThrowException, so
// we have to use the same type to keep the compiler happy.
//
// Another result of this is that _CRTIMP can't be used here. Instead, we
// synthesisze the -export directive below.
//
// Returns:
// NEVER. (until we implement resumable exceptions, that is)
// // We want double underscore for CxxThrowException for ARM CE only
__declspec(noreturn) extern "C" void __stdcall
#if !defined(_M_ARM) || defined(_M_ARM_NT)
_CxxThrowException(
#else
__CxxThrowException(
#endif
void* pExceptionObject, // The object thrown
_ThrowInfo* pThrowInfo // Everything we need to know about it
) {
EHTRACE_ENTER_FMT1("Throwing object @ 0x%p", pExceptionObject); static const EHExceptionRecord ExceptionTemplate = { // A generic exception record
EH_EXCEPTION_NUMBER, // Exception number
EXCEPTION_NONCONTINUABLE, // Exception flags (we don't do resume)
NULL, // Additional record (none)
NULL, // Address of exception (OS fills in)
EH_EXCEPTION_PARAMETERS, // Number of parameters
{ EH_MAGIC_NUMBER1, // Our version control magic number
NULL, // pExceptionObject
NULL,
#if _EH_RELATIVE_OFFSETS
NULL // Image base of thrown object
#endif
} // pThrowInfo
};
EHExceptionRecord ThisException = ExceptionTemplate; // This exception ThrowInfo* pTI = (ThrowInfo*)pThrowInfo;
if (pTI && (THROW_ISWINRT( (*pTI) ) ) )
{
ULONG_PTR *exceptionInfoPointer = *reinterpret_cast<ULONG_PTR**>(pExceptionObject);
exceptionInfoPointer--; // The pointer to the ExceptionInfo structure is stored sizeof(void*) infront of each WinRT Exception Info. WINRTEXCEPTIONINFO** ppWei = reinterpret_cast<WINRTEXCEPTIONINFO**>(exceptionInfoPointer);
pTI = (*ppWei)->throwInfo; (*ppWei)->PrepareThrow( ppWei );
} //
// Fill in the blanks:
//
ThisException.params.pExceptionObject = pExceptionObject;
ThisException.params.pThrowInfo = pTI;
#if _EH_RELATIVE_OFFSETS
PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
ThisException.params.pThrowImageBase = ThrowImageBase;
#endif //
// If the throw info indicates this throw is from a pure region,
// set the magic number to the Pure one, so only a pure-region
// catch will see it.
//
// Also use the Pure magic number on Win64 if we were unable to
// determine an image base, since that was the old way to determine
// a pure throw, before the TI_IsPure bit was added to the FuncInfo
// attributes field.
//
if (pTI != NULL)
{
if (THROW_ISPURE(*pTI))
{
ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
}
#if _EH_RELATIVE_OFFSETS
else if (ThrowImageBase == NULL)
{
ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
}
#endif
} //
// Hand it off to the OS:
// EHTRACE_EXIT;
#if defined(_M_X64) && defined(_NTSUBSET_)
RtlRaiseException( (PEXCEPTION_RECORD) &ThisException );
#else
RaiseException( ThisException.ExceptionCode,
ThisException.ExceptionFlags,
ThisException.NumberParameters,
(PULONG_PTR)&ThisException.params );
#endif
}

可以看到,这个函数首先是创建了一个EHExceptionRecord 对象,其实对应的就是 SEH里的结构EXCEPTION_RECORD,并且给这个结构成员赋值。

在这里通过如下代码,0xe06d7363就赋值给ThisException.ExceptionCode

还有就是将ThrowInfo赋值给EHExceptionRecordThisException.params.pThrowInfo。_ThrowInfo 结构体定义如下:

typedef const struct _s__ThrowInfo
{
unsigned int attributes;
_PMFN pmfnUnwind;
int (__cdecl*pForwardCompat)(...);
_CatchableTypeArray *pCatachableTypeArray;
} _ThrowInfo;

结构体中重要的成员是_CatchableTypeArray。它包含了程序运行时抛出对象的类新信息(RTTI).
如果你的程序运行时抛出一个my_exception类型的对象,那么抛出的数据参数pCatchableTypeArray包含了两个重要子数据信息。一个是typeid(my_exception),另外一个是typeid(std::exception)。

在我们的例子里

紧接着就调用RaiseException函数进入了异常的分发过程。

综上,在C++ EH Exception 的异常里,EXCEPTION_RECORD结构填充如下:

ExceptionAddress: 异常地址 76d018a2 (KERNELBASE!RaiseException+0x00000062)
   ExceptionCode: 异常代码 e06d7363 (C++ EH exception)
  ExceptionFlags: 标志 00000001
NumberParameters: 3 or 4 64位时是4
   Parameter[0]: 19930520//魔数
   Parameter[1]: 00d5f690// 抛出的异常对象指针
   Parameter[2]: 00989084// 异常对象类型信息
   Parameter[3]: 0000000010000000 // 64位下模块句柄HINSTANCE

C++ EH Exception(0xe06d7363)----抛出过程的更多相关文章

  1. 异常CLRDBG_NOTIFICATION_EXCEPTION_CODE( 0x04242420)的抛出过程

    新建一个c#控制工程,就用自动生成的代码,不用补任何代码,如下: using System; using System.Collections.Generic; using System.Linq; ...

  2. 异常0xc000041d的抛出过程

    为了说明这个过程,我们必须写一个示例程序,如下: #include "stdafx.h" #include <tchar.h> #include <stdio.h ...

  3. EXCEPTION_HIJACK(0xe0434f4e)异常的抛出过程

    样例工程 在VS2013里新建一个C#控制台工程,写下如下代码: using System; using System.Collections.Generic; using System.Linq; ...

  4. C++ EH Exception(0xe06d7363)---捕获过程

    书接上文<C++ EH Exception(0xe06d7363)----抛出过程>,下面我们讲下,VC++是如何catch到异常且处理的. 我们知道,在VC++里,C++异常实现的底层机 ...

  5. springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...

  6. (转)spring异常抛出触发事务回滚策略

    背景:在面试时候问到事务方法在调用过程中出现异常,是否会传递的问题,平时接触的比较少,有些懵逼. spring异常抛出触发事务回滚策略 Spring.EJB的声明式事务默认情况下都是在抛出unchec ...

  7. Spring异常抛出触发事务回滚

    Spring.EJB的声明式事务默认情况下都是在抛出unchecked exception后才会触发事务的回滚 /** * 如果在spring事务配置中不为切入点(如这里的切入点可以定义成test*) ...

  8. JAVA异常的捕获与抛出原则

    在可能会出现exception的地方,要使用try-catch或者throws或者两者都要.我的判断依据是:如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-cat ...

  9. 【开发技术】java异常的捕获与抛出原则

    在可能会出现exception的地方,要使用try-catch或者throws或者两者都要.我的判断依据是:如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-cat ...

随机推荐

  1. Matlab 信号与系统课设 - BiRadio仿真电台

    BiRadio Matlab 信号与系统课设 - BiRadio仿真电台 Author : Benjamin142857 Date : 2018-12 Video : BiliBili Github ...

  2. 2019 年在 Raspberry Pi 「树莓派」上运行的 10 个操作系统推荐

    原文:2019 年在 Raspberry Pi 「树莓派」上运行的 10 个操作系统推荐 image Raspberry Pi** 是一款基于 ARM 的单板计算机,默认运行一款称为 Raspbian ...

  3. .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖

    原文:.NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖 我们有多种工具可以将程序集合并成为一个.打包成一个程序集可以避免分发程序的时候带上一堆依赖而出问题. ILMerge 可以用来 ...

  4. 复杂sql语句之单字段分类count计数和多字段count计数

    SELECT AF_DEPARTMENT, dept.FULLNAME, SUM(CASE AF_CLASSIFY WHEN THEN ELSE END) AS 'o_standard', (COUN ...

  5. C# 截取字符串方法总结

    第一种:根据单个分隔字符用split截取 string st="GT123_1"; string[] sArray=st.split("_"); //即可得到s ...

  6. 2019 朗玛信息java面试笔试题 (含面试题解析)

    本人3年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.朗玛信息等公司offer,岗位是Java后端开发,最终选择去了朗玛信息. 面试了很多家公司,感觉大部分公司考察的点 ...

  7. Linux:检查当前运行级别的五种方法

    运行级就是Linux操作系统当前正在运行的功能级别.存在七个运行级别,编号从0到6.系统可以引导到任何给定的运行级别.运行级别由数字标识. 每个运行级别指定不同的系统配置,并允许访问不同的进程组合.默 ...

  8. Vue学习之项目部分代码(十八)

    1.mian.js: // 入口文件 import Vue from "vue"; // 1.1导入路由 import VueRouter from "vue-route ...

  9. 英语caement水泥

    水泥石  又称净浆硬化体.是指 硬化后的水泥浆体,称为水泥石,在英语里是cement有时写作caement [1] ,是由胶凝体.未水化的水泥颗粒内核.毛细孔等组 成的非均质体. 中文名:水泥石 外 ...

  10. ABAP开发环境语法高亮的那些事儿

    关于SAP ABAP开发环境,Jerry之前写过几篇公众号文章: 那些年我用过的SAP IDE 不喜欢SAP GUI?那试试用Eclipse进行ABAP开发吧 使用Visual Studio Code ...