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 >
C/C++ Source or Header  |  1992-03-30  |  11KB  |  384 lines

  1. /*
  2.  * FAULT.C
  3.  *
  4.  * Very small Windows application demonstrating how to use the TOOLHELP
  5.  * library to trap GP Faults and Divide by Zero exceptions.  Trapping these
  6.  * faults allows an application to perform cleanup, save files, and
  7.  * otherwise insure integrity of the user's data.
  8.  *
  9.  * FAULT uses the ToolHelp functions InterruptRegister and
  10.  * InterruptUnRegister to hook itself into the interrupt handler
  11.  * chain.  Before executing an operation that might cause an exception,
  12.  * we use the Catch function to store the current register state.
  13.  * If an exception occurs, ToolHelp will trap it and call our interrupt
  14.  * handler in HANDLER.ASM.  Within the handler, we check if the
  15.  * exception is something we can handle.  If not, that interrupt is
  16.  * passed on to the next handler.  Otherwise it calls Throw, returning
  17.  * control to the function that last called Catch.
  18.  *
  19.  * The handler catches Interrupts 0 (Divide by Zero) and 13 (GP Fault).
  20.  *
  21.  * Copyright(c) Microsoft Corp. 1992 All Rights Reserved
  22.  *
  23.  */
  24.  
  25. #include <windows.h>
  26. #include <toolhelp.h>
  27. #include "fault.h"
  28.  
  29.  
  30. //Global variable block.
  31. GLOBALS     stGlobals;
  32. LPGLOBALS   pGlob=&stGlobals;
  33.  
  34. /*
  35.  * These global variables hold information that is needed from the
  36.  * interrupt handler.  They are set apart here to make them more visible.
  37.  */
  38. CATCHBUF    cbEx;                       //Stores register state.
  39. LPCATCHBUF  pcbEx=(LPCATCHBUF)&cbEx;    //Convenient pointer
  40. WORD        wException;                 //Indicates which exceptions to trap.
  41.  
  42.  
  43.  
  44. /*
  45.  * WinMain
  46.  *
  47.  * Purpose:
  48.  *  Main entry point of application.   Should register the app class
  49.  *  if a previous instance has not done so and do any other one-time
  50.  *  initializations.
  51.  *
  52.  * Parameters:
  53.  *  Standard
  54.  *
  55.  * Return Value:
  56.  *  Value to return to Windows--termination code.
  57.  *
  58.  */
  59.  
  60. int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  61.                     LPSTR lpszCmdLine, int nCmdShow)
  62.     {
  63.     WNDCLASS        wc;
  64.     MSG             msg;
  65.  
  66.     pGlob->hInst=hInstance;
  67.  
  68.  
  69.     if (!hPrevInstance)
  70.         {
  71.         /*
  72.          * Note that we do not need to unregister classes on a failure
  73.          * since that's part of automatic app cleanup.
  74.          */
  75.         wc.style         = CS_HREDRAW | CS_VREDRAW;
  76.         wc.lpfnWndProc   = FaultWndProc;
  77.         wc.cbClsExtra    = 0;
  78.         wc.cbWndExtra    = 0;
  79.         wc.hInstance     = pGlob->hInst;
  80.         wc.hIcon         = LoadIcon(pGlob->hInst, MAKEINTRESOURCE(IDR_ICON));
  81.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  82.         wc.hbrBackground = COLOR_APPWORKSPACE + 1;
  83.         wc.lpszMenuName  = MAKEINTRESOURCE(IDR_ICON);
  84.         wc.lpszClassName = "Fault";
  85.  
  86.         if (!RegisterClass(&wc))
  87.             return FALSE;
  88.         }
  89.  
  90.     pGlob->hWnd=CreateWindow("Fault", "Exception Handler",
  91.                              WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW,
  92.                              CW_USEDEFAULT, CW_USEDEFAULT, 400, 120,
  93.                              NULL, NULL, hInstance, NULL);
  94.  
  95.     if (NULL==pGlob->hWnd)
  96.         return FALSE;
  97.  
  98.     ShowWindow(pGlob->hWnd, nCmdShow);
  99.     UpdateWindow(pGlob->hWnd);
  100.  
  101.     while (GetMessage(&msg, NULL, 0,0 ))
  102.         {
  103.         TranslateMessage(&msg);
  104.         DispatchMessage(&msg);
  105.         }
  106.  
  107.     return msg.wParam;
  108.     }
  109.  
  110.  
  111.  
  112.  
  113.  
  114. /*
  115.  * FaultWndProc
  116.  *
  117.  * Purpose:
  118.  *  Window class procedure.  Standard callback.
  119.  *
  120.  * Parameters:
  121.  *  The standard.
  122.  *
  123.  * Return Value:
  124.  *  The standard.
  125.  *
  126.  */
  127.  
  128. long FAR PASCAL FaultWndProc(HWND hWnd, UINT iMsg, UINT wParam, LONG lParam)
  129.     {
  130.     HANDLE      hMem;
  131.  
  132.     switch (iMsg)
  133.         {
  134.         case WM_CREATE:
  135.             /*
  136.              * Install the ExceptionHandler function in HANDLER.ASM
  137.              * as our fault handler and store the proc address in the
  138.              * global variable block.
  139.              */
  140.  
  141.             pGlob->pfnInt=MakeProcInstance((FARPROC)ExceptionHandler, pGlob->hInst);
  142.  
  143.             if (NULL!=pGlob->pfnInt)
  144.                 {
  145.                 //If we fail to register, fail the create and the application.
  146.                 if (!InterruptRegister(NULL, pGlob->pfnInt))
  147.                     {
  148.                     MessageBox(hWnd, "InterruptRegister Failed.", "Fatal Error", MB_OK);
  149.                     return -1L;
  150.                     }
  151.                 }
  152.             break;
  153.  
  154.  
  155.         case WM_DESTROY:
  156.             //Remove our exception handler and free the proc address.
  157.             if (NULL!=pGlob->pfnInt)
  158.                 {
  159.                 InterruptUnRegister(NULL);
  160.                 FreeProcInstance((FARPROC)pGlob->pfnInt);
  161.                 }
  162.  
  163.             PostQuitMessage(0);
  164.             break;
  165.  
  166.  
  167.         case WM_COMMAND:
  168.             switch (wParam)
  169.                 {
  170.                 case IDM_EXDIVIDEBYZERO:
  171.                     if (FPerformCalculation())
  172.                         {
  173.                         //This should NOT happen.
  174.                         MessageBox(hWnd, "IMPOSSIBLE: Missed a Divide by Zero!",
  175.                                    "Fault", MB_OK | MB_ICONHAND);
  176.                         }
  177.                     else
  178.                         {
  179.                         MessageBox(hWnd, "Calculation failed: Divide by zero.",
  180.                                    "Fault", MB_OK | MB_ICONEXCLAMATION);
  181.                         break;
  182.                         }
  183.  
  184.                     break;
  185.  
  186.  
  187.                 case IDM_EXGPFAULT:
  188.                     hMem=HAllocateNumbers();
  189.  
  190.                     if (NULL!=hMem)
  191.                         {
  192.                         //This should NOT happen.
  193.                         MessageBox(hWnd, "IMPOSSIBLE:  Missed the GP Fault!",
  194.                                    "Fault", MB_OK | MB_ICONHAND);
  195.  
  196.                         GlobalFree(hMem);
  197.                         }
  198.                     else
  199.                         {
  200.                         MessageBox(hWnd, "Allocation failed: GP fault.",
  201.                                    "Fault", MB_OK | MB_ICONEXCLAMATION);
  202.                         break;
  203.                         }
  204.  
  205.                     break;
  206.  
  207.  
  208.                 default:
  209.                     break;
  210.                 }
  211.             break;
  212.  
  213.  
  214.         default:
  215.             return (DefWindowProc(hWnd, iMsg, wParam, lParam));
  216.         }
  217.  
  218.     return 0L;
  219.     }
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226. /*
  227.  * FPerformCalculation
  228.  *
  229.  * Purpose:
  230.  *  Attempts to divide the number 10000 by the numbers 1 through 6.
  231.  *  However, this function was poorly written to use the wrong
  232.  *  terminating condition in a while loop, so the loop executes with
  233.  *  zero as the divisor and faults.
  234.  *
  235.  * Parameters:
  236.  *  None
  237.  *
  238.  * Return Value:
  239.  *  BOOL            TRUE if the function succeeded (this should not happen)
  240.  *                  FALSE if the function failed, even on a divide by zero
  241.  *                  exception.
  242.  */
  243.  
  244. BOOL FAR PASCAL FPerformCalculation(void)
  245.     {
  246.     int         iCatch;
  247.     WORD        i;
  248.     WORD        wValue;
  249.  
  250.     /*
  251.      * Call Catch and indicate what exceptions to trap.
  252.      *
  253.      * The first time we call Catch here we will get a 0 return value.
  254.      * When we call Throw in our exception handler, Catch returns with
  255.      * the value given in the second parameter to Throw.  Throw must
  256.      * use the same CATCHBUF we fill here in order to return here.
  257.      */
  258.  
  259.     //Indicate the trap(s) we want.
  260.     wException=EXCEPTION_DIVIDEBYZERO;
  261.  
  262.     //Save the register state.
  263.     iCatch=Catch(pcbEx);
  264.  
  265.     //Check if we returned from the exception handler.
  266.     if (0!=iCatch)
  267.         {
  268.         /*
  269.          * Now we can safely exit this procedure, skipping the code
  270.          * that faulted.  We indicate that we now want no exceptions
  271.          * by setting wException to EXCEPTION_NONE.
  272.          *
  273.          ***BE SURE to turn off exception handling that uses Catch
  274.          *  and Throw between messages.  In other words, only use
  275.          *  Catch and Throw within the scope of a function, NOT on
  276.          *  the scope of an application.
  277.          */
  278.  
  279.         wException=EXCEPTION_NONE;
  280.         return FALSE;
  281.         }
  282.  
  283.  
  284.     i=6;
  285.     wValue=10000;
  286.  
  287.     /*
  288.      * When we check i==1, the condition is TRUE so
  289.      * we continue the loop.  However, the post-decrement
  290.      * on i makes it zero, which will fault.
  291.      */
  292.  
  293.     while (i--)
  294.         wValue /=i;
  295.  
  296.     //We should never get here.
  297.     wException=EXCEPTION_NONE;
  298.     return TRUE;
  299.     }
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306. /*
  307.  * HAllocateNumbers
  308.  *
  309.  * Purpose:
  310.  *  Attempts to allocate a 1K block of memory and fill it with the
  311.  *  repeating sequence of the values 0 through 255.  However, due to
  312.  *  another bug in this function, we end up writing past the end of
  313.  *  the segment.  We trap the GP Fault and recover by freeing the memory
  314.  *  and indicating that the function failed.
  315.  *
  316.  * Parameters:
  317.  *  None
  318.  *
  319.  * Return Value:
  320.  *  HANDLE          A global memory handle containing the numbers if
  321.  *                  successful, NULL otherwise (including when we GP fault).
  322.  */
  323.  
  324. HANDLE FAR PASCAL HAllocateNumbers(void)
  325.     {
  326.     LPSTR       psz;
  327.     WORD        i;
  328.     int         iCatch;
  329.     HANDLE      hMem;
  330.  
  331.     /*
  332.      * Call Catch and indicate what exceptions to trap.
  333.      *
  334.      * The first time we call Catch here we will get a 0 return value.
  335.      * When we call Throw in our exception handler, Catch returns with
  336.      * the value given in the second parameter to Throw.  Throw must
  337.      * use the same CATCHBUF we fill here in order to return here.
  338.      */
  339.  
  340.     //Indicate the trap(s) we want.
  341.     wException=EXCEPTION_GPFAULT;
  342.  
  343.     //Save the register state.
  344.     iCatch=Catch(pcbEx);
  345.  
  346.     //Check if we returned from the exception handler.
  347.     if (0!=iCatch)
  348.         {
  349.         /*
  350.          * Free any resources this function allocated, perform other
  351.          * cleanup, turn OFF any exception handling, and return a failure.
  352.          */
  353.  
  354.         if (NULL!=hMem)
  355.             {
  356.             GlobalUnlock(hMem);
  357.             GlobalFree(hMem);
  358.             }
  359.  
  360.         wException=EXCEPTION_NONE;
  361.         return NULL;
  362.         }
  363.  
  364.  
  365.     //Get 1024 bytes of memory.
  366.     hMem=GlobalAlloc(GMEM_MOVEABLE, 1024);
  367.     psz=GlobalLock(hMem);
  368.  
  369.     /*
  370.      * Write to 1025 bytes of memory, thus accidentally walking over
  371.      * the edge.  Another case where an erroneous terminating condition
  372.      * in a loop can cause such a problem.
  373.      */
  374.  
  375.     i=0;
  376.     while (i++ <= 1024)     //Should be i++ < 1024, not <=
  377.         *psz++=(char)i;
  378.  
  379.     //We should never get here to return the handle.
  380.     GlobalUnlock(hMem);
  381.     wException=EXCEPTION_NONE;
  382.     return hMem;
  383.     }
  384.