Error Handling Strategies

Environment: VC++

Introduction

Error handling is one of the important tasks of writing software. Many developers do not want to spend time on such tasks. But it usually leads to serious problems and projects failing.

To effectively handle errors, you need to formalize a unique approach for each project. Also, if it is possible, identify unique base concepts for a whole company or companies.

Common Rules of Error Handling

  1. Do not leave unsuccessful results unchecked.
  2. Initialize pointers with nulls. Check or establish a null condition before operating with pointed memory.
  3. Minimize the usage of static-length buffers. Use dynamic allocation or appropriate classes instead.
  4. Write readable code.
  5. Use standard or already defined error codes if it is possible.
  6. Create a text document and list all possible error codes and custom error descriptions (if any) generated by the program.
  7. Use string resources to specify error-description templates. This will help to create international applications and maintain them in one place.
  8. Use the Event Log only for serious system errors, such as disk failure or SEH errors.

Using C++ Exceptions

  1. Usage of C++ exceptions is the preferred error-handling strategy.
  2. It is preferable to use exception classes. Base libraries provide their own exceptions classes: MFC, CException, Standard C++ library exception, Compiler COM support—_com_error, and so forth. For COM errors, use the following prototype static function:

    LocalErrors::ThrowError(HRESULT errorCode,
                 LPCTSTR errorDescription, .);
    LocalErrors::ThrowError(HRESULT errorCode,
                 UINT stringResourceId, .);
    
    Win32 error codes can be transformed to HRESULT by using the HRESULT_FROM_WIN32 macro. Preferable exception classes are from the library used. In cases of using several libraries in one project, the preferable ones are MFC and Compiler COM support exception classes for Windows-only projects. For cross platform projects, use the Standard C++ library exception classes, its derivations, and simple types.
  3. A function exception specification. To create error-proof code and to avoid unhandled exceptions, use explicit function exception specification. It helps to understand which exceptions can be thrown by the function.
  4. void AnyFunction() throw(_com_error, CException);
    void FunctionWithoutExceptions() throw();
    
  5. Handling exceptions. To handle exceptions, use try/catch statements.
  6. try
    {
      // Statements that can throw an exception.
      AnyFunction();
    }
    catch(_com_error &e)
    {
    }
    catch(CException *ex)
    {
      ex->Delete();
    }
    catch(.)
    {
      // Unhandled exceptions catches here.
    }
    
  7. Using old-style error handling. In a few circumstances, using exceptions is impossible or inconvenient. The example is writing a small COM object using ATL. Using the _ATL_MIN_CRT definition requires not using exceptions. This case should be handled using a resulting return value.

    HRESULT GetQueryString(CComBSTR &queryString);
    DWORD GetQueryString(CComBSTR &queryString);
    BOOL OpenSource();
    

    Use the following prototype static members to operate with detailed error information.

    LocalErrors::SetLastError(HRESULT errorCode,
                 LPCTSTR errorDescription, .);
    LocalErrors::SetLastError(HRESULT errorCode,
                 UINT stringResourceId, .);
    HRESULT LocalErrors::GetLastError(LPCTSTR
                         *errorDescription = 0);
    
    Place LocalErrors::SetLastError where the error must be described. Use Structured Exception Handling (SEH) where it's needed and transform to resulting code.
  8. Mixed error handling. Usage of C++ exceptions is preferable. Thus, error result codes must be thrown as appropriate exceptions.
  9. HRESULT hr = GetQueryString(queryString);
    if(FAILED(hr))
    {
      throw _com_error(hr); // Or _com_issue_error(hr);
    }
    
    or
    BOOL isSuccessful = OpenSource();
    if(!isSuccessful)
    {
      LPCTSTR errorDescription = 0;
      HRESULT hr = LocalErrors::GetLastError(&errorDescription)
      ThrowError(hr, errorDescription);
    }
    


Comments

  • Common professional methods:

    Posted by Legacy on 11/27/2002 12:00am

    Originally posted by: vgrigor

    First you can read articles on BugsLayer in msdn,
    witt very right humor - according to the problem:
    like "Bugs- the coolest thing ever".

    Concrete:
    You must organize your check into some infrastructure,
    simple for simple case, not simple for thoses with reliability needs.
    Make your systev fit your error apperance methods,
    dividinf them to technology groups and create specializing good designed classes according to your complexity types.


    Right Style:
    Recomendations - select and proceed with common policy
    architecture
    for your app, according to your reliability purpose.
    Following that you''ll receive better app, and than
    ex- and implicitly reliabilty.
    You'll economy debug and maintanance time ("and be not fired" for clients wishes).

    For harder technics:

    Technique:
    internal:
    - Adopt some programming "Validating" architecture
    into your app. And handle your app.
    External
    - Check resources used by app, for exceeding critical conditions and catch them to begin use of internal mechanics.

    Than you'll receive some sheet, do not worry,
    but it will be "right" managed as you desighned
    smaller system - error handling.

    Reply
  • Let the compiler do the work..

    Posted by Legacy on 11/24/2002 12:00am

    Originally posted by: David V. Corbin

    Your points are valid, but a number of the issues (initialize pointers, check pointer, check return codes) can all be accomplished with the use of smart pointers.

    Remember a well written object that encapsulates behaviour is ALWAYS better than counting on the human to get it right every time.

    Reply
  • pro-active debugging technics

    Posted by Legacy on 11/23/2002 12:00am

    Originally posted by: dan

    interesting article...

    i learned that while developping and testing i should trace, trace, trace, ASSERT ASSERT and ASSERT, and it works, fixing bugs while cooding is kool because bugs are not kool, and waiting for the end-user to find bugs is bad software engineering....then when building the released version all that tracing and assertions just go away...my mentor on that matter is John Robbins and his book "Debugging applications" (microsoft press). If you have real practice interest in building the best bug free app. read it and use that technics...

    thanks code guru, and thanks Robbins....
    regards. dan.

    Reply
  • One more thing

    Posted by Legacy on 11/22/2002 12:00am

    Originally posted by: TheVoodoo

    Another good technique that I always use is write out a list of all possible error codes that can be thrown along with the line number that throws them, and all possible functions which can call it if it happens to be a seperate function independant of the main body.

    Reply
  • exception hater

    Posted by Legacy on 11/22/2002 12:00am

    Originally posted by: Greg

    Just wondering out there who else dislikes the try-catch exception setup? I find this be be more of a fancy class version of the GOTO statement. Like GOTO, throwing and catching exceptions skips over code that might be better off skipping with a condition. Then you know why the code is passed over. Just my thoughts.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: May 6, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT While you likely have very good reasons for remaining on WinXP after end of support -- an estimated 20-30% of worldwide devices still are -- the bottom line is your security risk is now significant. In the absence of security patches, attackers will certainly turn their attention to this new opportunity. Join Lumension Vice President Paul Zimski in this one-hour webcast to discuss risk and, more importantly, 5 pragmatic risk mitigation techniques …

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds