Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Handling OS/2 Error Codes


AUG90: HANDLING OS/2 ERROR CODES

Simplify the tracking and correcting of OS/2 development errors with this utility

This article contains the following executables: MAK.LST

Nico Mak is a software developer for Mansfield Software Group in Storrs, Conn. He can be reached at 70056, 241 on CompuServe or as Nico_Mak on BIX.


This article presents OS2ERR, a simple and effective system to handle unexpected OS/2 error codes in Microsoft C programs. When an OS/2 system function returns an error, OS2ERR displays a pop-up screen with details about the error. It then gives you a choice of aborting or continuing the program that caused the error. The pop-up screen contains the program name, process ID, thread ID, error code, error classification, and the recommended action. It also lists the source code filename, line number, and source code that called the system function. The system error message (if any) for the error code is displayed. This information greatly simplifies tracking and correcting the problem. Figure 1 shows a typical sample output.

Figure 1: Sample pop-up screen

  Error code:    6
  Program:       D:\DDJ\ERRDEMO.EXE
  Command tail:  test
  Process ID:    98
  Thread ID:     1
  Action:        Abort in an orderly manner
  Locus:         Unknown
  Class:         Application error
  Source name:   D:\DDJ\ERRDEMO.C
  Source line:   11 (DosRead (hf, Buf, sizeof(Buf), &cbRead))

Incorrect internal file identifier.
Press Esc to abort or any other key to continue

This detailed error report saves time during application development and testing, but is generally inappropriate for a final product. OS2ERR was designed to be used in conjunction with typical error handling routines and is easy to "turn off." No source code changes are needed to disable OS2ERR processing -- just recompile and relink as described later in this article. Or you can customize OS2ERR for your production environment.

Using OS2ERR

OS2ERR is easy to use. First include OS2ERR.H (see Listing One, page 181) at the beginning of your source program, after including the OS/2 header files. Then use the os2chk() macro on any OS/2 system function to display the pop-up screen if the function generates an error. See Example 1 for an example of how to use this macro. Alternately, you can use the os2() macro on system functions to record the error code, filename, line number, and source line for possible later use in an error handling routine. The poperr() macro will display the information recorded by the os2() macro along with other details about the error. See Example 2 for an example of how to use these macros.

Example 1: Examples of OS/2chk() macro

  /* examples of os2chk() macro */
  os2chk (DosClose(hanConfig));    /* display pop-up screen w/error info */
  os2chk (VioGetFont (&viofi, 0)); /* if system calls return error codes */

