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. 一句话比较两种算法的优越性[蓝桥杯,LeetCode]

    动态规划 166 数学 155 字符串 146 树 120 哈希表 119 深度优先搜索 109 二分查找 79 贪心算法 64 双指针 59 广度优先搜索 54 栈 53 回溯算法 49 设计 41 ...

  2. 解决html 图片缓存问题

    <!--问题:上传一张图片,通过js更新src属性刷新图片使其即时显示时, 当img的src当前的url与上次地址无变化时(只更改图片,名称不变,不同图片名称相同)图片不变化(仍显示原来的图片) ...

  3. 【已解决】ArcMap的界面如何恢复默认设置

    解决方案:在C盘内搜索“Normal.mxt”,将它删除,然后重启ArcMap,即可.  效果图:

  4. HDU2476 String painter(DP)

    题目 String painter 给出两个字符串s1,s2.对于每次操作可以将 s1 串中的任意一个子段变成另一个字符.问最少需要多少步操作能将s1串变为s2串. 解析 太妙了这个题,mark一下. ...

  5. 视频网站大杂烩--HTML+CSS练手项目1【Frameset】

    [本文为原创,转载请注明出处] 技术[CSS+HTML]   布局[Frameset] -------------------------------------------------------- ...

  6. 【转】Vue.js——60分钟组件快速入门(上篇)

    文章作者:keepfool 文章出处:http://www.cnblogs.com/keepfool/ 组件简介 组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的 ...

  7. linux修改当前用户环境变量永久生效

    在linux环境中,修改当前用户环境变量,且永久生效的方法如下. 1,编辑~/.bash_profile文件 1 2 3 # Get the aliases and functions 4 if [ ...

  8. Tomcat启动堆栈异常

    近日部署项目到Tomcat,访问一直404. 于是查看 catalina.out 日志信息,发现如下信息 30-Jun-2019 16:38:03.642 SEVERE [main] org.apac ...

  9. 使用EwoMail搭建属于自己的个人邮件服务器——超详细图文教程

    版权声明:本文为CSDN博主「C_成」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/qq_41692307 ...

  10. map put相同的key

    Map添加相同的key 2018年09月09日 10:37:12 Airport_Le 阅读数:6479   HashMap是的key是不能重复的,如果有相同的key,最后一个key对应的value会 ...