用于未处理异常错误的.NET框架清理工具
当你启动某些程序时,会收到与此错误类似的未处理异常错误:Unhandled e0434f4dh exception at 7c81eb33h。此问题是由于.NET框架未正确安装或.NET框架系统中的另一个问题引起的。
最常见的情况是,可以通过重新安装相应的.NET框架先决条件来解决此问题。但是,在许多情况下,可能需要使用.NET框架清理工具完全删除.NET框架安装。
Win10下装 VC6.0单步调试报((OLE32.DLL): 0xC0000005: Access Violation)
想在win10上重温下经典VC++6.0,结果单步调试时弹出个异常对话框,报((OLE32.DLL): 0xC0000005: Access Violation)
解决方法如下:
1、菜单 工具->选项 中的调试,反汇编下的来源注解取消
2、点确定保存设置,然后即可单步调试
3、再将工具->选项 中的调试,反汇编下的来源注解勾上
CLR内部异常(下)
直接使用SEH
有些情况里直接使用SEH会更合适一些。特别是,如果需要在第一次遍历(first pass - SEH异常处理流程里的第一遍处理)时需要执行某些操作时,也就是在堆栈向上展开之前,SEH是唯一的选项。一个SEH里的 __try/__except 过滤代码除了决定是否要处理某个异常以外,还能执行任何操作。调试器通知(Debugger notification)就是在第一次遍历时需要考虑的领域。
过滤代码的编写需要极其小心。一般来讲,过滤代码需要考虑到任何随机,而且很可能不一致的状态。因为过滤代码在第一次遍历时执行,而析构函数(dtors)在第二次遍历时执行,holders还没有执行,而且也没有恢复它们的状态。
PAL_TRY / PAL_EXCEPT, PAL_EXCEPT_FILTER, PAL_FINALLY / PAL_ENDTRY
如果需要过滤代码,PAL_TRY家族代码是CLR里可移植的写法。因为过滤代码直接使用SEH,它不能在一个函数里与C++异常处理共存,因此在函数里不能使用holders。
再次强调,这种情况很少见。
__try / __except, __finally
在CLR里没有充足理由来直接使用它们。
异常和GC模式
使用COMPlusThrowXXX()抛出异常不会影响GC模式,而且在所有模式里都是安全的。当异常将堆栈展开到EX_CATCH,在栈上的所有holders都会销毁,释放它们的资源和重置它们的状态。当在EX_CATCH里恢复执行时,被holder保护的状态会恢复到EX_TRY时的状态。
转换
在托管代码,CLR,COM服务器和其它原生代码之间,有很多如调用规范(calling conventions),内存管理和异常处理机制之间的转换。关于异常,对CLR开发者来说是幸运的,大部分转换要么完全在CLR之外,或者就是被自动处理了。CLR开发人员日常需要考虑的就是三种转换。其它的都是高端话题。
托管代码进入CLR
由“fcall”、"jit helper"等触发。典型路径是CLR通过一个托管异常向托管代码报告错误。因此,如果是一个fcall函数直接或间接抛出一个托管异常,那没什么问题。一般的CLR托管异常实现会“做正确的事”而且会去找一个适合的托管异常处理代码。
从另一面来说,如果fcall函数也可以包含抛出CLR内部异常的代码(一个C++异常),这个异常不能泄漏到托管代码那边。为了处理这种情况,CLR提供了UnwindAndContinueHandler (UACH),它是捕捉C++异常并转换成托管异常并抛出的一系列代码。
任何从托管代码端调用的CLR函数都有可能抛出C++异常,必须将抛出异常的代码封装在INSTALL_UNWIND_AND_CONTINUE_HANDLER / UNINSTALL_UNWIND_AND_CONTINUE_HANDLER里。安装一个HELPER_METHOD_FRAME会自动安装UACH。安装UACH的性能代价很大,因此不能随处使用。一种技术是,在执行关键代码的时候不要使用UACH,但是在抛出异常之前安装一个UACH。
当一个C++异常被抛出时,而少了一个UACH,典型错误就是一个在CPFH_RealFirstPassHandler里“GC_TRIGGERS在一个GC_NOTRIGGER区域里被调用”的合约违规(Contract Violation)。要修复这些错误,留意托管代码到CLR的切换,并检查INSTALL_UNWIND_AND_CONTINUE_HANDLER或HELPER_METHOD_FRAME_BEGIN_XXX。
CLR代码到托管代码
从CLR切换到托管代码是跟平台很相关的。在32位Windows平台,CLR托管异常代码要求在进入托管代码之前已经安装了“COMPlusFrameHandler”。这些切换被一些专用的辅助函数处理,它们来执行恰当的异常处理。一般对托管代码的调用很少用其它方法。如果没有COMPlusFrameHander,最可能的情况就是托管代码端的异常处理代码没有被执行,finally块和catch块都不会被执行。
CLR代码切换到外部原生代码
从CLR调用其它原生代码(操作系统、CRT和其它DLL)的过程要格外注意。这是因为外部代码可能会触发一个异常。由于EX_TRY宏的实现方式,这是一个问题,特别是它们将一个非异常错误翻译或者封装异常的方式。对于C++异常,只能通过放弃捕捉到的异常的所有信息,是可以捕捉任意或者所有异常(通过"catch(...)")。捕捉到一个异常后*,这个宏有异常对象可检查,但如果捕捉到其它东西,那就没什么可检查的了,宏只能猜异常是什么。如果异常是从CLR外部来的,那宏总是会猜错。
目前的解决方案是将对外部代码的调用封装到一个“callout filter”。这个过滤代码会捕捉外部异常,并将它翻译成一个CLR内部异常:SEHException。这个过滤代码是预定义的,而且用起来也很简单。然而,使用过滤代码就意味着使用SEH,因此在同一个函数里不能使用C++异常。在使用C++异常的函数里加上“callout filter”要求将它分成两个函数。
要使用callout filter,这样的代码:
length = SysStringLen(pBSTR);
要写成:
BOOL OneShot =TRUE;
PAL_TRY
{
length=SysStringLen(pBSTR);
}
PAL_EXCEPT_FILTER(CallOutFilter,&OneShot)
{
_ASSERTE(!"CallOutFilter returned EXECUTE_HANDLER.");
}
PAL_ENDTRY;
WinDbg里查找线程栈的StackBase 和 StackLimit 的硬偏移
User Mode 32bit
0:000> dt nt!_TEB.Stack*
ntdll!_TEB
+0x000 NtTib :
+0x004 StackBase : Ptr Void
+0x008 StackLimit : Ptr Void
User Mode 64bit
0:000> dt nt!_TEB NtTib.Stack*
ntdll!_TEB
+0x000 NtTib :
+0x008 StackBase : Ptr64 Void
+0x010 StackLimit : Ptr64 Void
Kernel Mode 32bit
0:000> dt nt!_KTHREAD Stack*
ntdll!_KTHREAD
+0x01c StackLimit : Ptr Void (NT v5.0 - Windows 2000)
+0x01c StackLimit : Ptr Void (NT v5.1 - Windows XP SP3)
+0x01c StackLimit : Ptr Void (NT v5.2 - Windows 2003 SP2)
+0x02c StackLimit : Ptr Void (NT v6.0 - Windows Vista/2008)
+0x02c StackLimit : Ptr Void (NT v6.1 - Windows 7/2008R2)
+0x15c StackBase : Ptr Void (NT v5.0 - Windows 2000)
+0x15c StackBase : Ptr Void (NT v5.1 - Windows XP SP3)
+0x158 StackBase : Ptr Void (NT v5.2 - Windows 2003 SP2)
+0x174 StackBase : Ptr Void (NT v6.0 - Windows Vista/2008)
+0x194 StackBase : Ptr Void (NT v6.1 - Windows 7/2008R2)
Kernel Mode 64bit
0:000> dt nt!_KTHREAD Stack*
ntdll!_KTHREAD
+0x??? StackLimit : Ptr64 Void(NT v5.1 - Windows XP SP3)
+0x??? StackLimit : Ptr64 Void(NT v5.2 - Windows 2003 SP2)
+0x030 StackLimit : Ptr64 Void(NT v6.0 - Windows Vista/2008)
+0x030 StackLimit : Ptr64 Void(NT v6.1 - Windows 7/2008R2)
+0x??? StackBase : Ptr64 Void(NT v5.1 - Windows XP SP3)
+0x??? StackBase : Ptr64 Void(NT v5.2 - Windows 2003 SP2)
+0x250 StackBase : Ptr64 Void(NT v6.0 - Windows Vista/2008)
+0x278 StackBase : Ptr64 Void(NT v6.1 - Windows 7/2008R2)
CLR内部异常(中)
不捕捉某一个异常
常常有这种情况,代码不需要捕捉异常,但需要执行一些清理或者修正操作。虽然不总是,支持物(holders)经常用在这种场景里。在支持物(holders)不适用的情况里,CLR提供了两个“finally”块的变种。
EX_TRY_FOR_FINALLY
当需要在代码退出时执行修正操作时,一个finally块就比较合适。在CLR里有一系列的宏来实现try/finally:
EX_TRY_FOR_FINALLY//code EX_FINALLY//exit and/or backout code EX_END_FINALLY