Q32309: Using _harderr to Capture Critical Error Interrupt 24h

Article: Q32309
Product(s): Microsoft C Compiler
Version(s): winnt:
Operating System(s): 
Keyword(s): kb16bitonly
Last Modified: 22-JUL-2001

-------------------------------------------------------------------------------
The information in this article applies to:

- The C Run-Time (CRT), included with:
   - Microsoft C for MS-DOS, versions 5.1, 6.0, 6.0a, 6.0ax 
   - Microsoft C for OS/2, versions 5.1, 6.0, 6.0a 
   - Microsoft C/C++ for MS-DOS, version 7.0 
   - Microsoft Visual C++, versions 1.0, 1.5 
-------------------------------------------------------------------------------

SUMMARY
=======

The text below demonstrates using the critical-error-handler function _harderr()
that was introduced with the Microsoft C version 5.0 run- time library.

This function change the MS-DOS critical-error-handler interrupt, Interrupt 24h,
to point to a user-defined error-handler function. This function accepts three
parameters that contain information about the hardware error that triggered the
call to the error-handler function.

The sample code includes a number of functions that shift and mask these
parameters and returns a value that corresponds to one of the manifest constant
values defined in the library header file.

MORE INFORMATION
================

The end of the HARDERR.C file provides information about declaring the
user-defined error-handler function. The HARDTEST.C file demonstrates calling
the user-defined error-handler function.

HARDERR.H
---------

  // This header file defines the manifest constants and function
  // prototypes for HARDERR.C.

  // Boolean constants

  #define TRUE -1
  #define FALSE 0
  typedef signed boolean;

  // Manifest constants for get_codeloc()

  #define DOS       0x0
  #define FAT       0x1
  #define DIRECTORY 0x2
  #define DATA      0x3

  // Manifest constants for get_errcode()

  #define WRITE_PROTECT_ERR  0x0
  #define UNKNOWN_UNIT       0x1
  #define DRIVE_NOT_READY    0x2
  #define UNKNOWN_CMD        0x3
  #define CRC_ERR            0x4
  #define BAD_DRV_REQUEST    0x5
  #define SEEK_ERR           0x6
  #define UNKNOWN_MEDIA      0x7
  #define SECTOR_NOT_FOUND   0x8
  #define OUT_OF_PAPER       0x9
  #define WRITE_FAULT        0xA
  #define READ_FAULT         0xB
  #define GENERAL_FAILURE    0xC

  // Manifest constants for get_curr_dev()

  #define CURRENT_INPUT_DEVICE    0x1
  #define CURRENT_OUTPUT_DEVICE   0x2
  #define CURRENT_NULL_DEVICE     0x4
  #define CURRENT_CLOCK_DEVICE    0x8
  #define BAD_MEM_IMAGE           0xF

  // Type definitions

  #define HEADER struct _device_header
  extern HEADER
  {
     long     far *devptr;
     unsigned far *attrib;
     unsigned far *stratptr;
     unsigned far *intrptr;
     char     far *devname;
  };

  // Function prototypes

  unsigned getdrv(void);
  unsigned get_errcode(void);
  unsigned get_deverr(void);
  unsigned get_codeloc(void);
  unsigned far* get_devhdr(HEADER *header);
  char get_curr_dev(void);
  void* get_devname(void);
  boolean is_harderr(void);
  void hardreset(void);
  boolean is_diskerr(void);
  boolean is_char_dev(void);
  boolean is_read_err(void);
  boolean is_write_err(void);
  void handler(unsigned deverr, unsigned errcode, unsigned far *devhdr);

