GetExceptionInformation

[Previous] [Next]

When an exception occurs, the operating system pushes the following three structures on the stack of the thread that raised the exception: the EXCEPTION_RECORD structure, the CONTEXT structure, and the EXCEPTION_POINTERS structure.

The EXCEPTION_RECORD structure contains CPU-independent information about the raised exception, and the CONTEXT structure contains CPU-dependent information about the raised exception. The EXCEPTION_POINTERS structure has only two data members that are pointers to the pushed EXCEPTION_RECORD and CONTEXT data structures:

 typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; 

To retrieve this information and use it in your own application, you will need to call the GetExceptionInformation function:

 PEXCEPTION_POINTERS GetExceptionInformation(); 

This intrinsic function returns a pointer to an EXCEPTION_POINTERS structure.

The most important thing to remember about the GetExceptionInformation function is that it can be called only in an exception filter—because the CONTEXT, EXCEPTION_RECORD, and EXCEPTION_POINTERS data structures are valid only during the exception filter processing. Once control has been transferred to the exception handler, the data on the stack is destroyed.

Though this situation is rarely necessary, if you need to access the exception information from inside your exception handler block, you must save the EXCEPTION_RECORD data structure and/or CONTEXT data structure pointed to by the EXCEPTION_POINTERS structure in one or more variables that you create. The following code demonstrates how to save both the EXCEPTION_RECORD and CONTEXT data structures:

 void FuncSkunk() { // Declare variables that we can use to save the exception // record and the context if an exception should occur. EXCEPTION_RECORD SavedExceptRec; CONTEXT SavedContext;     _ _try {        } _ _except ( SavedExceptRec = *(GetExceptionInformation())->ExceptionRecord, SavedContext = *(GetExceptionInformation())->ContextRecord, EXCEPTION_EXECUTE_HANDLER) { // We can use the SavedExceptRec and SavedContext // variables inside the handler code block. switch (SavedExceptRec.ExceptionCode) {           } }     } 

Notice the use of the C language's comma (,) operator in the exception filter. Many programmers aren't used to seeing this operator. It tells the compiler to execute the comma-separated expressions from left to right. When all of the expressions have been evaluated, the result of the last (or rightmost) expression is returned.

In FuncSkunk, the left expression will execute, causing the EXCEPTION_RECORD structure on the stack to be stored in the SavedExceptRec local variable. The result of this expression is the value of SavedExceptRec. However, this result is discarded and the next expression to the right is evaluated. This second expression causes the CONTEXT structure on the stack to be stored in the SavedContext local variable. The result of the second expression is SavedContext, and again, this expression is discarded as the third expression is evaluated. This is a very simple expression that evaluates to EXCEPTION_EXECUTE_HANDLER. The result of this rightmost expression is the result of the entire comma-separated expression.

Because the exception filter evaluated to EXCEPTION_EXECUTE_HANDLER, the code inside the except block executes. At this point, the SavedExceptRec and SavedContext variables have been initialized and can be used inside the except block. Keep in mind it is important that the SavedExceptRec and SavedContext variables be declared outside the try block.

As you've probably guessed, the ExceptionRecord member of the EXCEPTION_POINTERS structure points to an EXCEPTION_RECORD structure:

 typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; 

The EXCEPTION_RECORD structure contains detailed, CPU-independent information about the exception that has most recently occurred:

  • ExceptionCode contains the code of the exception. This is the same information that is returned from the GetExceptionCode intrinsic function.
  • ExceptionFlags contains flags about the exception. Currently the only two values are 0 (which indicates a continuable exception) and EXCEPTION_NONCONTINUABLE (which indicates a noncontinuable exception). Any attempt to continue execution after a noncontinuable exception causes an EXCEPTION_NONCONTINUABLE_EXCEPTION exception to be raised.
  • ExceptionRecord points to an EXCEPTION_RECORD structure for another unhandled exception. While handling one exception, it is possible to raise another exception. For example, the code in your exception filter could attempt to divide a number by 0. Exception records can be chained to provide additional information when nested exceptions occur. A nested exception occurs if an exception is generated during the processing of an exception filter. If there are no unhandled exceptions, this member will contain NULL.
  • ExceptionAddress specifies the address of the CPU instruction that generated the exception.
  • NumberParameters specifies the number of parameters (0 to 15) associated with the exception. This is the number of defined elements in the ExceptionInformation array. For almost all exceptions, this value will be 0.
  • ExceptionInformation specifies an array of additional arguments that describe the exception. For almost all exceptions, the array elements are undefined.

The last two members of the EXCEPTION_RECORD structure, NumberParameters and ExceptionInformation, offer the exception filter some additional information about the exception. Currently only one type of exception offers additional information: EXCEPTION_ACCESS_VIOLATION. All other possible exceptions will have the NumberParameters member set to 0. You examine the NumberParameters array member to look at the additional information about a generated exception.

For an EXCEPTION_ACCESS_VIOLATION exception, ExceptionInformation[0] contains a flag that indicates the type of operation that caused the access violation. If this value is 0, the thread tried to read the inaccessible data. If this value is 1, the thread tried to write to inaccessible data. ExceptionInformation[1] specifies the address of the inaccessible data.

By using these members, you can produce exception filters that offer a significant amount of information about your application. For example, you might write an exception filter like this:

 _ _try {     } _ _except (ExpFltr(GetExceptionInformation()->ExceptionRecord)) {     } LONG ExpFltr (PEXCEPTION_RECORD pER) { char szBuf[300], *p; DWORD dwExceptionCode = pER->ExceptionCode; sprintf(szBuf, "Code = %x, Address = %p", dwExceptionCode, pER->ExceptionAddress); // Find the end of the string. p = strchr(szBuf, 0); // I used a switch statement in case Microsoft adds // information for other exception codes in the future. switch (dwExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: sprintf(p, "Attempt to %s data at address %p", pER->ExceptionInformation[0] ? "write" : "read", pER->ExceptionInformation[1]); break; default: break; } MessageBox(NULL, szBuf, "Exception", MB_OK | MB_ICONEXCLAMATION); return(EXCEPTION_CONTINUE_SEARCH); } 

The ContextRecord member of the EXCEPTION_POINTERS structure points to a CONTEXT structure (discussed in Chapter 7). This structure is platform-dependent; that is, the contents of this structure will differ from one CPU platform to another.

Basically, this structure contains one member for each of the registers available on the CPU. When an exception is raised, you can find out even more information by examining the members of this structure. Unfortunately, realizing the benefit of such a possibility requires you to write platform-dependent code that recognizes the machine it's running on and uses the appropriate CONTEXT structure. The best way to handle this is to put #ifdefs into your code. The CONTEXT structures for the various CPUs supported by Windows are in the WinNT.h file.



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net