这是我之前question regarding exceptions的后续内容。
我有一些我试图维护的遗留代码。它有一个我很难理解的自定义内存管理组件。
我对该系统的理解如下:
调用函数要求为其分配一些内存,提供所需的初始内存量(needed
)和最大量(max
)。这叫:
base = VirtualAlloc(0, max, MEM_RESERVE, PAGE_NOACCESS);
我理解保留内存但不提供访问权限。换句话说,如果我尝试写入保留段,我将获得访问冲突。
然后调用:
VirtualAlloc(base, needed, MEM_COMMIT, PAGE_READWRITE);
这使needed
内存量从base
开始。
当试图检测何时需要访问更多内存时,会出现粘性部分。我的理解是,系统会在发生访问冲突异常时尝试捕获它们,并在地址上调用VirtualAlloc
以使内存可访问。
它通过声明以下方法来实现:
unsigned long __cdecl
exceptionCatch(struct _EXCEPTION_RECORD* er, void*, struct _CONTEXT* cr, void*)
{
if( er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
&& ExtendBuffer( (void*)er->ExceptionInformation[1] ) )
return ExceptionContinueExecution;
return ExceptionContinueSearch;
}
然后,它将此注册为堆栈顶部的异常处理程序(我认为),使用这段特别可怕的代码:
void __cdecl SetHandler(bExceptionRegistration& v)
{
__asm
{
mov eax, 8[ebp] ; get exception register record to install
mov ecx, fs:[0] ; get current head of chain
cmp ecx, eax ; should we be at head?
jb search
mov [eax], ecx ; save current head
mov fs:[0], eax ; install new record at head
jmp short ret1
search:
cmp [ecx], eax ; at proper location yet?
ja link
mov ecx, [ecx] ; get next link
jmp search
link:
mov edx, [ecx]
mov [eax], edx ; point to next
mov [ecx], eax
ret1:
}
}
通过在方法范围中实例化特定类来调用此方法。看起来它只将处理程序应用于当前堆栈上下文;如果异常未传播到当前方法,则当前方法不会处理在被调用函数中抛出的异常。
所有这一切的结果是不仅没有捕获访问冲突,而且它在堆栈的当前顶部禁用异常处理。我在exceptionCatch
函数中设置了断点,执行似乎没有输入。
我想我的主要问题是:
我不认为像set_unexpected
这样的东西是可行的,因为内存管理只适用于这个特定的库,而客户端应用程序可能(在我们的例子中)有自己的意外异常处理程序。 / p>
修改
通过使用以下类构造函数和析构函数声明类bExceptionRegistration
来完成每个堆栈的处理程序的设置和取消设置:
bExceptionRegistration :: bExceptionRegistration() : function(exceptionCatch)
{
SetHandler(*this);
}
bExceptionRegistration :: ~bExceptionRegistration()
{
UnsetHandler(*this);
}
因此,要实际设置特定堆栈范围的处理程序,您将拥有:
void someFunction()
{
bExceptionRegistration er;
// do some stuff here
}
编辑:我猜这可能是最合适的解决办法,就是用bExceptionRegistration
块替换代码中的__try, __except
声明。我希望避免这种情况,因为它存在于很多地方。
答案 0 :(得分:2)
如果没有看到更多代码,我对此并不是100%肯定。
它不会在堆栈顶部注册异常处理程序,但它使用技巧来插入定义EXCEPTION_REGISTRATION
结构的异常处理。例如(可能在你的情况下它的实现有点不同):
void function3(EXCEPTION_REGISTRATION& handler)
{
SetHandler(handler);
//Do other stuff
}
void function2(EXCEPTION_REGISTRATION& handler)
{
__try
{
//Do something
function3(handler);
}
__except(expression)
{
//...
}
}
void function()
{
EXCEPTION_REGISTRATION handler;
//..Init handler
function2(handler)
}
当你调用SetHandler
时,它将插入异常处理,就像它在函数范围内一样。所以在这种情况下,当你调用SetHandler
时,它看起来好像在函数中有一个__try __except块。
因此,如果在function3中有异常,则首先调用函数中的处理程序,如果该处理程序不处理它,将调用由SetHandler
安装的处理程序。