编写程序时出一些错误是难免的,在C++中称在软件或硬件中发生的不期望或不需要的事件为异常(Exception)。MFC提供了两种异常处理机制:
l C++异常,在MFC 3.0和更高版本中可以使用
l MFC异常,在MFC 1.0和更高版本中可以使用
在程序出错误后需要调试程序,MFC提供了许多诊断服务,可以让用户轻松的调试程序,这些诊断服务大多以特定宏和全局函数形式出现。
本章将向读者介绍以下内容:
l 处理C++异常
l MFC异常
l 诊断服务
第一节 处理C++异常
C++使用try、catch、throw三个关键字来实现异常处理。使用C++的异常处理能够使你的程序从异常状态中恢复。这些异常由处于正常控制流之外的代码来处理。
l 注意:
l 新的32位的异常处理机制支持C和C++。但是,它并非为C++专门设计的。你应当保证你的代码非常适合于C++的异常处理,并且C++的异常处理是相当灵活的,它可以处理任何类型的异常。
异常处理机制允许程序对严重的和没有预料到的问题做出响应。一个异常块由下列三个部分组成:
l try块
标志你认为可能会出现异常的代码。
l catch块
紧跟着try块,里面包含了处理异常的代码。
l throw块
抛出一个异常,激活catch块中的相应的异常处理代码。
异常处理的机制比较简单,首先你将有可能出现问题的代码放在try块中,然后在catch块中放进用来处理异常的代码。如果在try块中的代码抛出一个异常,try块迅速退出执行,程序将转入catch块中执行相应的异常处理代码。
第二节 MFC异常
在MFC中CException类是所有异常类的基类,它是一个抽象类,你不能使用它的对象,只能创建它的派生类的对象。它有两个公用方法:GetErrorMessage()和ReportError(),分别用于查找描述异常的信息和为用户显示一个错误信息的信息对话框。
CException类包含以下基类(如图11.1所示),并提供了THROW、THROW_LAST、TRY、CATC、 AND_CATCH、END_CATCH这些宏用来处理异常。
图11. 1 CException的基类
(1)CArchiveException
一个CArchiveException用来描述序列化异常状态。它包含了一个数据成员m_cause用来表现异常的原因。它可能为以下值:
none : 没有错误发生
generic:未指定的错误
readOnly:试图写入一个为载入而打开的文档
endOfFile:当读取一个文档时到达文档尾部
writeOnly:试图读取一个为存储而打开的文档
badIndex:非法文件格式
badClass:试图读一个对象到一个错误类型对象中
badSchema:试图读一个对象,它带有不同的类的版本
另外MFC还提供AfxThrowArchiveException()函数来抛出一个存档文件异常,形式如下:
void AfxThrowArchiveException(int cause, LPCTSTR lpszArchiveName );
(2)CDaoException
一个CDaoException类对象用来表示基于数据访问对象(DAO,data access objects)的MFC数据库类的异常。这个异常类包含三个决定异常原因的成员变量,这三个成员变量是:
m_scode:描述与异常相关的SCODE代码
m_pErrorInfo:包含用于所有MFC的DAO类的所有异常的扩展错误代码
m_nAfxDaoError:包含DAO错误对象的指向CDaoErrorInfo结构的指针
这个类有下列两个操作:
GetErrorCount() 获取在数据库引擎异常集合中的异常数目
GetErrorInfo() 获取在异常集合中相似对象的错误信息
MFC中的AfxThrowDaoException()函数抛出一个CDaoException类型的异常,使用如下形式:
void AFXAPI AfxThrowDaoException(int nAfxDaoError = NO_AFX_DAO_ERROR, SCODE scode = S_OK );
(3)CDBException
一个CDBExceptio类的对象表示MFC数据库中的一种异常情况。这个类包含两个用来决定异常原因的成员变量:
m_nRetCode:一个ODBC(OpenDatabase Connectivity)返回的RETCODE类型的代码
m_strError:包含一个描述错误信息的字符串
m_strStateNativeOrigin:包含描述带有ODBC错误代码的错误的字符串
MFC中的AfxThrowDBException()函数抛出一个CDBException类型的异常,使用如下形式:
void AfxThrowDBException( RETCODEnRetCode, CDatabase* pdb, HSTMT hstmt );
(3)CFileException
一个CFileException对象描述一个与文件相关的异常状态。这个类包含三个描述异常原因的成员变量:
m_cause:包含与错误原因对应的代码。
m_lOsError:包含相关的操作系统错误数
m_strFileName:包含出现例外的文件名
成员变量m_cause可能为以下值:
none:没有错误发生
generic:未指定的错误
fileNotFound :文件不能定位错误
badPath :全部或部分路径无效
tooManyOpenFiles:达到允许打开的文件数目
accessDenied :不能访问文件
invalidFile :试图访问一个无效的文件
removeCurrentDir:删除正在操作中的目录
directoryFull:目录个数已满
badSeek :试图设置文件指针出错
hardIO :硬件出错
sharingViolation :共享出错
lockViolation :试图锁定已锁定的区域
diskFull :磁盘空间已满
endOfFile :到达文件结尾
MFC中的AfxThrowFileException()函数抛出一个CFileException类型的异常,使用如下形式:
void AfxThrowFileException( intcause, LONG lOsError = –1, LPCTSTR lpszFileName= NULL );
(4)CInternetException
一个CInternetException对象代表一个和Internet操作有关的异常状态。它包含两个成员变量:
m_dwError:表示导致异常的错误
m_dwContext:和引起错误的操作有关的上下文变量
(5)CMemoryException
一个CMemoryException对象描述一个内存溢出异常。内存异常自动的被new操作符抛出。
MFC中的AfxThrowMemoryException()函数抛出一个
CMemoryException类型的异常,使用如下形式:
void AfxThrowMemoryException( );
(6)CNotSupportedException
一个CNotSupportedException对象表示当不支持的特性被请求时发生的异常,没有其它必要或可能的限制。
MFC中的AfxThrowNotSupportedException()函数抛出一个CNotSupportedException类型的异常,使用如下形式:
voidAfxThrowNotSupportedException( );
(7)COleException
一个COleException对象表示和OLE操作有关的异常。它包含一个成员变量m_sc来容纳异常原因的状态码。
MFC中的AfxThrowOleException()函数抛出一个COleException类型的异常,使用如下形式:
void AFXAPI AfxThrowOleException(SCODE sc );
void AFXAPI AfxThrowOleException(HRESULT hr );
(8)COleDispatchException
一个COleDispatchException对象表现为OLE自动化的关键部分所特有的异常。它包含5个成员变量:
m_wCode: IDispatch特有的错误代码
m_strDescription:一个描述性错误
m_dwHelpContext:用于错误的Help上下文ID
m_strHelpFile:使用m_dwHelpContext的Help文件
m_strSource:产生异常的应用程序
MFC中的AfxThrowOleDispatchException()函数抛出一个COleDispatchException类型的异常,使用如下形式:
void AFXAPIAfxThrowOleDispatchException( WORD wCode, LPCSTR lpszDescription,UINT nHelpID =0 );
void AFXAPIAfxThrowOleDispatchException( WORD wCode, UINT nDescriptionID,UINT nHelpID = –1 );
(9)CResourceException
一个CResourceException对象表示当Windows不能定位或分配需要的资源时抛出的异常。
MFC中的AfxThrowOleDispatchException()函数抛出一个CResourceException类型的异常,使用如下形式:
void AfxThrowResourceException();
(10)CUserException
一个CUserException对象表示停止终端用户操作时抛出的异常。
MFC中的AfxThrowUserException()函数抛出一个CUserException类型的异常,使用如下形式:
void AfxThrowUserException( );
第三节 诊断服务
上节讲述了使用异常处理来捕获程序中的错误,然而并非程序中所有的错误都是可以捕获的,还会出现很多无法预知的错误,这些错误需要在调试程序中发现并更正。MFC提供了许多诊断服务,供程序员调试程序使用。
MFC提供的用于诊断程序的宏有:ASSERT、ASSERT_KINDOF、ASSERT_VALID、DEBUG_NEW、TRACE、TRACE0、TRACE1、TRACE2、TRACE3、VARIFY。本节将详细介绍ASSERT、VERIFY、TRACE。
(1)ASSERT
这个宏的用法如下:
ASSERT( booleanExpression )
其中的参数booleanExpression是一个表达式或指针。
这个宏用来测试它的参数是否为真。如果参数不为真,这个宏就显示一个诊断信息对话框,并终止程序的运行。如果参数为真,它不做任何事情。诊断信息按照下面的形式显示:
Debug Assertion Failed!
Program:<Program Name>
File:<File Name>
Line:<num>
其中Program Name是程序的的名称,File Names是出错的文件名,num是出问题的诊断语句所在的行数。诊断信息对话框如图11.2所示。
图11. 2 诊断输出信息
值得注意的是,ASSERT仅在MFC的调试(Debug)版本中有效,在MFC的发布(Release)版本中,ASSERT语句不再有效,它不对参数进行真假检测。
下面的代码中ASSERT作用是检查一个指向用户自定义的类CMyClass的指针是否为空,代码如下:
CMyClass* m_pMyClass=newCMyClass;
ASSERT(m_pMyClass);
// ......
(2)VERIFY
这个宏和ASSERT差不多,它的用法如下:
VERIFY( booleanExpression )
其中的参数booleanExpression是一个表达式或指针。
在MFC的调试版本中,VERIFY宏检测它的参数,如果参数不为真,弹出如图1.2所示的诊断信息对话框。如果参数为真,它不做任何事情。
在MFC的发布版本中,它仍对参数进行测试,但是当参数为假时,不弹出诊断信息对话框。
下面这段代码可以让用户对ASSERT和VERIFFY两个宏之间的差别有更深入的了解,这段代码是在MFC的发布版本中编译的,之所以选择发布版本是因为在这段代码中两个宏后面的参数都为假,使用发布版本编译可以忽略这些错误。但是ASSERT不检测参数的真假,而VERIFY检测参数的真假,所以只能弹出一个对话框。
在一个MFC的多文档应用程序Test中的OnDraw()函数中ToDo语句后面添加下面的代码:
bool m_bValue=false;
ASSERT(m_bValue &ASSERTMessage());
VERIFY(m_bValue &VERIFYMessage());
为CTestView类添加两个成员函数ASSERTMessage()和VERIFYMessage():
bool CTestView::ASSERTMessage()
{
MessageBox("经过ASSERT检验");
return true;
}
bool CTestView::VERIFYMessage()
{
MessageBox("经过VERIFY检验");
return true;
}
在MFC的发布版本中编译并运行该程序,弹出如图11.3所示的消息框,显示“经过VERIFY检验”,说明VERIFY检测了它的参数,而ASSERT没有检测其参数。
图11. 3 VERIFY示例
(3)TRACE
TRACE宏的用法如下:
TRACE( exp )
其中的参数exp定义了一组数量可变的参数。
TRACE是一个在程序运行时跟踪变量数值的便捷的方法,它的用法和Printf完全相同。
l 注意:
l 使用TRACE一次最多可以显示512个字符,而且这个宏也只在MFC的调试版本中有效。
下面的例子使用TRACE宏在程序运行时跟踪变量m_value的值。
代码如下:
int m_value=100;
for(int i=0;i<5;i++)
{
m_value++;
TRACE("m_value =%d",m_value);
}
图11. 4 查看TRACE信息
以调试方式运行该应用程序的调试版本,即可从Developer Studio的Output窗口在检查到TRACE宏输出的诊断信息。