int __except_handler3(
struct _EXCEPTION_RECORD * pExceptionRecord,
struct EXCEPTION_REGISTRATION * pRegistrationFrame,
struct _CONTEXT *pContextRecord,
void * pDispatcherContext )
{
LONG filterFuncRet
LONG trylevel
EXCEPTION_POINTERS exceptPtrs
PSCOPETABLE pScopeTable
CLD // Clear the direction flag (make no assumptions!)
// if neither the EXCEPTION_UNWINDING nor EXCEPTION_EXIT_UNWIND bit
// is set... This is true the first time through the handler (the
// non-unwinding case)
if ( ! (pExceptionRecord->ExceptionFlags
& (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) )
{
// Build the EXCEPTION_POINTERS structure on the stack
exceptPtrs.ExceptionRecord = pExceptionRecord;
exceptPtrs.ContextRecord = pContextRecord;
// Put the pointer to the EXCEPTION_POINTERS 4 bytes below the
// establisher frame. See ASM code for GetExceptionInformation
*(PDWORD)((PBYTE)pRegistrationFrame - 4) = &exceptPtrs;
// Get initial "trylevel" value
trylevel = pRegistrationFrame->trylevel
// Get a pointer to the scopetable array
scopeTable = pRegistrationFrame->scopetable;
search_for_handler:
if ( pRegistrationFrame->trylevel != TRYLEVEL_NONE )
{
if ( pRegistrationFrame->scopetable[trylevel].lpfnFilter )
{
PUSH EBP // Save this frame EBP
// !!!Very Important!!! Switch to original EBP. This is
// what allows all locals in the frame to have the same
// value as before the exception occurred.
EBP = &pRegistrationFrame->_ebp
// Call the filter function
filterFuncRet = scopetable[trylevel].lpfnFilter();
POP EBP // Restore handler frame EBP
if ( filterFuncRet != EXCEPTION_CONTINUE_SEARCH )
{
if ( filterFuncRet < 0 ) // EXCEPTION_CONTINUE_EXECUTION
return ExceptionContinueExecution;
// If we get here, EXCEPTION_EXECUTE_HANDLER was specified
scopetable == pRegistrationFrame->scopetable
// Does the actual OS cleanup of registration frames
// Causes this function to recurse
__global_unwind2( pRegistrationFrame );
// Once we get here, everything is all cleaned up, except
// for the last frame, where we'll continue execution
EBP = &pRegistrationFrame->_ebp
// Set the current trylevel to whatever SCOPETABLE entry
// was being used when a handler was found
pRegistrationFrame->trylevel = scopetable->previousTryLevel;
// Call the _except {} block. Never returns.
pRegistrationFrame->scopetable[trylevel].lpfnHandler();
}
}
//==================================================
// ShowSEHFrames - Matt Pietrek 1997
// Microsoft Systems Journal, February 1997
// FILE: ShowSEHFrames.CPP
// To compile: CL ShowSehFrames.CPP
//==================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#pragma hdrstop
//----------------------------------------------------------------------------
// !!! WARNING !!! This program only works with Visual C++, as the data
// structures being shown are specific to Visual C++.
//----------------------------------------------------------------------------
#ifndef _MSC_VER
#error Visual C++ Required (Visual C++ specific information is displayed)
#endif
// __except_handler3 is a Visual C++ RTL function. We want to refer to
// it in order to print it's address. However, we need to prototype it since
// it doesn't appear in any header file.
extern "C" int _except_handler3(PEXCEPTION_RECORD, EXCEPTION_REGISTRATION *,
PCONTEXT, PEXCEPTION_RECORD);
// Print out the location of the __except_handler3 function
printf( "_except_handler3 is at address: %08X\n", _except_handler3 );
printf( "\n" );
// Get a pointer to the head of the chain at FS:[0]
__asm mov eax, FS:[0]
__asm mov [pVCExcRec], EAX
// Walk the linked list of frames. 0xFFFFFFFF indicates the end of list
while ( 0xFFFFFFFF != (unsigned)pVCExcRec )
{
ShowSEHFrame( pVCExcRec );
pVCExcRec = (VC_EXCEPTION_REGISTRATION *)(pVCExcRec->prev);
}
}
void Function1( void )
{
// Set up 3 nested _try levels (thereby forcing 3 scopetable entries)
_try
{
_try
{
_try
{
WalkSEHFrames(); // Now show all the exception frames
}
_except( EXCEPTION_CONTINUE_SEARCH )
{
}
}
_except( EXCEPTION_CONTINUE_SEARCH )
{
}
}
_except( EXCEPTION_CONTINUE_SEARCH )
{
}
}
int main()
{
int i;
// Use two (non-nested) _try blocks. This causes two scopetable entries
// to be generated for the function.
_try
{
i = 0x1234; // Do nothing in particular
}
_except( EXCEPTION_CONTINUE_SEARCH )
{
i = 0x4321; // Do nothing (in reverse)
}
_try
{
Function1(); // Call a function that sets up more exception frames
}
_except( EXCEPTION_EXECUTE_HANDLER )
{
// Should never get here, since we aren't expecting an exception
printf( "Caught Exception in main\n" );
}
// Begin traversing the list of EXCEPTION_REGISTRATION
while ( -1 != pExcptRegHead )
{
EXCEPTION_RECORD excptRec2;
if ( pExcptRegHead == pRegistrationFrame )
{
_NtContinue( &context, 0 );
}
else
{
// If there's an exception frame, but it's lower on the stack
// then the head of the exception list, something's wrong!
if ( pRegistrationFrame && (pRegistrationFrame <= pExcptRegHead) )
{
// Generate an exception to bail out
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
if ( EXCEPTION_CONTINUE_EXECUTION == retValue )
return EXCEPTION_CONTINUE_EXECUTION;
}
// See if this process is being run under a debugger...
retValue = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort,
&debugPort, sizeof(debugPort), 0 );
if ( (retValue >= 0) && debugPort ) // Let debugger have it
return EXCEPTION_CONTINUE_SEARCH;
// Did the user call SetUnhandledExceptionFilter? If so, call their
// installed proc now.
if ( _BasepCurrentTopLevelFilter )
{
retValue = _BasepCurrentTopLevelFilter( pExceptionPtrs );
if ( EXCEPTION_EXECUTE_HANDLER == retValue )
return EXCEPTION_EXECUTE_HANDLER;
if ( EXCEPTION_CONTINUE_EXECUTION == retValue )
return EXCEPTION_CONTINUE_EXECUTION;
// Only EXCEPTION_CONTINUE_SEARCH goes on from here
}
// Has SetErrorMode(SEM_NOGPFAULTERRORBOX) been called?
if ( 0 == (GetErrorMode() & SEM_NOGPFAULTERRORBOX) )
{
harderr.elem0 = pExcptRec->ExceptionCode;
harderr.elem1 = pExcptRec->ExceptionAddress;
if ( _BasepAlreadyHadHardError )
NtTerminateProcess(GetCurrentProcess(), pExcptRec->ExceptionCode);
}
return EXCEPTION_EXECUTE_HANDLER;
}
LPTOP_LEVEL_EXCEPTION_FILTER
SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );
{
// _BasepCurrentTopLevelFilter is a KERNEL32.DLL global var
LPTOP_LEVEL_EXCEPTION_FILTER previous= _BasepCurrentTopLevelFilter;
// Set the new value
_BasepCurrentTopLevelFilter = lpTopLevelExceptionFilter;
if ( pRegistrationFrame & 3 ) // Make sure stack is DWORD aligned
{
pExcptRec->ExceptionFlags |= EH_STACK_INVALID;
return DISPOSITION_DISMISS; // 0
}
if ( someProcessFlag )
{
// Doesn't seem to do a whole heck of a lot.
hLog = RtlpLogExceptionHandler( pExcptRec, pContext, 0,
pRegistrationFrame, 0x10 );
}
RtlpExecutehandlerForUnwind: // Handles unwind (second time through)
MOV EDX,XXXXXXXX
int ExecuteHandler( PEXCEPTION_RECORD pExcptRec
PEXCEPTION_REGISTRATION pExcptReg
CONTEXT * pContext
PVOID pDispatcherContext,
FARPROC handler ) // Really a ptr to an _except_handler()
// Set up an EXCEPTION_REGISTRATION, where EDX points to the
// appropriate handler code shown below
PUSH EDX
PUSH FS:[0]
MOV FS:[0],ESP
// Invoke the exception callback function
EAX = handler( pExcptRec, pExcptReg, pContext, pDispatcherContext );
// Remove the minimal EXCEPTION_REGISTRATION frame
MOV ESP,DWORD PTR FS:[00000000]
POP DWORD PTR FS:[00000000]
return EAX;
}
Exception handler used for _RtlpExecuteHandlerForException:
{
// If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
// assign pDispatcher context and return DISPOSITION_NESTED_EXCEPTION
Exception handler used for _RtlpExecuteHandlerForUnwind:
{
// If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
// assign pDispatcher context and return DISPOSITION_COLLIDED_UNWIND