📄 apd.htm
字号:
<B>FREEING SYSTEM RESOURCES</B></P> <P>This exception-catching principle becomes very useful when you want to detect and handle errors arising from large portions of code. It can save coding lots of individual error-checking lines, but you must still free up any system resources that you've allocated in lines before the exception was raised.<HR></BLOCKQUOTE><H3><A NAME="Heading3"></A>Throwing Exceptions</H3><P>You can throw exceptions yourself from code embedded in any enclosing try blockwhen an error condition arises. The corresponding catch block will then handle theexception. Or you can throw the exception again from within a catch section to ahigher-level catch section, enclosing the first.</P><P>Several AfxThrow... functions will automatically generate and throw various typesof MFC exceptions up to the next catch level, such as AfxThrowFileException() orAfxThrowMemoryException(). These are covered in detail in the "MFC ExceptionTypes" section. However, these functions create a new instance of a specificCException-derived object for you--using the C++ new<I> </I>keyword and then thethrow keyword to raise an exception, as shown in the code fragment in Listing D.5.</P><P><H4>LISTING D.5. LST29_5.CPP--RAISING AN EXCEPTION WITH THE throw KEYWORD.</H4><PRE>1: try2: {3: DoSomeFileHandling();4: }5: catch(CFileException* e)6: {7: e->ReportError();8: e->Delete();9: }10: 11: return TRUE;12: }13: 14: BOOL bSomeThingWentWrong = TRUE;15: 16: void CExceptionalDlg::DoSomeFileHandling()17: {18: // ** ... File handling functions19: if (bSomeThingWentWrong == TRUE)20: {21: CFileException* pException = 22: new CFileException(CFileException::generic);23: throw(pException);24: }25: 26: // ** ... Yet More file handling27: } </PRE><P>In Listing D.5 the try block encloses a call to the DoSomeFileHandling() functionin line 16. This function may implement some file-handling procedures and raisesan exception when the error condition on line 19 is found to be TRUE. Line 22 createsa new CFileException object passing the CFileException::generic flag to its constructorand then throws the new object in line 23 to be caught by the catch section in line5.</P><P>This process of newing a CException-derived object and then using the throw keywordis the basis of the exception-raising mechanism. The specific details indicatingthe cause of the error can be attached to the CException object, or extra informationcan be added by deriving a class from the CException base class and adding extravariables to store more specific information.</P><P>Your catch block can then determine whether the error is too severe to be handledat that level. If so, you might want to throw the exception out to a higher-levelenclosing catch block. You can use the throw keyword (with no parameters) from withinthe catch block to rethrow the exception before you delete it. Instead of deletingthe exception, you could rethrow it to a higher level catch block by changing thecatch block shown in Listing D.5 to add the throw keyword like this:</P><P><PRE>e->ReportError();throw;</PRE><P>Then after reporting the error, the exception will be thrown again for an enclosingtry block to catch. If you haven't implemented this nesting, the overall MFC outsidethe catch block will catch it. You can use this nesting mechanism to determine theerror severity and implement appropriate recovery mechanisms at various hierarchicallevels in your program.</P><P><H3><A NAME="Heading4"></A>Deleting Exceptions</H3><P>As you've seen, you are fundamentally responsible for new-ing exceptions and mustalso delete these objects when you've handled them. If you delete one of the MFCexceptions, you shouldn't use the normal C++ delete keyword (as you've seen) becausethe exception might be a global object or a heap object. Instead, the CExceptionbase class has a Delete() function that first checks to see whether the exceptionshould be deleted. The creator of the exception can specify whether the exceptionshould be deleted or not by passing TRUE into the b_AutoDelete parameter of the CExceptionclass's constructor (which is the only parameter).</P><P><H2><A NAME="Heading5"></A>MFC Exception Types</H2><P>The Microsoft Foundation Classes have several predefined CException-derived classesthat are used during different types of MFC operations. You've already seen CFileExceptionand CResourceException in use. The following section covers each of these variousclasses and how it is raised in more detail. Each class is based on the CExceptionclass and extends the functionality of CException for different types of exceptionhandling. You can also derive your own exception classes from CException, and a genericCUserException is used for user-oriented application exceptions.</P><P><H3><A NAME="Heading6"></A>Using the CException Base Class</H3><P>CException itself has a constructor that takes an AutoDelete flag as discussedearlier, and is defined like this:</P><P><PRE>CException( BOOL b_AutoDelete );</PRE><P>If you new a CException or derived class, you should ensure that this is set toTRUE so that it will be deleted with the C++ delete keyword. Otherwise, a globalor stack-based exception should pass TRUE so that it is deleted only when it goesout of scope (at the end of a function or program that declares it).</P><P>The base class contains the Delete()function and two error-reporting functions.GetErrorMessage() can be used to store the error message into a predefined bufferand specify the ID of a help message to show the user context-specific help pertinentto the error. Its first parameter is the address of a destination buffer to holdthe associated error message. The second parameter specifies the maximum size ofthe buffer so that messages stored in the buffer don't over-spill outside the bufferarea. The third optional parameter can specify the context help ID as a UINT value.</P><P>You might use this function to help format an error message more relevant to yourapplication:</P><P><PRE>char msg[512];e->GetErrorMessage(msg,sizeof(msg));CString strMsg;strMsg.Format("The following error occurred in ÂMyApp: %s",msg);AfxMessageBox(strMsg);</PRE><P>The sizeof() C++ operator in the GetErrorMessage() function returns the size ofan array or variable, so if the msg array is changed, you don't have to change anyother code. The message is then formatted into the strMsg CString object and displayedin a message box.</P><P>The ReportError()function displays the message text directly in the familiar exceptionmessage box and would be used from the catch block:</P><P><PRE>e->ReportError();</PRE><H3><A NAME="Heading7"></A>Using the Memory Exception</H3><P>The CMemoryException is raised automatically when a C++ new<I> </I>keyword fails.You can also raise it yourself using the AfxThrowMemoryException(); function. Themeaning of this exception is exclusively that Windows can't allocate any more memoryvia its GlobalAlloc() or other memory allocation functions. This is a pretty diresituation for any program; you would usually handle this exception by writing codethat lets your program die gracefully, freeing up memory and system resources asit goes. There are rare cases in which you could take recovery action if you hada large block of memory allocated and could free it without too much detriment tothe users' activities.</P><P>Due to the exclusivity of this exception, no other cause attributes or specificfunctions extend the CException class's functionality.</P><P>You can watch new automatically raise a CMemoryException with these lines:</P><P><PRE>MEMORYSTATUS mem;GlobalMemoryStatus(&mem);BYTE* pBig = new BYTE[mem.dwAvailVirtual+1];</PRE><P>The mem.dwAvailVirtual structure member of MEMORYSTATUS will hold the total availablememory after the GlobalMemoryStatus() function retrieves the details. The new onthe next line requests one more byte than it could possibly have, thus throwing theexception.</P><P><H3><A NAME="Heading8"></A>Using the Resource Exceptions</H3><P>CResourceException is thrown in many places where system resources are compromised,as you saw in the mutex and semaphore example in Listing D.3. If you want to throwthese exceptions yourself, use the corresponding AfxThrowResourceException() function.</P><P>Windows can't find or allocate the requested resource and doesn't give any morespecific guidance; hence it has no other functions or attributes.</P><P><H3><A NAME="Heading9"></A>Using the File and Archive Exceptions</H3><P>You already looked at CFileException in Listing D.5. This is probably one of themore sophisticated MFC exceptions because of the number of things that can go wrongwith file access. You can throw these yourself using the AfxThrowFileException()function, which takes three parameters, one mandatory and the other two optional.The first mandatory parameter, cause, is a cause code for the exception. This willbe placed in the file exception's m_cause member variable for interrogation in acatch block.</P><P>Table D.1 shows a list of the various cause codes. The second parameter, lOsError,can be used to specify an operating system error code to be placed in the file exception'sm_lOsError member variable. This long value can help clarify an error in more detailby drawing on the operating system's own list of file access errors. The third parameter,strFileName, is placed into the file exception's m_strFileName member string variableto indicate the filename of the file that was being accessed when the error occurred.</P><P><H4>TABLE D.1. THE CFileException m_cause CODES.</H4><P><TABLE BORDER="1"> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">Cause Code </TD> <TD ALIGN="LEFT">Meaning </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::none </TD> <TD ALIGN="LEFT">There was no error. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::generic </TD> <TD ALIGN="LEFT">No error code specified. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::tooManyOpenFiles </TD> <TD ALIGN="LEFT">Too many concurrently open files. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::fileNotFound </TD> <TD ALIGN="LEFT">Can't find the specified file. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::badPath </TD> <TD ALIGN="LEFT">The path name specified is invalid. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::invalidFile </TD> <TD ALIGN="LEFT">An attempt was made to use an invalid file handle. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::badSeek </TD> <TD ALIGN="LEFT">The seek operation failed. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::endOfFile </TD> <TD ALIGN="LEFT">The end of the file was reached. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::diskFull </TD> <TD ALIGN="LEFT">There is no spare disk space. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::hardIO </TD> <TD ALIGN="LEFT">A hardware error occurred. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::accessDenied </TD> <TD ALIGN="LEFT">Permissions deny access to the file. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::directoryFull </TD> <TD ALIGN="LEFT">The directory has too many files and can't add another. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::removeCurrentDir </TD> <TD ALIGN="LEFT">Can't remove the current working directory. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::lockViolation </TD> <TD ALIGN="LEFT">Can't lock an already locked region of the file. </TD> </TR> <TR ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT">CFileException::sharingViolation </TD> <TD ALIGN="LEFT">A shared region is locked or can't be shared. </TD> </TR></TABLE></P><P>There is also a ThrowOsError()static member function that throws and configuresa file exception based on an operating system error code. You must pass ThrowOsError()the operating system error code as its first parameter and an optional filename asits second parameter. Another member function, ThrowErrno(), does the same thingbut uses the UNIX-style errno error codes as its only parameter (from the Errno.hheader file). Because these are static functions, you would use them with staticscope to raise exceptions with lines like this:</P><P><PRE>CFileException::ThrowOsError(ERROR_BAD_PATHNAME); Â// Invalid PathCFileException::ThrowErrno (ENOSPC); // Disk Full</PRE><P>Another static member function, OsErrorToException(), automatically converts betweenoperating system error codes and CFileException cause codes. By passing an OS errorcode, it will return the corresponding cause code. A corresponding function ErrnoToException()does the same when passed an errno error code.</P><P>When using archives with the CArchive class, you normally handle both CFileExceptionsand CArchiveException cases in conjunction: Many of the CArchive operations are tied
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -