home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CICA 1992 November
/
CICA_MS_Windows_CD-ROM_Walnut_Creek_November_1992.iso
/
zipped
/
programr
/
faultw.zoo
/
FAULT.C
next >
Wrap
C/C++ Source or Header
|
1992-03-30
|
11KB
|
384 lines
/*
* 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;
}