HARDERR.C
---------

  // This file implements a number of functions to work with the
  // _harderr() function in the Microsoft C run-time library. When a
  // hardware error occurs, the _harderr() function calls an error-
  // handler function defined in this file. The error handler sets flags
  // to indicate the cause of the hardware error in a global structure
  // _hflags.
  // 
  // Bits in each flag byte indicate the causes of the error. The
  // functions shift and mask the bits to obtain the causes of the
  // error.
  // 
  // Please read the comments in the code below carefully; they contain
  // several warnings.
  // 
  // Compile options needed: -AL -c -Od -W3 -Zi
  // Link options needed: None

  // Preprocessor information
  #include <dos.h>
  #include <malloc.h>
  #define TRUE -1
  #define FALSE 0

  // Type definition
  typedef signed boolean;

  // Do not modify the members of the HEADER structure! For more
  // information, please refer to an IBM or Microsoft programmer's
  // reference manual.
  #define HEADER struct _device_header
  typedef HEADER
  {
     long     far *devptr;
     unsigned far *attrib;
     unsigned far *stratptr;
     unsigned far *intrptr;
     char     far *devname;
  };

  // Global data
  static struct
  {
     unsigned deverr;
     unsigned errcode;
     unsigned far *devhdr;  // READ ONLY pointer to a HEADER structure
  } near _hflags;

  static boolean near _err = FALSE; // Error flag. FALSE = no hardware
                                    // error

  // Function definitions
  // Use these functions to retrieve information after a hardware error
  // occurs. When no hardware error has occurred, these functions
  // return random values.

  // getdrv() returns drive letter if error occurred on a drive.
  unsigned getdrv(void)
  {
     return (_hflags.deverr & 0x00ff) + 0x41;
  }

  // get_errcode() returns the hardware error type. The function
  // returns one of the manifest constants defined in HARDERR.H.
  unsigned get_errcode(void)
  {
     return _hflags.errcode;
  }

  // get_deverr returns information about device errors; for example,
  // a disk error
  unsigned get_deverr(void)
  {
     return _hflags.deverr;
  }

  // get_codeloc() returns the code location where the hardware error
  // occurred. The function returns one of the code locations defined in
  // HARDERR.H.
  unsigned get_codeloc(void)
  {
     return (_hflags.deverr & 0x0600) >> 9;
  }

  // get_devhdr() accepts the address of a HEADER structure and returns
  // a pointer to the device header. If the HEADER structure the pointer
  // parameter specifies defines the WORD fields of the device header,
  // these fields are defined in the HEADER structure specified by the
  // return value. Otherwise, these fields are not defined.
  unsigned far* get_devhdr(HEADER *header)
  {
     if (!_err)
        return 0;
     header = (HEADER *)_hflags.devhdr;
     return _hflags.devhdr;
  }

  // get_curr_dev() returns the low nibble of the attribute word at
  // offset 04 of the device header. The function returns one of the
  // manifest constants defined in HARDERR.H. This information indicates
  // serious hardware failures.
  char get_curr_dev(void)
  {
     return (char)(_hflags.devhdr[2] & 0x000f);
  }

  // get_devname() returns the name of the device on which the error
  // occurred. For example, if a printer is out of paper, this function
  // returns the value "PRN".
  void* get_devname(void)
  {
     char *ptr, i = 0;
     ptr = (char *)malloc(8);
     while (i < 8)
     {
        ptr[i] = ((char *) &(_hflags.devhdr[5]))[i];
        i++;
     }
     ptr[8] = '\0';
     return &ptr[0];
  }

  // The following functions determine if a hardware error has occurred
  // and, if so, where it occurred. You must call these functions in the
  // proper order. Failing to call them in the correct order can cause
  // unpredictable results.

  // is_harderr() is the most important function of the library. It
  // determines that a hardware error has occurred. If this function
  // does not return TRUE, the values returned by all the other
  // functions defined in this library are undefined.
  boolean is_harderr(void)
  {
     return _err;
  }

  // After calling is_harderr(), you must call hardreset().
  void hardreset(void)
  {
     _err = FALSE;
  }

  // is_diskerr() returns TRUE if a device error occurred on a disk
  // drive; for example, if the drive door is open. This function
  // returns undefined results for drives that do not exist and for
  // virtual drives, such as RAM drives.
  boolean is_diskerr(void)
  {
     return (_hflags.deverr & 0x8000) == 0 ? TRUE : FALSE;
  }

  // If a hardware error occurred, and it is not a disk error, call
  // is_char_dev() to determine if the error involved a character device
  // (PRN, KBD, TRM, and so on) of if it is a block error. If this
  // function returns FALSE, the error may be a bad memory image of the
  // File Allocation Table (FAT).
  boolean is_char_dev(void)
  {
     return (_hflags.devhdr[2] & 0x8000) == 0 ? FALSE : TRUE;
  }

  // is_read_err() returns TRUE when the error was a read error.
  boolean is_read_err(void)
  {
     return (_hflags.deverr & 0x0100) != 0 ? FALSE : TRUE;
  }

  // is_write_err() returns TRUE when the error was a write error.
  boolean is_write_err(void)
  {
     return (is_read_err() == TRUE ? FALSE : TRUE);
  }

  // handler() is the main function in this file. You cannot call any of
  // the functions defined above until after you call the _harderr()
  // function with the address of this handler.
  void handler(unsigned deverr, unsigned errcode, unsigned far *devhdr)
  {
     _hflags.deverr  = deverr;
     _hflags.errcode = errcode;
     _hflags.devhdr  = devhdr;
     _err = TRUE;
     _hardretn(1);
  }

HARDTEST.C
----------

  // This code example demonstrates using HARDERR.LIB.
  // 
  // This program determines if the floppy drive door is open when the
  // program attempts to write to disk or it determines if the printer
  // is on or is out of paper. The program checks only the disk or the
  // printer each time it runs.
  // 
  // To use this program, perform one of the following:
  //  - Open the door to your floppy disk drive and run the program.
  //    -or-
  //  - Turn off your printer and run the program.
  //    -or-
  //  - Remove the paper from your printer and run the program.
  // 
  // Compiler options needed: -AL -Od -W3 -Zi

  #include <stdio.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <sys\types.h>
  #include <sys\stat.h>
  #include <io.h>
  #include <dos.h>
  #include <graph.h>
  #include <conio.h>
  #include "harderr.h"

  #define _PATHLENGTH_ 64
  #define _BUFFLENGTH_ 20
  #define INT24 0x24

  void (far *fptr)();   // Pointer to error-handler routine
                        // defined in HARDERR.C.
  void (interrupt far *OldHandler)(); // Pointer to the original
                                      // error-handler routine.

  void main(void);
  void print_errmsg(unsigned errcode);
  void print_codeloc(unsigned codeloc);
  void p_deverr(char error);

  char path[_PATHLENGTH_], buffer[_BUFFLENGTH_] = "Hello, World\n";

  void main(void)
  {
     int fh, ch;
     unsigned far *headptr;
     HEADER header;

     OldHandler = _dos_getvect(INT24); // Save original handler.
     fptr = handler;
     _harderr(fptr);  // Set harderr to handler()

     printf("Do you wish to reset the harderr handler? [y/n] ");
     ch = getche();

     if (toupper(ch) == 'Y')   // Reset to original handler.
        _dos_setvect(INT24, OldHandler);

     _clearscreen(_GCLEARSCREEN);
     printf("\nPlease enter path: ");
     fscanf(stdin, "%s", path);

     fh = open(path, O_CREAT | O_TEXT | O_RDWR, S_IWRITE);
     printf("\nOpening %s....\n", path);

     if (!is_harderr())    // Was open successful?
     {
        puts("No hardware error detected.");

        fh = fileno(stdprn);
        printf("\nPrinting %s....\n", path);
        write(fh, buffer, _BUFFLENGTH_);    // Write to the printer
     }

     if (is_harderr())   // Print hardware error diagnostics
     {
        puts("Hardware error!");
        if (is_diskerr())
        {
           printf("Drive         = %c:\n", getdrv());
           printf("Error code    = ");
           print_errmsg(get_errcode());
           printf("Code location = ");
           print_codeloc(get_codeloc());
        }
        else
        {
           headptr = get_devhdr(&header);
           // Implemented for illustration and for viewing in CodeView.

           if (is_char_dev())
              puts("Character device error!");
           else
              puts("Block device error!");
           printf("Error code = ");
           print_errmsg(get_errcode());
           printf("Code location = ");
           print_codeloc(get_codeloc());
           printf("Device name = %s\n", get_devname());
           printf("Device: ");
           p_deverr(get_curr_dev());
        }
        if (is_write_err())
           puts("Write error!");
        else
           puts("Read error!");
        hardreset();
     }
     else
        puts("No hardware error detected.");
     close(fh);
     exit(0);
  }

  void print_errmsg(unsigned errcode)
  {
     switch (errcode)
        {
     case WRITE_PROTECT_ERR:
        puts("WRITE PROTECT ERROR!");
        break;
     case UNKNOWN_UNIT:
        puts("UNKNOWN UNIT!");
        break;
     case DRIVE_NOT_READY:
        puts("DRIVE NOT READY!");
        break;
     case UNKNOWN_CMD:
        puts("UNKNOWN COMMAND!");
        break;
     case CRC_ERR:
        puts("CYCLIC REDUNDANCY CHECK!");
        break;
     case BAD_DRV_REQUEST:
        puts("BAD DRIVE REQUEST!");
        break;
     case SEEK_ERR:
        puts("SEEK ERROR!");
        break;
     case UNKNOWN_MEDIA:
        puts("UNKNOWN MEDIA!");
        break;
     case SECTOR_NOT_FOUND:
        puts("SECTOR NOT FOUND!");
        break;
     case OUT_OF_PAPER:
        puts("OUT OF PAPER!");
        break;
     case WRITE_FAULT:
        puts("WRITE FAULT!");
        break;
     case READ_FAULT:
        puts("READ FAULT!");
        break;
     case GENERAL_FAILURE:
        puts("GENERAL FAILURE!");
        break;
     default:
        puts("NO ERROR!");
        break;
        }
  }

  void print_codeloc(unsigned codeloc)
  {
     switch (codeloc)
        {
     case DOS:
        puts("MS-DOS");
        break;
     case FAT:
        puts("FAT");
        break;
     case DIRECTORY:
        puts("DIRECTORY");
        break;
     case DATA:
        puts("DATA");
        break;
     default:
        puts("ERROR PRINTING CODE LOCATION.");
        break;
        }
  }

  // Call p_deverr only if an error occurred on a device other than a
  // disk drive and if the error occurred on a default device. This
  // program uses only the default selection of the switch statement.
  void p_deverr(char error)
  {
     switch ( error )
        {
     case CURRENT_INPUT_DEVICE:
        puts("Current input device.");
        break;
     case CURRENT_OUTPUT_DEVICE:
        puts("Current output device.");
        break;
     case CURRENT_NULL_DEVICE:
        puts("Current null device.");
        break;
     case CURRENT_CLOCK_DEVICE:
        puts("Current clock device.");
        break;
     default:
        puts("No error on default device.");
        break;
        }
  }

Additional query words: kbinf 1.00 1.50 5.10 6.00 6.00a 6.00ax 7.00

======================================================================
Keywords          : kb16bitonly 
Technology        : kbVCsearch kbAudDeveloper kbCRT
Version           : winnt:

=============================================================================