【请关注】 VC++的各类异常捕获处理分析

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 vec;

    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  (需提前引用 COM 组件),捕获  _com_error  解析 HRESULT。

 

四、数据库异常(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* ),但需注意顺序(先捕具体类)。

 

 

你可能感兴趣的:(MFC,VC++,VC,c++,mfc)