CWinAPIException

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

The MFC library provides a series of classes for handling exceptions. For example, CFileException can be used for handling exceptions thrown from CFile methods, CMemoryException can be used for handling out-of-memory exceptions thrown by the new operator, and so on.

Sometimes you need to extend MFC by writing your own classes with corresponding exception classes. As long as usually you are using Windows API calls (like MFC generally does), an exception class for handling Windows API errors would be great.

First, let’s take a fast look into CException.

CException class

CException is the base class for all other MFC exception classes.
It has two virtual functions:

  • GetErrorMessage – gets a string which describes the error;
  • ReportError – displays a message box showing the error message.

If derive from CException, then you can override one or both of these virtual functions to provide your own error descriptions and message boxes. Now, we can go further and implement the exception class for generic WinAPI errors.

A lite CWinAPIException class

class CWinAPIException : public CException
{
public:
   CWinAPIException(DWORD dwError) : m_dwErrorCode(dwError) {}

// Attributes
private:
   DWORD m_dwErrorCode;    // error code given by Windows API

// Overrides
public:
   virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
      PUINT pnHelpContext = NULL);
   virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);
// ...
};

CWinAPIException overrides CException::GetErrorMessage and CException::ReportError (more info about implementation can be found in the attached sources). We could stop here, but no… CWinAPIException can do more.

An improved CWinAPIException class

Usually MFC exception classes only give the error description, as for example “Path not found”. Sometimes is useful to have more information such as the exception source (source file name and code line number). CWinAPIException can provide this additional information at the programmer’s request. Let’s say, in a beta release of an application we can choose to get all the information, while later in a more stable version we can set to display only the error description.
Here is the complete CWinAPIException class:

class CWinAPIException : public CException
{
   DECLARE_DYNAMIC(CWinAPIException)

// Constructor
private:
   CWinAPIException(DWORD dwError, LPCTSTR pszFile, UINT nLine, COleDateTime& odtTime);

// Attributes
private:
   DWORD m_dwErrorCode;    // error code given by Windows API
   CString m_strFile;      // the file from which the exception was thrown
   UINT m_nLine;           // the line from which the exception was thrown
   COleDateTime m_odtTime; // date/time when exception occurred

   // switches used for customizing displayed info
   static bool m_bShowErrorCode, m_bShowTimestamp, m_bShowSource;

// Overrides
public:
   virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
      PUINT pnHelpContext = NULL);
   virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);

// Operations
public:
   // method for throwing exceptions
   // NOTE: Prefer below macros instead of directly CWinAPIException::Throw call!
   static void Throw(DWORD dwError, LPCTSTR pszFile, UINT nLine);
   // accessors for custom display switches
   static void ShowErrorCode(bool bShowErrorCode) {m_bShowErrorCode = bShowErrorCode;}
   static void ShowSource(bool bShowSource) {m_bShowSource = bShowSource;}
   static void ShowTimestamp(bool bShowTimestamp) {m_bShowTimestamp = bShowTimestamp;}
   static bool IsErrorCodeShown() {return m_bShowErrorCode;}
   static bool IsSourceShown() {return m_bShowSource;}
   static bool IsTimestampShown() {return m_bShowTimestamp;}

// Implementation
private:
   void FormatMessage(CString& strText, LPCTSTR pszSep);
   void FormatMessageFromSystem(CString& strMessage);
};

There remains just few little things to add. When throwing an exception by calling CWinAPIException::Throw you have to pass the error code dwError, the source (full path and) file name pszFile, and the code line number nLine. The error code is usually retrieved with ::GetLastError.
For source file name and line number can be passed the predefined macros __FILE__ and __LINE__, respectively.
To make this task easier I have added the following macros to be used instead of CWinAPIException::Throw:

#define WINAPI_THROW_ERROR(e)          CWinAPIException::Throw(e, __FILE__, __LINE__)
#define WINAPI_THROW_LAST_ERROR        WINAPI_THROW_ERROR(::GetLastError())

// use this macro for functions which return 0 (FALSE) in case of failure
#define WINAPI_VERIFY_TRUE(r)          if(!r) WINAPI_THROW_LAST_ERROR;
// use this macro for functions which returns NULL in case of failure
#define WINAPI_VERIFY_NOT_NULL(r)      if(NULL == r) WINAPI_THROW_LAST_ERROR;
// use this macro which return ERROR_SUCCESS in case of success and an error code otherwise
#define WINAPI_VERIFY_ERROR_SUCCESS(r) if(ERROR_SUCCESS != r) WINAPI_THROW_ERROR(r);

Examples

  • Using WINAPI_VERIFY_TRUE to throw an exception in case of failure (the function returns FALSE)
       BOOL bRet = ::AppendMenu(hMenu, MF_STRING, nID, pszNewItem);
       WINAPI_VERIFY_TRUE(bRet);
    
  • Using WINAPI_VERIFY_NOT_NULL to throw an exception in case of failure (the function returns NULL)
       SC_HANDLE hSCManager = ::OpenSCManager(NULL, NULL, GENERIC_READ);
       WINAPI_VERIFY_NOT_NULL(hSCManager);
    
  • Using WINAPI_VERIFY_ERROR_SUCCESS to throw an exception in case of failure (the function returns an

    error code)

       UINT nRet = ::RegOpenKey(HKEY_LOCAL_MACHINE, pszPath, &hKey);
       WINAPI_VERIFY_ERROR_SUCCESS(nRet);
    
  • Catching CWinAPIException exceptions
       try
       {
          // some code which can throw CWinAPIException
       }
       catch(CWinAPIException* e)
       {
          e->ReportError();
          e->Delete();
       }
    
  • Prevent showing error code
       CWinAPIException::ShowErrorCode(false);
    

Demo application



ExceptionDemo is a simple SDI application that demonstrates how to use CWinAPIException. You can choose which additional info has to be shown (error code, source, and timestamp), then push “Test exception” button and enjoy.

Related articles

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read