Filtering Structured Exceptions (C++)

From RAD Studio
Jump to: navigation, search

Go Up to Structured Exceptions Under Win32 (C++)

A filter expression can invoke a filter function but the filter function cannot call GetExceptionInformation. You can pass the return value of GetExceptionInformation as a parameter to a filter function.

To pass the EXCEPTION_POINTERS information to an exception handler, the filter expression or filter function must copy the pointer or data from GetExceptionInformation to a location where the handler can later access it.

In the case of nested try-except statements, each statement's filter expression is evaluated until it locates EXCEPTION_EXECUTE_HANDLER or EXCEPTION_CONTINUE_EXECUTION. A filter expression can invoke GetExceptionInformation to get exception information.

As long as GetExceptionInformation or GetExceptionCode is called directly in the expression provided to __except, you can use a function to determine what to do with an exception rather than trying to create a complex C++ expression. Almost all of the information needed to handle an exception can be extracted from the result of GetExceptionInformation. GetExceptionInformation returns a pointer to an EXCEPTION_POINTERS structure:

struct EXCEPTION_POINTERS {
	EXCEPTION_RECORD *ExceptionRecord;
	CONTEXT *Context;
};

EXCEPTION_RECORD contains the machine-independent state:

struct EXCEPTION_RECORD {
	DWORD ExceptionCode;
	DWORD ExceptionFlags;
	struct EXCEPTION_RECORD *ExceptionRecord;
	void *ExceptionAddress;
	DWORD NumberParameters;
	DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
};

Typically, the filter function looks at the information in the ExceptionRecord to decide how to respond. Sometimes more specific information is needed (especially if the action to take is EXCEPTION_CONTINUE_EXECUTION: if nothing is done, the code that caused the exception would be executed again). For this situation, the other field of the EXCEPTION_POINTERS structure provides the processor state at the time of the exception. If this structure is modified and the filter returns EXCEPTION_CONTINUE_EXCEPTION, it is used to set the state of the thread before continuing with execution. For example:

static int xfilter(EXCEPTION_POINTERS *xp) {
	int rc;
	EXCEPTION_RECORD *xr = xp->ExceptionRecord;
	CONTEXT *xc = xp->Context;
	switch (xr->ExceptionCode) {
	case EXCEPTION_BREAKPOINT:
		// whoops, someone left an embedded breakpoint.
		// just step over it (1 byte on x86)
		++xc->Eip;
		rc = EXCEPTION_CONTINUE_EXECUTION;
		break;
	case EXCEPTION_ACCESS_VIOLATION:
		rc = EXCEPTION_EXECUTE_HANDLER;
		break;
	default:
		// give up
		rc = EXCEPTION_CONTINUE_SEARCH;
		break;
	};
	return rc;
}

// ...

EXCEPTION_POINTERS * xp;

try {
	func();
}
__except (xfilter(xp = GetExceptionInformation())) {
	abort();
}

See Also