This section describes the syntax and usage of structured exception handling as it appears in Microsoft C.
The following keywords are interpreted by the Microsoft C compiler as part of the structured exception handling mechanism.
try
except
finally
GetExceptionCode
GetExceptionInformation
AbnormalTermination
Two sets of syntax are provided to handle exceptions.
For exception handling, The syntax is
try-except-statement ::= try compound-statement
except (expression) compound-statement
For termination handlers it is
try-finally-statement ::=
try compound-statement finally compound-statement
Note that the compound statements require braces.
The compound-statement after try is the body or guarded statement. The second compound statement is the exception handler or termination handler. The expression after except is the exception-filter.
Jumping (via a goto) into the body of a try statement or into a handler is not allowed. This rule applies to both try-except and try-finally statements.
If an exception occurs in the body of a try-except, the exception-filter is evaluated. If the resulting value is EXCEPTION_EXECUTE_HANDLER, control is transferred to the exception handler. Note that the transfer of control to the exception handler is similar to a longjmp; stack frames are unwound back to (but not including) the frame of the try statement, and SP is reset (by code at the beginning of the exception handler). In addition, during the unwind all termination handlers are called.
If the value of the exception-filter is EXCEPTION_CONTINUE_SEARCH, the search for a handler continues (first for containing try-except statements, then for handlers in the dynamically preceding function activation, etc.).
If the value of the exception-filter is EXCEPTION_CONTINUE_EXECUTION, the exception is dismissed, i.e., control returns to the point where the exception occurred, provided this is allowed for in the case of the particular exception. (If not allowed, the underlying system convention for this case applies.)
This gives the C programmer a convenient hook to catch continuable exceptions, e.g., a CTRL-BREAK if the system raises that as an exception.
The exception-filter is evaluated in the context of the function activation executing the try-except (even though there may be dynamic descendents on the stack). Thus local variables may be accessed in the exception-filter.
Because the filter expression can invoke a function, the exception-filter logic can be as elaborate as one wants.
Two intrinsic functions may be used within the exception-filter to access information about the exception being filtered.
DWORD
GetExceptionCode(
VOID
)
The value of the exception code from the exception record (.e.g EXCEPTION_ACCESS_VIOLATION) is returned by this intrinsic.
e.g.
try {
.
.
.
}
except ( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH ) {
.
.
.
LPEXCEPTION_INFORMATION
GetExceptionInformation(
VOID
)
This returns a pointer to a structure containing additional information about the exception. Through this pointer one can access the machine state that existed at the point of an exception and detailed information about the exception.
This function is useful for exception filters that need additional information about the exception. The format of the return value is:
Structure EXCEPTION_INFORMATION:
LPEXCEPTION_RECORD lpExceptionRecord -
Supplies a pointer to the portable description
of the exception.
LPCONTEXT lpContext -
Supplies a pointer to the machine state at the time
of the exception. This structure is described
with the Win32 debug functions.
The GetExceptionCode function may also be used within the exception handler. However GetExceptionInformation can only be used within the exception-filter. The information it points to is generally on the stack and will be destroyed when control is transferred to the exception handler. Within the exception-filter, the programmer can copy the information to safe storage, e.g., by passing the value of GetExceptionInformation to an appropriate function.
The compiler detects out-of-context uses of these functions.
A termination handler is executed no matter how the body of the try-finally terminates. The termination handler itself can either terminate sequentially or by a transfer out of the handler. If the handler terminates sequentially, control continues according to how it reached the termination handler. The cases are:
If the body of the try-finally statement was aborted by an unwind (e.g. by a jump to an exception handler or a longjmp), control returns to the runtime, which will continue invoking termination handlers as required before jumping to the target of the unwind.
If the body of the try-finally terminated with normal control flow (sequentially, return, break, continue, or goto) that flow takes place. (If such a return, break, or goto exits a higher-level try-finally, it's termination handler will also be invoked.)
A termination handler may exit via a jump: goto, return (from the procedure containing the handler), break, longjmp, or jump to an exception handler. If so, the jump ends the termination/unwind handling that led to invocation of the termination handler. Of course, if this jump exits a containing try-finally, there will be another round of termination handling; but that would be an unusual case.
A termination handler is executed as an internal procedure with access to, or directly using, the frame of the function containing the try-finally. As in a exception-filter, local variables may be accessed in the termination handler, but such access may inhibit some optimization related to the local variables.
An intrinsic function AbnormalTermination is available within a termination handler. It returns FALSE if the body of the try-finally statement terminated sequentially (reached the closing “}”). In all other cases it returns TRUE.
The following example shows a version of strcpy that can handle a bad pointer and return NULL if it is encountered.
char *
SafeStrcpy(
char *string1,
char *string2
)
{
try {
return strcpy(string1,string2);
}
except (EXCEPTION_EXECUTE_HANDLER) {
return NULL;
}
}
The following example shows how finally can be used to always free resources when leaving a block.
LPSTR Buffer;
try {
Buffer = NULL;
EnterCriticalSection(&CriticalSection);
Buffer = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,10);
strcpy(Buffer,"Hello"); // <-- Possible access violation,
printf("%s\n",Buffer); // if allocation fails.
LocalFree(Buffer);
}
finally {
// Whether or not the allocation succeeds, we'll leave
// the critical section.
LeaveCriticalSection(&CriticalSection);
}