24.1.3 Exception Handling Syntax

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.

24.1.3.1 Terminology

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.

24.1.3.2 Goto Rule

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.

24.1.3.3 Exception Semantics

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

)

Return Value

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.

24.1.3.4 Termination Semantics

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.

24.1.3.5 Examples

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);

}