CWinAPIException

Ovidiu Cucu, 11.01.2011

Mediu: Viusal Studio, Windows

Introducere

Bibioteca MFC pune la dispozitie o serie de clase pentru tratarea exceptiilor. De exemplu, CFileException este utlizata pentru exceptiile aruncate din metodele clasei CFile, CMemoryException este pentru exceptiile aruncate de operatorul new s.a.m.d.
Cateodata este nevoie sa extindem MFC scriind propriile clase avand clasele corespunzatoare pentru exceptii. Atata timp cat de obicei apelam functii Windows API (asa cum face si MFC-ul), ar fi utila o clasa pentru tratarea generica a erorilor Windows API. Mai intai, sa aruncam o privire la CException.

Clasa CException

CException este clasa de baza pentru toate clasele MFC pentru exceptii.
Ea are doua functii virtuale:

  • GetErrorMessage - intoarce un string cu descrierea erorii;
  • ReportError - afiseaza mesajul de eroare intr-un message box.
Daca derivam din CException, putem suprascrie aceste functii pentru a furniza descrierile si mesajele necesare.

O clasa CWinAPIException simpla

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);
// ...
};
In CWinAPIException sunt suprascrise CException::GetErrorMessage and CException::ReportError. Implementarea lor se poate gasi in fisierele sursa atasate la articol.

O clasa CWinAPIException imbunatatita

De obicei, o clasa MFC pentru exceptii da doar descrierea erorii, de exemplu "Path not found". Cateodata este util sa avem mai multe informatii, cum ar fi sursa exceptiei (fisierul sursa si numarul liniei de cod de unde a fost aruncata). CWinAPIException poate furniza aceste informatii aditionale la cererea programatorului. Sa spunem ca intr-o versiune beta a aplicatiei putem alege sa primim toate informatiile, iar mai tarziu intr-o versine stabila sa lasam doar descrierea erorii. Iata clasa CWinAPIException completa:

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

Mai raman doar cateva mici lucruri de adaugat. Cand se arunca o exceptie apeland CWinAPIException::Throw, trebuie sa-i pasam codul de eroare dwError, numele fisierului sursa pszFile si numarul liniei de cod nLine. Codul de eroare este de obicei intors de functia API sau obtinut cu ::GetLastError. Pentru numele fisierului sursa si numarul liniei de cod se pot folosi macro-urile predefinite __FILE__ si respectiv __LINE__. Pestru a face treaba asta mai usor, am adaugat urmatoarele macro-uri pentru a fi utilizate in loc de 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 (zero) 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);

Exemple

  • Utilizare WINAPI_VERIFY_TRUE pentru a arunca o exceptie in caz ca functia intoarce FALSE
       BOOL bRet = ::AppendMenu(hMenu, MF_STRING, nID, pszNewItem);
       WINAPI_VERIFY_TRUE(bRet);
    
  • Utilizare WINAPI_VERIFY_NOT_NULL pentru a arunca o exceptie in caz ca functia intoarce NULL
       SC_HANDLE hSCManager = ::OpenSCManager(NULL, NULL, GENERIC_READ);
       WINAPI_VERIFY_NOT_NULL(hSCManager);
    
  • Utilizare WINAPI_VERIFY_ERROR_SUCCESS pentru a arunca o exceptie in caz ca functia intoarce codul de eroare
       UINT nRet = ::RegOpenKey(HKEY_LOCAL_MACHINE, pszPath, &hKey);
       WINAPI_VERIFY_ERROR_SUCCESS(nRet);
    
  • Prinderea exceptiilor tip CWinAPIException*
       try
       {
          // some code which can throw CWinAPIException
       }
       catch(CWinAPIException* e)
       {
          e->ReportError();
          e->Delete();
       }
    
  • Prinderea tuturor exceptiilor MFC
       try
       {
          // some code which can throw any MFC exception
       }
       catch(CException* e)
       {
          e->ReportError();
          e->Delete();
       }
    
  • Prinderea exceptiilor CWinAPIException* utilizand macro-urile TRY/CATCH/END_CATCH
       TRY
       {
          // some code which can throw CWinAPIException
       }
       CATCH(CWinAPIException, e)
       {
          e->ReportError();
       }
       END_CATCH
    
  • Pentru a nu mai afisa codul de eroare
       CWinAPIException::ShowErrorCode(false);
    

Aplicatia demo

ExceptionDemo este o aplicatie simpla SDI care demonstreaza cum se foloseate CWinAPIException. Puteti alege ce informatii vreti sa se afiseze apoi apasati butonul "Test exception".

Link-uri

Atasamente

Va rugam sa va autentificati pentru a descarca fisierele!

CWinAPIException.zip - Fisiere sursa
ExceptionDemo.zip - Proiect demo


Unelte: