Using _harderr to Capture Critical Error Interrupt 24h

ID: Q32309


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++ for Windows, 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
Version : winnt:
Platform : winnt
Issue type :


Last Reviewed: January 3, 2000
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.