home *** CD-ROM | disk | FTP | other *** search
- /*
- * FAULT.C
- *
- * Very small Windows application demonstrating how to use the TOOLHELP
- * library to trap GP Faults and Divide by Zero exceptions. Trapping these
- * faults allows an application to perform cleanup, save files, and
- * otherwise insure integrity of the user's data.
- *
- * FAULT uses the ToolHelp functions InterruptRegister and
- * InterruptUnRegister to hook itself into the interrupt handler
- * chain. Before executing an operation that might cause an exception,
- * we use the Catch function to store the current register state.
- * If an exception occurs, ToolHelp will trap it and call our interrupt
- * handler in HANDLER.ASM. Within the handler, we check if the
- * exception is something we can handle. If not, that interrupt is
- * passed on to the next handler. Otherwise it calls Throw, returning
- * control to the function that last called Catch.
- *
- * The handler catches Interrupts 0 (Divide by Zero) and 13 (GP Fault).
- *
- * Copyright(c) Microsoft Corp. 1992 All Rights Reserved
- *
- */
-
- #include <windows.h>
- #include <toolhelp.h>
- #include "fault.h"
-
-
- //Global variable block.
- GLOBALS stGlobals;
- LPGLOBALS pGlob=&stGlobals;
-
- /*
- * These global variables hold information that is needed from the
- * interrupt handler. They are set apart here to make them more visible.
- */
- CATCHBUF cbEx; //Stores register state.
- LPCATCHBUF pcbEx=(LPCATCHBUF)&cbEx; //Convenient pointer
- WORD wException; //Indicates which exceptions to trap.
-
-
-
- /*
- * WinMain
- *
- * Purpose:
- * Main entry point of application. Should register the app class
- * if a previous instance has not done so and do any other one-time
- * initializations.
- *
- * Parameters:
- * Standard
- *
- * Return Value:
- * Value to return to Windows--termination code.
- *
- */
-
- int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
- LPSTR lpszCmdLine, int nCmdShow)
- {
- WNDCLASS wc;
- MSG msg;
-
- pGlob->hInst=hInstance;
-
-
- if (!hPrevInstance)
- {
- /*
- * Note that we do not need to unregister classes on a failure
- * since that's part of automatic app cleanup.
- */
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.lpfnWndProc = FaultWndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = pGlob->hInst;
- wc.hIcon = LoadIcon(pGlob->hInst, MAKEINTRESOURCE(IDR_ICON));
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = COLOR_APPWORKSPACE + 1;
- wc.lpszMenuName = MAKEINTRESOURCE(IDR_ICON);
- wc.lpszClassName = "Fault";
-
- if (!RegisterClass(&wc))
- return FALSE;
- }
-
- pGlob->hWnd=CreateWindow("Fault", "Exception Handler",
- WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, CW_USEDEFAULT, 400, 120,
- NULL, NULL, hInstance, NULL);
-
- if (NULL==pGlob->hWnd)
- return FALSE;
-
- ShowWindow(pGlob->hWnd, nCmdShow);
- UpdateWindow(pGlob->hWnd);
-
- while (GetMessage(&msg, NULL, 0,0 ))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return msg.wParam;
- }
-
-
-
-
-
- /*
- * FaultWndProc
- *
- * Purpose:
- * Window class procedure. Standard callback.
- *
- * Parameters:
- * The standard.
- *
- * Return Value:
- * The standard.
- *
- */
-
- long FAR PASCAL FaultWndProc(HWND hWnd, UINT iMsg, UINT wParam, LONG lParam)
- {
- HANDLE hMem;
-
- switch (iMsg)
- {
- case WM_CREATE:
- /*
- * Install the ExceptionHandler function in HANDLER.ASM
- * as our fault handler and store the proc address in the
- * global variable block.
- */
-
- pGlob->pfnInt=MakeProcInstance((FARPROC)ExceptionHandler, pGlob->hInst);
-
- if (NULL!=pGlob->pfnInt)
- {
- //If we fail to register, fail the create and the application.
- if (!InterruptRegister(NULL, pGlob->pfnInt))
- {
- MessageBox(hWnd, "InterruptRegister Failed.", "Fatal Error", MB_OK);
- return -1L;
- }
- }
- break;
-
-
- case WM_DESTROY:
- //Remove our exception handler and free the proc address.
- if (NULL!=pGlob->pfnInt)
- {
- InterruptUnRegister(NULL);
- FreeProcInstance((FARPROC)pGlob->pfnInt);
- }
-
- PostQuitMessage(0);
- break;
-
-
- case WM_COMMAND:
- switch (wParam)
- {
- case IDM_EXDIVIDEBYZERO:
- if (FPerformCalculation())
- {
- //This should NOT happen.
- MessageBox(hWnd, "IMPOSSIBLE: Missed a Divide by Zero!",
- "Fault", MB_OK | MB_ICONHAND);
- }
- else
- {
- MessageBox(hWnd, "Calculation failed: Divide by zero.",
- "Fault", MB_OK | MB_ICONEXCLAMATION);
- break;
- }
-
- break;
-
-
- case IDM_EXGPFAULT:
- hMem=HAllocateNumbers();
-
- if (NULL!=hMem)
- {
- //This should NOT happen.
- MessageBox(hWnd, "IMPOSSIBLE: Missed the GP Fault!",
- "Fault", MB_OK | MB_ICONHAND);
-
- GlobalFree(hMem);
- }
- else
- {
- MessageBox(hWnd, "Allocation failed: GP fault.",
- "Fault", MB_OK | MB_ICONEXCLAMATION);
- break;
- }
-
- break;
-
-
- default:
- break;
- }
- break;
-
-
- default:
- return (DefWindowProc(hWnd, iMsg, wParam, lParam));
- }
-
- return 0L;
- }
-
-
-
-
-
-
- /*
- * FPerformCalculation
- *
- * Purpose:
- * Attempts to divide the number 10000 by the numbers 1 through 6.
- * However, this function was poorly written to use the wrong
- * terminating condition in a while loop, so the loop executes with
- * zero as the divisor and faults.
- *
- * Parameters:
- * None
- *
- * Return Value:
- * BOOL TRUE if the function succeeded (this should not happen)
- * FALSE if the function failed, even on a divide by zero
- * exception.
- */
-
- BOOL FAR PASCAL FPerformCalculation(void)
- {
- int iCatch;
- WORD i;
- WORD wValue;
-
- /*
- * Call Catch and indicate what exceptions to trap.
- *
- * The first time we call Catch here we will get a 0 return value.
- * When we call Throw in our exception handler, Catch returns with
- * the value given in the second parameter to Throw. Throw must
- * use the same CATCHBUF we fill here in order to return here.
- */
-
- //Indicate the trap(s) we want.
- wException=EXCEPTION_DIVIDEBYZERO;
-
- //Save the register state.
- iCatch=Catch(pcbEx);
-
- //Check if we returned from the exception handler.
- if (0!=iCatch)
- {
- /*
- * Now we can safely exit this procedure, skipping the code
- * that faulted. We indicate that we now want no exceptions
- * by setting wException to EXCEPTION_NONE.
- *
- ***BE SURE to turn off exception handling that uses Catch
- * and Throw between messages. In other words, only use
- * Catch and Throw within the scope of a function, NOT on
- * the scope of an application.
- */
-
- wException=EXCEPTION_NONE;
- return FALSE;
- }
-
-
- i=6;
- wValue=10000;
-
- /*
- * When we check i==1, the condition is TRUE so
- * we continue the loop. However, the post-decrement
- * on i makes it zero, which will fault.
- */
-
- while (i--)
- wValue /=i;
-
- //We should never get here.
- wException=EXCEPTION_NONE;
- return TRUE;
- }
-
-
-
-
-
-
- /*
- * HAllocateNumbers
- *
- * Purpose:
- * Attempts to allocate a 1K block of memory and fill it with the
- * repeating sequence of the values 0 through 255. However, due to
- * another bug in this function, we end up writing past the end of
- * the segment. We trap the GP Fault and recover by freeing the memory
- * and indicating that the function failed.
- *
- * Parameters:
- * None
- *
- * Return Value:
- * HANDLE A global memory handle containing the numbers if
- * successful, NULL otherwise (including when we GP fault).
- */
-
- HANDLE FAR PASCAL HAllocateNumbers(void)
- {
- LPSTR psz;
- WORD i;
- int iCatch;
- HANDLE hMem;
-
- /*
- * Call Catch and indicate what exceptions to trap.
- *
- * The first time we call Catch here we will get a 0 return value.
- * When we call Throw in our exception handler, Catch returns with
- * the value given in the second parameter to Throw. Throw must
- * use the same CATCHBUF we fill here in order to return here.
- */
-
- //Indicate the trap(s) we want.
- wException=EXCEPTION_GPFAULT;
-
- //Save the register state.
- iCatch=Catch(pcbEx);
-
- //Check if we returned from the exception handler.
- if (0!=iCatch)
- {
- /*
- * Free any resources this function allocated, perform other
- * cleanup, turn OFF any exception handling, and return a failure.
- */
-
- if (NULL!=hMem)
- {
- GlobalUnlock(hMem);
- GlobalFree(hMem);
- }
-
- wException=EXCEPTION_NONE;
- return NULL;
- }
-
-
- //Get 1024 bytes of memory.
- hMem=GlobalAlloc(GMEM_MOVEABLE, 1024);
- psz=GlobalLock(hMem);
-
- /*
- * Write to 1025 bytes of memory, thus accidentally walking over
- * the edge. Another case where an erroneous terminating condition
- * in a loop can cause such a problem.
- */
-
- i=0;
- while (i++ <= 1024) //Should be i++ < 1024, not <=
- *psz++=(char)i;
-
- //We should never get here to return the handle.
- GlobalUnlock(hMem);
- wException=EXCEPTION_NONE;
- return hMem;
- }
-