Example: Treating Errors as Exceptions
Previous examples use ReportError to process system call and other errors. The function terminates the process when the programmer indicates that the error is fatal. This approach, however, prevents an orderly shutdown, and it also prevents program continuation after recovering from an error. For example, the program may have created temporary files that should be deleted, or the program may simply proceed to do other work after abandoning the failed task. ReportError has other limitations, including the following.
- A fatal error shuts down the entire process when only a single thread (Chapter 7) should terminate.
- You may wish to continue program execution rather than terminate the process.
- Synchronization resources (Chapter 8), such as mutexes, will not be released in many circumstances.
Open handles will be closed by a terminating process (but not by a terminating thread), but it is necessary to address the other deficiencies.The solution is to write a new function, ReportException. This function invokes ReportError (developed in Chapter 2) with a nonfatal code in order to generate the error message. Next, on a fatal error, it will raise an exception. The system will use an exception handler from the calling try block, so the exception may not actually be fatal if the handler allows the program to recover. Essentially, ReportException augments normal defensive programming techniques, previously limited to ReportError. Once an error is detected, the exception handler allows the program to recover and continue after the error. Program 4-2 illustrates this capability.Program 4-1 shows the function. It is in the same source module as ReportError, so the definitions and include files are omitted.
Program 4-1. ReportException: Exception Reporting Function
ReportException is used in several subsequent examples.
/* Extension of ReportError to generate a user-exception
code rather than terminating the process. */
VOID ReportException (LPCTSTR UserMessage, DWORD ExceptionCode)
/* Report as a nonfatal error. */
{
ReportError (UserMessage, 0, TRUE);
/* If fatal, raise an exception. */
if (ExceptionCode != 0)
RaiseException (
(0x0FFFFFFF & ExceptionCode) | 0xE0000000, 0, 0, NULL);
return;
}
The UNIX signal model is significantly different from SEH. Signals can be missed or ignored, and the flow is different. Nonetheless, there are points of comparison.UNIX signal handling is largely supported through the C library, which is also available in a limited implementation under Windows. In many cases, Windows programs can use console control handlers, which are described near the end of this chapter, in place of signals.Some signals correspond to Windows exceptions.Here is the limited signal-to-exception correspondence:
The C library raise function corresponds to RaiseException.Windows will not generate SIGILL, SIGSEGV, or SIGTERM, although raise can generate one of them. Windows does not support SIGINT.The UNIX kill function (kill is not in the Standard C library), which can send a signal to another process, is comparable to the Windows function GenerateConsoleCtrlEvent (Chapter 6). In the limited case of SIGKILL, Windows has TerminateProcess and TerminateThread, allowing one process (or thread) to "kill" another, although these functions should be used with care (see Chapters 6 and 7). |