Contributor: GAYLE DAVIS              

{$S-,R-,V-,I-,N-,B-,F-}

{$IFNDEF Ver40}
  {Allow overlays}
  {$F+,O-,X+,A-}
{$ENDIF}

UNIT CritErr;

INTERFACE

USES DOS;

TYPE
    Str10 = STRING[10];
    IOErrorRec = Record
                 RoutineName : PathStr;
                 ErrorAddr   : Str10;
                 ErrorType   : Str10;
                 TurboResult : Word;  { TP Error number }
                 IOResult    : Word;  { DOS Extended number }
                 ErrMsg      : PathStr;
                 End;


{}PROCEDURE IOResultTOErrorMessage (IOCode : WORD; VAR MSG : STRING);
{}PROCEDURE GetDOSErrorMessage (VAR Msg : STRING);
{}FUNCTION  UserIOError(ErrNum : INTEGER; VAR IOErr : IOErrorRec) : BOOLEAN;
{}PROCEDURE CriticalErrorDOS;
{}PROCEDURE CriticalErrorTP;
{}PROCEDURE CriticalErrorOwn(ErrAddr: POINTER);

IMPLEMENTATION

VAR
    TurboInt24: POINTER;        { Holds address of TP's error handler }

  function Hex(v: Longint; w: Integer): String;
  var
    s               : String;
    i               : Integer;
  const
    hexc            : array [0 .. 15] of Char= '0123456789abcdef';
  begin
    s[0] := Chr(w);
    for i := w downto 1 do begin
      s[i] := hexc[v and $F];
      v := v shr 4
    end;
    Hex := s;
  end {Hex};


PROCEDURE CriticalErrorDOS;

    BEGIN
        SetIntVec($24,SaveInt24);
    END;



PROCEDURE CriticalErrorTP;

    BEGIN
        SetIntVec($24,TurboInt24);
    END;



PROCEDURE CriticalErrorOwn(ErrAddr: POINTER);

    BEGIN
        SetIntVec($24,ErrAddr);
    END;



PROCEDURE GetDOSErrorMessage (VAR Msg : STRING);

TYPE pointerwords =
  RECORD
    ofspoint, segpoint : WORD;
  END;

VAR
  breakdown : pointerwords ABSOLUTE erroraddr;

BEGIN
IOResultToErrorMessage (ExitCode, MSG);
      WITH breakdown DO
      Msg := Msg + ' $' + hex (SegPoint, 4) + ':' + hex (OfsPoint, 4);
END;                          {Exitprogram}

PROCEDURE IOResultToErrorMessage (IOCode : WORD; VAR MSG : STRING);
BEGIN
      CASE IOCode OF
      $01 : msg := 'Invalid DOS Function Number';
      $02 : msg := 'File not found ';
      $03 : msg := 'Path not found ';
      $04 : msg := 'Too many open files ';
      $05 : msg := 'File access denied ';
      $06 : msg := 'Invalid file handle ';
      $07 : msg := 'Memory Control Block Destroyed';
      $08 : msg := 'Not Enough Memory';
      $09 : msg := 'Invalid Memory Block Address';
      $0A : msg := 'Environment Scrambled';
      $0B : msg := 'Bad Program EXE File';
      $0C : msg := 'Invalid file access mode';
      $0D : msg := 'Invalid Data';
      $0E : msg := 'Unknown Unit';
      $0F : msg := 'Invalid drive number ';
      $10 : msg := 'Cannot remove current directory';
      $11 : msg := 'Cannot rename across drives';
      $12 : msg := 'Disk Read/Write Error';
      $13 : msg := 'Disk Write-Protected';
      $14 : msg := 'Unknown Unit';
      $15 : msg := 'Drive Not Ready';
      $16 : msg := 'Unknown Command';
      $17 : msg := 'Data CRC Error';
      $18 : msg := 'Bad Request Structure Length';
      $19 : msg := 'Seek Error';
      $1A : msg := 'Unknown Media Type';
      $1B : msg := 'Sector Not Found';
      $1C : msg := 'Printer Out Of Paper';
      $1D : msg := 'Disk Write Error';
      $1E : msg := 'Disk Read Error';
      $1F : msg := 'General Failure';
      $20 : msg := 'Sharing Violation';
      $21 : msg := 'Lock Violation';
      $22 : msg := 'Invalid Disk Change';
      $23 : msg := 'File Control Block Gone';
      $24 : msg := 'Sharing Buffer Exceeded';
      $32 : msg := 'Unsupported Network Request';
      $33 : msg := 'Remote Machine Not Listening';
      $34 : msg := 'Duplicate Network Name';
      $35 : msg := 'Network Name NOT Found';
      $36 : msg := 'Network BUSY';
      $37 : msg := 'Device No Longer Exists On NETWORK';
      $38 : msg := 'NetBIOS Command Limit Exceeded';
      $39 : msg := 'Adapter Hardware ERROR';
      $3A : msg := 'Incorrect Response From NETWORK';
      $3B : msg := 'Unexpected NETWORK Error';
      $3C : msg := 'Remote Adapter Incompatible';
      $3D : msg := 'Print QUEUE FULL';
      $3E : msg := 'No space For Print File';
      $3F : msg := 'Print File Cancelled';
      $40 : msg := 'Network Name Deleted';
      $41 : msg := 'Network Access Denied';
      $42 : msg := 'Incorrect Network Device Type';
      $43 : msg := 'Network Name Not Found';
      $44 : msg := 'Network Name Limit Exceeded';
      $45 : msg := 'NetBIOS session limit exceeded';
      $46 : msg := 'Filer Sharing temporarily paused';
      $47 : msg := 'Network Request Not Accepted';
      $48 : msg := 'Print or Disk File Paused';
      $50 : msg := 'File Already Exists';
      $52 : msg := 'Cannot Make Directory';
      $53 : msg := 'Fail On Critical Error';
      $54 : msg := 'Too Many Redirections';
      $55 : msg := 'Duplicate Redirection';
      $56 : msg := 'Invalid Password';
      $57 : msg := 'Invalid Parameter';
      $58 : msg := 'Network Device Fault';
      $59 : msg := 'Function Not Supported By NETWORK';
      $5A : msg := 'Required Component NOT Installed';

      (* Pascal Errors *)
       94 : msg := 'EMS Memory Swap Error';
       98 : msg := 'Disk Full';
      100 : msg := 'Disk read error ';
      101 : msg := 'Disk write error ';
      102 : msg := 'File not assigned ';
      103 : msg := 'File not open ';
      104 : msg := 'File not open for input ';
      105 : msg := 'File not open for output ';
      106 : msg := 'Invalid numeric format ';
      150 : msg := 'Disk is write_protected';
      151 : msg := 'Unknown unit';
      152 : msg := 'Drive not ready';
      153 : msg := 'Unknown command';
      154 : msg := 'CRC error in data';
      155 : msg := 'Bad drive request structure length';
      156 : msg := 'Disk seek error';
      157 : msg := 'Unknown media type';
      158 : msg := 'Sector not found';
      159 : msg := 'Printer out of paper';
      160 : msg := 'Device write fault';
      161 : msg := 'Device read fault';
      162 : msg := 'Hardware Failure';
      163 : msg := 'Sharing Confilct';
      200 : msg := 'Division by zero ';
      201 : msg := 'Range check error ';
      202 : msg := 'Stack overflow error ';
      203 : msg := 'Heap overflow error ';
      204 : msg := 'Invalid pointer operation ';
      205 : msg := 'Floating point overflow ';
      206 : msg := 'Floating point underflow ';
      207 : msg := 'Invalid floating point operation ';
      390 : msg := 'Serial Port TIMEOUT';
      399 : msg := 'Serial Port NOT Responding';

     1008 : Msg := 'EMS Memory Swap Error '
      ELSE
          GetDosErrorMessage (Msg);
      END;
END;


FUNCTION  UserIOError(ErrNum : INTEGER; VAR IOErr : IOErrorRec) : BOOLEAN;
{ RETURN ALL INFO ABOUT THE ERROR IF IT OCCURED}
CONST
      ErrTitles : ARRAY [1..5] OF STRING [10] =
                  ('System', 'Disk', 'Network', 'Serial', 'Memory');

VAR
    Msg       : STRING;
    Regs      : REGISTERS;

    BEGIN

    UserIOError := FALSE;
    FILLCHAR(IOErr,SizeOf(IOErr),#0);
    IF ErrNum <=0 THEN EXIT;

    { GET DOS Extended Error }
    WITH Regs DO
    BEGIN
      AH := $59;
      BX := $00;
      MSDOS (Regs);
    END;

    IOResultToErrorMessage (Regs.AX, Msg);

    IOErr.RoutineName  := PARAMSTR (0);
    IOErr.ErrorAddr    := Hex (SEG (ErrorAddr^), 4) + ':' + Hex (OFS (ErrorAddr^), 4);
    IOErr.ErrorType    := ErrTitles[Regs.CH];
    IOErr.TurboResult  := ErrNum;
    IOErr.IOResult     := Regs.AX;
    IOErr.ErrMsg       := Msg;

    UserIOError        := (ErrNum > 0);
    END;

BEGIN
 GetIntVec($24,TurboInt24);
 CriticalErrorDOS;
END.

{ --------------------------     DEMO  --------------------- }

{ EXAMPLE FOR CRITICAL ERROR HANDLER UNIT }
{ COMPILE AND RUN FROM DOS !!!   WILL NOT WORK PROPERLY FROM THE IDE }
{$I-}   { A MUST FOR THE CRITICAL HANDLER TO WORK !!!! }

USES
  CRT, CRITERR;

VAR
  f:  TEXT;
  i:  INTEGER;
  ErrMsg : STRING;
  IOErr  : IOErrorRec;

BEGIN
    ClrScr;
    WriteLn(' EXAMPLE PROGRAM FOR CRITICAL ERROR HANDLER ');
    WriteLn;
    WriteLn('Turbo Pascal replaces the operating system''s critical-error');
    WriteLn('handler with its own.  For this demonstration we will generate');
    WriteLn('a critical error by attempting to access a diskette that is not');
    WriteLn('present.  Please ensure that no diskette is in drive A, then');
    WriteLn('press RETURN...');
    ReadLn;
    CriticalErrorTP;
    Assign(f,'A:NOFILE.$$$');
    WriteLn;
    WriteLn('Now attempting to access drive...');
    Reset(f);
    IF UserIOError(IOResult,IOErr) THEN
       BEGIN
       WriteLn(IOErr.RoutineName);
       WriteLn(IOErr.ErrorAddr);
       WriteLn(IOErr.ErrorType);
       WriteLn(IOErr.TurboResult);
       WriteLn(IOErr.IOResult);
       WriteLn(IOErr.ErrMsg);
       END;
    WriteLn;
    Write('Press RETURN to continue...');
    ReadLn;
    WriteLn;
    CriticalErrorDOS;
    WriteLn('With the DOS error handler restored, you will be presented');
    WriteLn('with the usual "Abort, Retry, Ignore?" prompt when such an');
    WriteLn('error occurs.  (Later DOS versions allow a "Fail" option.)');
    WriteLn('Run this program several times and try different responses.');
    Write('Press RETURN to continue...');
    ReadLn;
    WriteLn('Now attempting to access drive again...');
    Reset(f);
    IF UserIOError(IOResult,IOErr) THEN
       BEGIN
       WriteLn(IOErr.RoutineName);
       WriteLn(IOErr.ErrorAddr);
       WriteLn(IOErr.ErrorType);
       WriteLn(IOErr.TurboResult);
       WriteLn(IOErr.IOResult);
       WriteLn(IOErr.ErrMsg);
       END;
    Readkey;
END.