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);
    }