Example 2: Examples of OS/2() and poperr() macros

     /* examples of os2 () and poperr() macros */

  SubCommandProcess()
    {

    if (!set jmp (jmpbuf)) {                 /* set up error handler */

      /* ... processing ... */

      err == os2 (VioGetFont (&viofi, 0)); /* get current font info */
      if (err == ERROR_VIO_EXTENDED_SG)    /* if running in a VIO window */
          InVioWindow = YES;               /* remember we're in window */
      else if (err)                        /* if any other VioGetFont
                                              error */
             poperr();            /* display pop-up screen with error
                                     info */

      /* ... more processing ... */

      if (os2(DosClose (hanConfig))  /* if DosClose returns an error
                                        code */
           long jmp (jmpbuf);     /* skip to application's error
                                     handler */

      /* ... yet more processing ... */

      }
  else
      poperr(); /* error handler - display pop-up screen with error info */

Note that OS2ERR waits until any existing pop-up screen is closed before displaying its own pop-up screen. The os2chk() and poperr() macros should therefore not be used while your application is displaying a pop-up screen.

To enable OS2ERR processing while testing your application, compile your programs with the -- DOS2ERR option, and link your application with OS2ERR.OBJ. If this option is NOT used, then the macros do not generate code, and OS2ERR is disabled. When OS2ERR is disabled you do not need to link with OS2ERR.OBJ.

I've tested OS2ERR with Microsoft C, Version 5.1, and IBM OS/2, Version 1.10, and with the Microsoft OS/2 Software Development Kit, Version 1.06. Only one minor change was needed when switching among these environments: The DosGetPid and VioWrtTTy system functions were renamed to DosGetPID and VioWrtTTY in the SDK.

OS2ERR Components and Operation

OS2ERR.H defines the os2(), os2chk(), and poperr() macros. These macros generate calls to routines in OS2ERR.C (see Listing Two, page 181). The os2() and os2chk() macros pass the following information to these routines: the source code of the OS/2 system function, as provided by the ANSI C stringizing operator (#), the file name and line number, as provided by the __FILE__ and __LINE__ predefined identifiers, and the return code from the OS/2 system function.

OS2ERR.C contains routines to display the pop-up screen, error messages, and handle the abort or continue prompt. In addition to the information passed by the os2() and os2chk() macros, these routines use the DosGetEnv function to access the program name and command tail, the DosGetPID function to report on the process and thread IDs, the DosErrClass function to determine the system recommended action, locus, and error class, and the DosGetMessage function to obtain the system error message text. They use the VioPopUp and VioEndPopUp functions to manage the pop-up screen, and call KbdCharIn to wait for the user to press a key. If the user presses Esc to abort, the DosExit function is invoked to terminate the process.

Conclusion

OS2ERR has helped me to quickly identify the source of a number of OS/2 development problems. I hope it also saves you time. If you make enhancements to OS2ERR, I encourage you to upload the modified code to the Dr. Dobb's Forum on CompuServe. I've already uploaded a version that includes the system identifier for the error code (from BSEERR.H) if there is no system message available for a particular error. Comments and suggestions are welcome.

_HANDLING OS/2 ERROR CODES_ by Nico Mak

[LISTING ONE]

<a name="01be_000b">


/* OS2ERR.H version 1.0, March, 1989 */
/* Include this file after including OS2.H or OS2DEF.H */

#ifdef OS2ERR
    USHORT APIENTRY xos2chk(PSZ, USHORT, PSZ, USHORT);
    USHORT APIENTRY xos2(PSZ, USHORT, PSZ, USHORT);
    VOID   APIENTRY xpoperr(VOID);
    #define os2chk(ErrCode) (xos2chk(__FILE__, __LINE__, #ErrCode, ErrCode))
    #define os2(ErrCode) (xos2(__FILE__, __LINE__, #ErrCode, ErrCode))
    #define poperr() (xpoperr())
#else
    #define os2chk(ErrCode) (ErrCode)
    #define os2(ErrCode) (ErrCode)
    #define poperr()
#endif



<a name="01be_000c"><a name="01be_000c">
<a name="01be_000d">
[LISTING TWO]
<a name="01be_000d">

/* OS2ERR.C version 1.1, April 1989 by Nico Mak */
/* These functions are called by the macros in OS2ERR.H */
/* Compile this program with the options you use for the rest of */
/* your application, and link your application with OS2ERR.OBJ */

#define MAX_SAVE_ENTRIES 7 /* maximum number of threads that will use OS2ERR */

#include <stdio.h>      /* definitions/declarations for standard I/O routines */
#define INCL_BASE       /* to include all of OS/2 base */
#include "os2def.h"     /* include OS/2 common definitions */
#include "bse.h"        /* include OS/2 base definitions */
#define OS2ERR          /* to include OS2ERR declarations and macros */
#include "os2err.h"     /* include OS2ERR declarations */

typedef struct _SAVEINFO {                  /* save */
    TID tid;
    PSZ pszFileName;
    USHORT usLineNumber;
    PSZ pszLineSource;
    USHORT usErrCode;
    } SAVEINFO, FAR *PSAVEINFO;

SAVEINFO save[MAX_SAVE_ENTRIES];
USHORT cSavedEntries = 0;   /* number of threads that have used OS2ERR */
BOOL fOverFlow = 0;         /* 1 when all entries in "save" table are full */

/* DosErrClass error classifications */
PSZ pszClass[] = {
    "",
    "Out of Resource",
    "Temporary Situation",
    "Permission problem",
    "Internal System Error",
    "Hardware Failure",
    "System Failure",
    "Application Error",
    "Not Found",
    "Bad Format",
    "Locked",
    "Media Failure",
    "Collision with Existing Item",
    "Unknown/other",
    "Can't perform requested action",
    "Time-out",
    };

/* DosErrClass recommended actions */
PSZ pszAction[] = {
    "",
    "Retry immediately",
    "Delay and retry",
    "User error, get new values",
    "Abort in orderly manner",
    "Abort immediately",
    "Ignore this error",
    "Retry after user intervention",
    };

/* DosErrClass locus */
PSZ pszLocus[] = {
    "",
    "Unknown",
    "Disk",
    "Network",
    "Serial device",
    "Memory parameter",
    };

CHAR szWaitMsg[] = "\nPress Esc to abort process or any other key to continue";
CHAR szErrTID[] = "\npoperr() error: no information saved for this thread\n\r";
CHAR szErrOverFlow[] = "\npoperr() error: thread storage area overflowed\n\r";
CHAR szBuffer[400];

USHORT PASCAL PszLen(PSZ psz); /* function returns length of a far string */
PSAVEINFO PASCAL FindSaveEntry(TID); /* function returns PSAVEINFO for TID */

/* xos2chk - handle error (if any) and return error code */
USHORT APIENTRY xos2chk(PSZ pszFileName, USHORT usLineNumber,
 PSZ pszLineSource, USHORT usErrCode)
    {
    if (xos2(pszFileName, usLineNumber, pszLineSource, usErrCode))
        xpoperr();
    return usErrCode;
    }

/* xos2 - save error information and return error code */
USHORT APIENTRY xos2(PSZ pszFileName, USHORT usLineNumber,
 PSZ pszLineSource, USHORT usErrCode)
    {
    PSAVEINFO psave;
    PIDINFO pidi;

    if (usErrCode)
        {
        DosGetPID(&pidi);
        if ((psave = FindSaveEntry(pidi.tid)) == NULL)
            {
            DosEnterCritSec();
            if (cSavedEntries < MAX_SAVE_ENTRIES)
                psave = save + cSavedEntries++;
            else
                fOverFlow = 1;
            DosExitCritSec();
            }
        if (psave)
            {
            psave->tid = pidi.tid;
            psave->pszFileName = pszFileName;
            psave->usLineNumber = usLineNumber;
            psave->pszLineSource = pszLineSource;
            psave->usErrCode = usErrCode;
            }
        }
    return usErrCode;
    }

/* xpoperr - display pop-up screen with error information, return error code */
VOID APIENTRY xpoperr(VOID)
    {
    KBDKEYINFO kbci;
    PIDINFO pidi;
    PSZ psz;
    USHORT usClass, usAction, usLocus, usWait, usEnviron, usOffsetCmd, cbMsg;
    PSAVEINFO psave;

    /* open a pop-up screen */
    usWait = VP_WAIT | VP_OPAQUE;
    VioPopUp(&usWait, 0);

    DosGetPID(&pidi);
    if ((psave = FindSaveEntry(pidi.tid)) == NULL)
        {
        VioWrtTTY(szErrTID, sizeof(szErrTID), 0);
        if (fOverFlow)
            VioWrtTTY(szErrOverFlow, sizeof(szErrOverFlow), 0);
        }
    else
        {
        /* display error code, command name, and command tail */
        sprintf(szBuffer, "Error Code:   %u\n\r", psave->usErrCode);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);
        DosGetEnv(&usEnviron, &usOffsetCmd);
        for (psz = MAKEP(usEnviron, 0); *psz; psz += PszLen(psz) + 1)
           ;
        psz += 1;
        sprintf(szBuffer, "Command:      %Fs\n\r", psz);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);
        psz += PszLen(psz) + 1;
        psz += PszLen(psz) + 1;
        sprintf(szBuffer, "Command tail: %Fs\n\r", psz);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);

        /* display process id, thread id, and DosErrClass information */
        sprintf(szBuffer, "Process ID:   %u\n\rThread ID:    %u\n\r",
         pidi.pid, pidi.tid);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);
        DosErrClass(psave->usErrCode, &usClass, &usAction, &usLocus);
        sprintf(szBuffer,
         "Action:       %Fs\n\rLocus:        %Fs\n\rClass:        %Fs\n\r",
         pszAction[usAction], pszLocus[usLocus], pszClass[usClass]);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);

        /* display source filename, line number, and source code */
        sprintf(szBuffer, "Source name: %Fs\n\rSource line: %d (%Fs)\n\r\n\r",
         psave->pszFileName, psave->usLineNumber, psave->pszLineSource);
        VioWrtTTY(szBuffer, PszLen(szBuffer), 0);

        /* display system message (if any) */
        if (!DosGetMessage(0, 0, szBuffer, sizeof(szBuffer), psave->usErrCode,
          "OSO001.MSG", &cbMsg))
            VioWrtTTY(szBuffer, cbMsg, 0);
        }

    /* wait for a keypress */
    VioWrtTTY(szWaitMsg, sizeof(szWaitMsg), 0);
    do {
        KbdCharIn(&kbci, IO_WAIT, 0);
        } while (!(kbci.fbStatus & 0x40));

    /* close the pop-up screen and abort if requested */
    VioEndPopUp(0);
    if (kbci.chChar == 27 && kbci.chScan == 1)
        DosExit(EXIT_PROCESS, 1);
    }

/* PszLen - return length of a far string */
USHORT PASCAL PszLen(PSZ psz)
    {
    PSZ pszSearch = psz;

    while (*pszSearch)
        pszSearch++;
    return pszSearch - psz;
    }

/* FindSaveEntry - return pointer to SAVEINFO for a thread ID */
PSAVEINFO PASCAL FindSaveEntry(TID tid)
    {
    PSAVEINFO psave;

    for (psave = save; psave < save + cSavedEntries; ++psave)
        if (psave->tid == tid)
            return psave;
    return NULL;
    }



[Example 1: Examples of OS/2chk() macro]

os2err(DosClose(hanConfig));    /* display pop-up screen w/error info */
os2err(VioGetFont(&viofi, 0));  /* if system calls return error codes */


[Example 2: Examples of OS/2() and poperr() macros]

SubCommandProcess()
    {

    if (!setjmp(jmpbuf)) {                         /* set up error handler */

        /* ... processing ... */

        err == os2(VioGetFont(&viofi, 0));        /* get current font info */
        if (err == ERROR_VIO_EXTENDED_SG)    /* if running in a VIO window */
            InVioWindow = YES;                 /* remember we're in window */
        else if (err)                     /* if any other VioGetFont error */
            poperr();             /* display pop-up screen with error info */

        /* ... more processing ... */

        if (os2(DosClose(hanConfig)) /* if DosClose returns an error code */
            longjmp(jmpbuf);       /* skip to application's error handler */

        /* ... yet more processing ... */

        }
    else
        poperr(); /* error handler - display pop-up screen with error info */
    }










Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.