Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

MFC Error and Exception Handling

This section introduces the exception-handling mechanisms available in MFC. The two available mechanisms are

MFC exception macros should be used only if they already reside in existing code. Better yet, when dealing with an older program using MFC exception macros, use C++ exceptions along with the macros.

MFC Exception Macros

MFC versions lower than 3.0 did not support the C++ exception mechanism. MFC provided macros to deal with exceptions. The macros are TRY, CATCH, and THROW. In contrast, the C++ exception keyword equivalents are try, catch, and throw.

Here is a list of the MFC exception macros:

THROW

This macro throws the exception to the CATCH block. The program execution is interrupted. If a CATCH block is specified, program control goes to the CATCH block. If not, control is passed to an MFC library module to print an error message and exit.

THROW_LAST

This macro throws a locally created exception. The macro sends the exception back to the next CATCH block. Usually when an exception that was just caught is thrown, it goes out of scope, thus being deleted. THROW_LAST passes the exception correct to the next CATCH handler.

TRY

You use this macro to set up a TRY block, which signifies the block of code where the throwing of exceptions occurs. The exceptions are processed in the CATCH and AND_CATCH blocks. The TRY block is ended with an END_CATCH or END_CATCH_ALL macro.

CATCH

This macro defines a block of code that catches the first exception thrown by a preceding TRY block. The TRY block should end with END_CATCH.

AND_CATCH

You use this macro to catch additional exceptional types thrown from a preceding TRY block. Use the CATCH macro to catch the first exception type, and then use AND_CATCH for every other exception type. The TRY block should end with END_CATCH.

AND_CATCH_ALL

This macro catches additional exception types thrown by a preceding TRY block. Use the CATCH macro to catch the first exception type, and then use AND_CATCH_ALL for all other exception types. The TRY block should end with END_CATCH_ALL.

END_CATCH

This macro signifies the end of the last CATCH or AND_CATCH block.

END_CATCH_ALL

Signifies the end of the last CATCH_ALL or AND_CATCH_ALL block.

Here are some possible program skeletons using these macros:

TRY
   {

   }
   CATCH()
   {

   }
   AND_CATCH()
   {

   }
   END_CATCH

and

TRY
{

}
CATCH_ALL()
{

   THROW_LAST();
}
END_CATCH_ALL

This should give you an idea of how these macros go together.

Here is a list of MFC exception-throwing functions:

These functions are explained in the following sections.

AfxThrowArchiveException

This function throws an archive exception by specifying an integer that indicates the reason for the exception stored in its m_cause member. Here are the enumerators that can be returned:

AfxThrowFileException

This function throws a file exception by specifying an integer that indicates the reason for the exception. The developer is responsible for determining the cause of the error based on the operating-system error code. Here are the possible enumerators that can be returned:

AfxThrowMemoryException

This function throws a memory exception. This function should be called when using memory allocators, such as GlobalAlloc and malloc functions.

AfxThrowNotSupportedException

This function throws an exception when an unsupported feature is requested.

AfxThrowResource

This function throws a resource exception.

AfxThrowResourceException

This function throws a resource exception when an unsupported feature is requested.

AfxThrowUserException

This function throws an exception to stop an end-user operation. AfxMessageBox normally calls this function after it reports an error to the user. The MFC OLE exception functions follow:

AfxThrowOleDispatchException

This function throws an exception within an OLE automation.

AfxThrowOleException

This function throws an exception and creates an object of type COleException. The MFC termination function is AfxAbort.

AfxAbort

This function is called internally by the MFC member function when a fatal error occurs. This is the default termination for MFC. AfxAbort usually is called by MFC when an exception occurs that cannot be handled. AfxAbort can be called by the developer when an error is encountered that makes recovery impossible.

Using MFC Exception Macros with C ++ Exception Handling

You can use MFC exception macros and C++ exception keywords in the same program. Again, it is advisable to do this only when MFC exception macros already exist in the program. Always use C++ exception handling to handle exceptions.

Note, however, that you cannot use MFC macros together with C++ exception keywords in the same block. The reason is that the macros automatically delete exception objects when their scope is lost. Caught C++ exception keywords in a catch block have to be explicitly deleted. When MFC macros and C++ exception keywords are mixed, memory leaks can arise if an exception object is not deleted; or, when an exception is deleted twice.

The Advantages of Converting from MFC Exception Macros

It is almost never necessary to convert from MFC exception macros, because they are compatible with even the latest version of MFC. In fact, MFC macros can coincide with C++ exception handling, but there are advantages to converting.

One advantage is that the code that uses C++ exception handling compiles slightly smaller EXEs and COMs. Another reason to convert is that C++ exception-handling keywords are much more versatile: They can handle exceptions of all data types (that can be copied). The macros can handle only exceptions of the class CException and classes derived from it.

