100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C++

MFC Exception Handling

How MFC's CException hierarchy works, how to catch and clean up MFC exceptions correctly, and how they interact with standard C++ exceptions.

Collections and UtilitiesIntermediate9 min readJul 10, 2026
Analogies

Exception Model in MFC

MFC predates standardized C++ exception handling in Visual C++, so it built its own hierarchy rooted at CException, with derived classes including CMemoryException, CFileException, CDBException (for ODBC/DAO database errors), and CArchiveException (for serialization errors). Historically, code used the TRY, CATCH, AND_CATCH, and END_CATCH macros defined in afx.h, but these are thin wrappers around ordinary try/catch, and modern MFC code is expected to use standard C++ try/catch(CException* e) blocks directly instead.

🏏

Cricket analogy: CException's hierarchy branching into CFileException and CDBException is like an umpire's decision tree that starts at a general 'stoppage of play' ruling and then branches into specific causes like rain, bad light, or crowd invasion.

Catching and Deleting Exceptions

MFC exception objects are typically allocated on the heap by the code that throws them, which is why you catch by pointer, CException* e, rather than by value or reference. After handling the exception, older MFC code must explicitly call e->Delete() to free the object, since CException overrides Delete() to either destroy itself or, for statically allocated exceptions, do nothing safely; forgetting this call in legacy TRY/CATCH blocks is a classic source of memory leaks in older MFC applications.

🏏

Cricket analogy: Catching by pointer and manually deleting resembles a groundstaff crew that must specifically remove a temporary sightscreen erected for one session, rather than it disappearing automatically when the innings ends.

cpp
void CMyDoc::OpenSettingsFile()
{
    CFile file;
    try
    {
        if (!file.Open(_T("settings.dat"), CFile::modeRead))
            AfxThrowFileException(CFileException::fileNotFound);

        BYTE buffer[128];
        file.Read(buffer, sizeof(buffer));
    }
    catch (CFileException* e)
    {
        TCHAR szError[256];
        e->GetErrorMessage(szError, 256);
        AfxMessageBox(szError);
        e->Delete(); // required: MFC heap-allocates thrown exceptions
    }
}

CMemoryException and Resource Exhaustion

CMemoryException is thrown by MFC internals, such as collection classes growing their storage, when the underlying operator new fails to allocate the requested memory. Because the very act of handling a low-memory condition may itself require allocating something, MFC's design historically avoided allocating memory in the throw path where possible, and application code catching CMemoryException should keep its handler simple, for example just informing the user and shutting down cleanly rather than attempting further complex operations that could themselves fail.

🏏

Cricket analogy: CMemoryException resembles a ground running out of spare cricket balls mid-match after a series of lost boundaries, forcing officials to simplify the remaining play rather than attempt elaborate replacement logistics.

Best Practices: Mixing MFC and C++ Exceptions

Because CException is not derived from std::exception, a single catch(std::exception&) block will not catch MFC exceptions, and code that mixes MFC internals with standard library or third-party code needs separate catch clauses for CException* alongside standard try/catch(const std::exception&) handlers. New MFC applications are encouraged to write ordinary try/catch(CException* e) rather than the legacy TRY/CATCH/END_CATCH macros, since the macros exist purely for backward compatibility with pre-standard-C++-exception-handling code from early MFC versions.

🏏

Cricket analogy: Needing separate catch clauses for CException and std::exception is like a tournament needing separate protocols for cricket-specific disputes (DRS reviews) versus general venue emergencies (fire alarms), since one rulebook doesn't cover both.

The TRY, CATCH, AND_CATCH, and END_CATCH macros defined in afx.h expand to ordinary try/catch statements with some bookkeeping for older compilers that lacked full C++ exception support. There is no special runtime magic in them; they exist for source compatibility with MFC code written before Visual C++ had standardized exception handling.

Catching an MFC exception by value, catch(CFileException e) instead of catch(CFileException* e), will slice the object and can lead to subtle bugs, since MFC's exception classes are designed to be caught and manipulated by pointer. Combined with forgetting e->Delete(), this is one of the most common sources of leaks and incorrect error handling in legacy MFC code.

  • CException is the root of MFC's exception hierarchy, with CMemoryException, CFileException, CDBException, and CArchiveException as key derived classes.
  • TRY/CATCH/END_CATCH macros are thin, backward-compatible wrappers around ordinary try/catch.
  • MFC exceptions are heap-allocated and must be caught by pointer, e.g. catch(CFileException* e).
  • Call e->Delete() after handling a caught MFC exception in legacy code to avoid memory leaks.
  • CMemoryException handlers should stay simple since further allocation may also fail under low memory.
  • CException does not derive from std::exception, so mixed codebases need separate catch clauses for each.
  • Modern MFC code should prefer plain try/catch(CException* e) over the legacy macros.

Practice what you learned

Was this page helpful?

Topics covered

#MFCMicrosoftFoundationClassesStudyNotes#MicrosoftTechnologies#MFCExceptionHandling#MFC#Exception#Handling#Model#ErrorHandling#StudyNotes#SkillVeris