VC++ 的各类异常捕获处理
一、基础 C++ 异常
1. 空指针解引用异常
void TestNullPointerException() {
CString* str = nullptr;
try {
str->MakeUpper(); // 空指针调用成员函数
} catch (const std::exception& e) {
AfxMessageBox(_T("空指针异常:") + CString(e.what()));
}
}
注解:MFC 对象(如 CString )通常内部做了空指针防护,但原生指针需手动校验。
2. 数组越界异常( std::vector )
void TestVectorOutofBounds() {
std::vector
try {
int val = vec.at(0); // at() 会抛异常,operator[] 直接崩溃
} catch (const std::out_of_range& e) {
AfxMessageBox(_T("数组越界:") + CString(e.what()));
}
}
注解:MFC 集合类(如 CArray )的 GetAt() 类似 at() ,越界时抛 CMemoryException 。
二、MFC 框架异常
3. 内存分配失败异常( CMemoryException )
void TestMemoryAllocation() {
try {
LPVOID ptr = operator new(0x7FFFFFFF); // 申请超大内存(假设系统不支持)
} catch (CMemoryException* e) {
e->ReportError(); // MFC 内置错误提示(如“内存不足”对话框)
e->Delete(); // 释放异常对象(MFC 异常类需手动删除)
}
}
注解:MFC 中 new 失败默认抛 CMemoryException ,非 MFC 代码抛 std::bad_alloc 。
4. 文件操作异常( CFileException )
void TestFileException() {
CFile file;
CFileException ex;
if (!file.Open(_T("nonexistent.txt"), CFile::modeRead, &ex)) {
// 非异常捕获方式(MFC 推荐的错误处理)
CString msg;
msg.Format(_T("文件打开失败:%s"), ex.GetErrorMessage());
AfxMessageBox(msg);
}
// 或用异常捕获方式:
try {
file.Open(_T("readonly.txt"), CFile::modeWrite); // 只读文件写入
} catch (CFileException* e) {
if (e->m_cause == CFileException::fileNotFound) {
AfxMessageBox(_T("文件未找到"));
} else if (e->m_cause == CFileException::accessDenied) {
AfxMessageBox(_T("权限拒绝"));
}
e->Delete();
}
}
注解:MFC 文件操作推荐用 Open 返回值 + CFileException 参数处理,也可抛异常。
5. 资源加载失败异常( CResourceException )
void TestResourceLoad() {
try {
HICON hIcon = AfxGetApp()->LoadIcon(IDI_NONEXISTENT); // 不存在的图标资源
} catch (CResourceException* e) {
AfxMessageBox(_T("资源加载失败:") + CString(e->m_lpszResourceName));
e->Delete();
}
}
注解:加载对话框、字符串表等资源时可能触发, m_lpszResourceName 含资源名。
三、COM 组件异常
6. COM 接口调用异常( _com_error )
void TestCOMException() {
try {
// 假设调用 Excel COM 对象时发生错误
Excel::_Application app;
app.CreateDispatch(_T("Excel.Application"));
app.Get_Workbooks(); // 可能抛异常
} catch (_com_error& e) {
CString errMsg = CString(e.ErrorMessage());
AfxMessageBox(_T("COM 错误:") + errMsg);
}
}
注解:需包含 #import
四、数据库异常(ODBC/DAO)
7. ODBC 连接失败( CDatabaseException )
void TestODBCConnection() {
CDatabase db;
try {
db.Open(_T("InvalidDSN")); // 不存在的数据源名称
} catch (CDatabaseException* e) {
AfxMessageBox(_T("数据库连接失败:") + e->m_strError);
db.Close();
e->Delete();
}
}
注解:MFC ODBC 类 CDatabase 、 CRecordset 抛 CDatabaseException ,含 SQL 错误信息。
五、自定义异常
8. 业务逻辑异常(继承 CException )
// 自定义异常类
class CMyBusinessException : public CException {
public:
CString m_strReason;
CMyBusinessException(LPCTSTR lpszReason) : m_strReason(lpszReason) {}
virtual void ReportError() const override {
AfxMessageBox(_T("业务异常:") + m_strReason);
}
};
// 使用示例
void CheckAge(int nAge) {
if (nAge < 0) {
throw new CMyBusinessException(_T("年龄不能为负数")); // MFC 异常需用 new 创建
}
}
void TestBusinessException() {
try {
CheckAge(-5);
} catch (CMyBusinessException* e) {
e->ReportError(); // 调用自定义错误提示
e->Delete(); // 手动释放异常对象
}
}
注解:MFC 异常类需继承 CException ,通过 new 创建并手动 Delete() 。
六、系统与运行时异常
9. 除以零异常(结构化异常处理,SEH)
void TestDivideByZero() {
int a = 10, b = 0;
__try {
int result = a / b; // 触发硬件异常
} __except (EXCEPTION_EXECUTE_HANDLER) { // SEH 捕获所有硬件异常
AfxMessageBox(_T("除以零错误"));
}
}
注解:C++ 的 try/catch 无法捕获硬件异常(如除零),需用 SEH( __try/__except ),需开启编译器选项 /EHa 。
10. 无效句柄异常( HANDLE 校验)
void TestInvalidHandle() {
HANDLE hFile = CreateFile(_T("NUL"), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) { // 非异常方式,检查返回值
DWORD dwErr = GetLastError();
AfxMessageBox(_T("句柄无效:") + CString(dwErr));
}
// 若强制用异常:
try {
if (hFile == INVALID_HANDLE_VALUE) {
throw CException(); // 抛通用异常
}
} catch (CException* e) {
e->Delete();
}
}
注解:Windows API 通常用返回值 + GetLastError() 处理错误,极少抛异常。
七、MFC 控件与消息异常
11. 无效窗口句柄异常( CWnd 操作)
void TestInvalidHWND() {
CWnd* pWnd = CWnd::FromHandle((HWND)NULL); // 空句柄对象
try {
pWnd->GetWindowText(m_strText); // 调用空窗口方法
} catch (const CException& e) {
AfxMessageBox(_T("无效窗口句柄"));
}
}
注解:MFC CWnd 对象需关联有效 HWND ,空句柄调用成员函数会触发断言(DEBUG 模式)或异常。
12. 消息传递异常( SendMessage 失败)
void TestMessageException() {
HWND hInvalidWnd = (HWND)0xDEADBEEF; // 无效窗口句柄
LRESULT lRes = SendMessage(hInvalidWnd, WM_NULL, 0, 0);
if (lRes == 0 && !IsWindow(hInvalidWnd)) { // 检查窗口有效性
AfxMessageBox(_T("消息发送失败:窗口句柄无效"));
}
}
注解:Windows 消息机制不抛异常,需手动校验目标窗口是否存在( IsWindow() )。
八、多线程异常
13. 线程同步对象异常( CSyncObject )
void TestThreadSync() {
CCriticalSection cs;
try {
cs.Lock(); // 正常锁定
cs.Lock(); // 递归锁定,DEBUG 模式触发断言
} catch (const CException& e) {
// CCriticalSection 通常不抛异常,但其他同步对象(如 CMutex)可能因系统限制失败
AfxMessageBox(_T("同步对象异常"));
}
}
注解:MFC 同步类(如 CCriticalSection )在 DEBUG 模式通过断言提示错误,RELEASE 模式可能无提示。
九、其他常见场景
14. 字符串转换异常( WideCharToMultiByte )
void TestStringConversion() {
CStringW strWide = L"测试";
char szMulti[10];
try {
int nLen = WideCharToMultiByte(CP_UTF8, 0, strWide, -1, szMulti, sizeof(szMulti), NULL, NULL);
if (nLen == 0) {
throw CException(); // 转换失败时抛异常
}
} catch (CException* e) {
AfxMessageBox(_T("字符转换失败"));
e->Delete();
}
}
注解:Windows API 字符转换失败时返回 0,需结合 GetLastError() 判断原因。
15. 资源泄漏检测(DEBUG 断言)
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
void TestMemoryLeak() {
CString* pStr = new CString(_T("Leak"));
// 故意不 delete,DEBUG 模式下 MFC 会在程序退出时输出泄漏信息
// 非异常处理,属于调试机制
}
注解:MFC DEBUG 模式通过 DEBUG_NEW 宏检测内存泄漏,不抛异常但输出日志。
MFC 异常处理关键原则
1. 优先错误码而非异常:
MFC 框架函数(如 CFile::Open 、 CWnd::Create )通常返回 BOOL 值,通过输出参数(如 CFileException* )传递错误信息,非必要不抛异常。
2. 异常对象管理:
MFC 异常类(如 CFileException )通过 new 创建,捕获后必须调用 Delete() 释放(类似 CObject 的 Delete() )。
3. SEH 与 C++ 异常混合:
处理硬件异常(如访问违规)需用 __try/__except ,普通 C++ 异常用 try/catch ,需注意编译器选项( /EHsc 或 /EHa )。
4. 调试与发布模式差异:
DEBUG 模式下 MFC 会触发更多断言(如 ASSERT_VALID(pWnd) ),RELEASE 模式需手动处理错误。
扩展说明
- MFC 异常类继承体系:
CException (基类)→ CMemoryException 、 CFileException 、 CResourceException 、 CDatabaseException 等。
- 非 MFC 异常兼容:
MFC 代码可同时捕获 C++ 标准异常( std::exception )和 MFC 异常( CException* ),但需注意顺序(先捕具体类)。