With C++ exception handling, the caught exception is deleted only by explicit instruction. Macros delete the caught exception when the exception goes out of scope.

Converting Code from MFC Macros to C++ Exception Handling

Perform the following steps to convert code from macros to C++ exception keywords:

  1. Find every occurrence of the MFC macros THROW, THROW_LAST, TRY, CATCH, AND_CATCH, and END_CATCH.
  2. Replace each THROW with throw.
  3. Replace each THROW_LAST with throw.
  4. Replace each TRY with try.
  5. Replace each CATCH with catch.
  6. Replace each AND_CATCH with catch.
  7. Delete each END_CATCH.
  8. Modify each macro argument so that it forms a valid C++ exception declaration.
  9. Modify each catch block so that it deletes exception objects where necessary.

Not too difficult, really. The only step that may require more explanation is number 8. Consider this example:

CATCH( CException, e )

to

catch( CException* e )

Predefined Exceptions for MFC

Here is a list of predefined exceptions to use the MFC. Each of these exceptions is derived from the base class CException. You can use these with C++ exceptions directly or with the MFC exception macros.

Using CFileException

For a quick introduction, take a look at the following code fragment, which uses CFileException:

//Try Block
try
{
  CFile fileNotOpen
    fileNotOpen.SeekToBegin();
}
catch(CFileException* e)  //Catch the file exceptions
{
 if (e->m_cause == CFileException::fileNotFound)
    AfxMessageBox("The File has not been opened.");
 e->Delete();
}

The try block guards the code to perform file operations. When a CException is raised, the catch block will catch the thrown exception and create the exception class object e. Note that this is very similar to C++ exception examples in the earlier section. Object e now points to any exception that was created. If this exception is equal to fileNotFound, a message appears telling the user that The File has not been opened. The final statement deletes the exception class object.

It is important to delete the exception class objects when you are finished with them. Unlike C++ exception handling, this is not taken care of automatically.

There are a few cause codes besides CFileException:

You can trigger this exception by calling the AfxThrowFileException() function. The function can take three parameters: The first one is required, and the other two are optional.

The first parameter, cause, is a cause code for the exception (listed earlier). It is this code that is placed in the m_cause member in the catch group.

The second parameter, lOsError, is used to specify an operating system error code. The third parameter is strFileName. It gets placed in the m_strFileName member string variable and holds the filename of the file that was being accessed when the error occurred.

Using CMemoryException

The CMemoryException exception is raised when the program simply can no longer allocate any more memory. Recovering from such an error is difficult and unlikely. The memory allocation functions C++ uses will trigger this exception.

This exception can be triggered by calling this function:

AfxThrowMemoryException()

Using CResourceCollection

The CResourceCollection exception is raised when the application can't allocate or find the requested system resources.

This exception can be triggered by calling the AfxThrowResourceException() function.

Using CArchiveException

The CArchiveException object represents a serialization exception condition. The class includes a public data member that contains the cause of the exception.

The m_cause member of the CArchiveException class holds the archive-specific cause codes, as listed here:

Using CDaoException

The CDaoException is used for DAO-based database access.

You can throw this exception by calling the AfxThrowDaoexception() function. This function has two optional parameters: nAfxDaoError and an OLE scode value. The nAfxDaoError is a DAO error code specific to DAO. The errors it returns are always specific to the DAO itself. The OLE scode value comes from a DAO-based OLE call.

The nAfxDaoError codes can be any from the following list:

Using OLE Exceptions: COleException and COleDispatchException

There are two OLE exception errors: COleException and COleDispatchException.

The COleException error normally is used for server-side or OLE-specific operations. You can trigger this exception by calling the AfxThrowOleException() function, passing an OLE scode value. This value then is stored in the m_sc member of the exception.

The COleDispatchException class is used in conjunction with OLE IDISPATCH interfaces and is thrown by the AfxThrowOleDispatchException() function. There are two forms to this function.

The first form has three parameters—the first two required, the last one optional. The first parameter is a wCodeWORD value that is an application-specific error code. The second parameter is an lpszDescription string pointer to represent the verbal string for describing the error. The last parameter is a help context ID.

The second form has three parameters as well. The first parameter is a wCodeWORD value that is an application-specific error code. The second parameter is nDescriptionID for a UINT resource code, which represents a verbal string describing the error. The last parameter is a help context ID.

Both forms are nearly identical—the difference is the second parameter.

Using NotSupportedException

The CNotSupportedException object is generated when a request is made for an unsupported feature. You can trigger this exception by calling the AfxThrowNotSupportedException() function.

Using CUserException

You use the CUserException class to generate exception objects for application-specific applications.

You can trigger this exception by calling the AfxThrowNotSupportedException() function.

Share ThisShare This

Informit Network