1. Floating-Point Exceptions
Readers not interested in floating-point arithmetic may wish to skip this section.
There are seven distinct floating-point exception
codes. These exceptions are disabled initially and will not occur
without first setting the processor-independent floating-point mask with
the _controlfp function. Alternatively, enable floating-point exceptions with the /fp:except compiler flag (you can also specify this from Visual Studio).
There are specific exceptions for underflow,
overflow, division by zero, inexact results, and so on, as shown in a
later code fragment. Turn the mask bit off to enable the particular exception.
DWORD _controlfp (DWORD new, DWORD mask)
|
The new value of the floating-point mask is determined by its current value (current_mask) and the two arguments as follows:
(current_mask & ~mask) | (new & mask)
The function sets the bits specified by new that are enabled by mask. All bits not in mask
are unaltered. The floating-point mask also controls processor
precision, rounding, and infinity values, which should not be modified
(these topics are out-of-scope).
The return value is the updated setting. Thus, if both argument values are 0, the value is unchanged, and the return value is the current mask setting, which can be used later to restore the mask. On the other hand, if mask is 0xFFFFFFFF, then the register is set to new, so that, for example, an old value can be restored.
Normally, to enable the floating-point exceptions, use the floating-point exception mask value, MCW_EM,
as shown in the following example. Notice that when a floating-point
exception is processed, the exception must be cleared using the _clearfp function.
#include <float.h>
DWORD fpOld, fpNew; /* Old and new mask values. */
...
fpOld = _controlfp(0, 0); /* Saved old mask. */
/* Specify six exceptions to be enabled. */
fpNew = fpOld & ~(EM_OVERFLOW | EM_UNDERFLOW
| EM_INEXACT | EM_ZERODIVIDE | EM_DENORMAL | EM_INVALID);
/* Set new control mask. MCW_EM combines the six
exceptions in the previous statement. */
_controlfp(fpNew, MCW_EM);
while (...) __try { /* Perform FP calculations. */
... /* An FP exception could occur here. */
}
__except (EXCEPTION_EXECUTE_HANDLER) {
... /* Analyze and log the FP exception. */
_clearfp(); /* Clear the exception. */
_controlfp(fpOld, 0xFFFFFFFF); /* Restore mask. */
/* Don't continue execution. */
}
This example enables all possible floating-point exceptions except for the floating-point stack overflow, EXCEPTION_FLT_STACK_CHECK.
2. Errors and Exceptions
An error can be thought of as a situation that
could occur occasionally and synchronously at known locations. System
call errors, for example, should be detected and reported immediately by
logic in the code. Thus, programmers normally include an explicit test
to see, for instance, whether a file read operation has failed.
An exception, on the other hand, could occur
nearly anywhere, and it is not possible or practical to test for an
exception. Division by zero and memory access violations are examples.
Exceptions are asynchronous.
Nonetheless, the distinction is sometimes
blurred. Windows will, optionally, generate exceptions during memory
allocation using the HeapAlloc and HeapCreate functions if memory is insufficient . Programs can also raise their own exceptions with programmer-defined exception codes using the RaiseException function, as described next.
Exception handlers provide a convenient mechanism
for exiting from inner blocks or functions under program control without
resorting to a goto, longjmp, or some other control logic to transfer control;. This capability is particularly important if the
block has accessed resources, such as open files, memory, or
synchronization objects, because the handler can release them.
User-generated exceptions provide one of the few
cases where it is possible or desirable to continue execution at the
exception point rather than terminate the program, thread, or the block
or function. However, use caution when continuing execution from the
exception point.
Finally, a program can restore system state, such
as the floating-point mask, on exiting from a block. Some examples use
handlers in this way.
User-Generated Exceptions
You can raise an exception at any point during program execution using the RaiseException function. In this way, your program can detect an error and treat it as an exception.
VOID RaiseException (
DWORD dwExceptionCode,
DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
CONST DWORD *lpArguments)
|
Parameters
dwExceptionCode is the user-defined
code. Do not use bit 28, which is reserved and Windows clears. The error
code is encoded in bits 27–0 (that is, all except the most significant
hex digit). Set bit 29 to indicate a “customer” (not Microsoft)
exception. Bits 31–30 encode the severity as follows, where the
resulting lead exception code hex digit is shown with bit 29 set.
0—Success (lead exception code hex digit is 2).
1—Informational (lead exception code hex digit is 6).
2—Warning (lead exception code hex digit is A).
3—Error (lead exception code hex digit is E).
dwExceptionFlags is normally 0, but setting the value to EXCEPTION_NONCONTINUABLE indicates that the filter expression should not generate EXCEPTION_CONTINUE_EXECUTION; doing so will cause an immediate EXCEPTION_NONCONTINUABLE_EXCEPTION exception.
lpArguments, if not NULL, points to an array of size nNumberOfArguments
(the third parameter) containing values to be passed to the filter
expression. The values can be interpreted as pointers and are 32 (Win32)
or 64 (Win64) bits long, EXCEPTION_MAXIMUM_PARAMETERS (15) is the maximum number of parameters that can be passed. Use GetExceptionInformation to access this structure.
Note that it is not possible to raise
an exception in another process or even another thread in your process.