【请关注】VC++ MFC常见异常问题及处理方法

VC++ MFC常见异常问题及处理方法

一、内存管理异常

  1. 野指针访问(崩溃弹框)

// 错误示例:删除指针后未置空

CString* pStr = new CString(_T("Test"));

delete pStr; // 释放后未置空

// pStr = nullptr; // 正确操作应加这行

CString str = *pStr; // 访问野指针,触发异常

解决:释放内存后立即置空指针。

  1. 堆数组越界(堆破坏弹框)

// 错误示例:动态数组访问越界

int* pArr = new int[5];

pArr[5] = 10; // 越界访问,破坏堆结构

delete[] pArr;

解决:用 sizeof 或变量记录数组长度,遍历前校验索引。

  1. 内存泄漏(无弹框但性能下降)

// 错误示例:new后未delete

void OnButtonClick() {

CMyClass* pObj = new CMyClass(); // 未释放

}

解决:用智能指针 CComPtr 或RAII模式管理资源。

二、界面与控件异常

  1. 控件句柄无效(断言 ASSERT(::IsWindow(m_hWnd)) )

// 错误示例:销毁控件后访问句柄

void CMyDlg::OnDestroy() {

CDialogEx::OnDestroy();

m_EditCtrl.DestroyWindow(); // 销毁控件

// m_EditCtrl.SetWindowText(_T("Text")); // 后续调用会触发断言

}

解决:销毁控件后设置句柄为 NULL : m_EditCtrl.m_hWnd = NULL; 。

  1. 对话框数据交换(DDX/DDV)失败

// 错误示例:控件ID与变量绑定错误

DDX_Text(pDX, IDC_EDIT_WRONG, m_WrongVar); // IDC_EDIT_WRONG实际应为IDC_EDIT_CORRECT

DDV_MaxChars(pDX, m_WrongVar, 10); // 数据校验失败触发弹框

解决:检查 .rc 文件中控件ID与 DoDataExchange 函数是否一致。

  1. 模态对话框阻塞主线程(无弹框但界面假死)

// 错误示例:在耗时操作中创建模态对话框

void CMyDlg::OnLongTask() {

// 模拟耗时操作

for (int i = 0; i < 1000000; i++) {}

CMyChildDlg dlg;

dlg.DoModal(); // 阻塞主线程,界面无法响应

}

解决:将耗时操作放到工作线程( AfxBeginThread )中执行。

三、文件与IO异常

  1. 文件未找到( CFileException )

// 错误示例:硬编码路径导致文件不存在

try {

CFile file;

file.Open(_T("C:\\不存在的文件.txt"), CFile::modeRead); // 触发异常

} catch (CFileException* e) {

e->ReportError(); // 弹出文件未找到对话框

e->Delete();

}

解决:先检查文件是否存在: CFile::GetStatus(path, status) 。

  1. 磁盘满导致写入失败

// 错误示例:未处理写入异常

CFile file;

file.Open(_T("large_file.dat"), CFile::modeCreate | CFile::modeWrite);

// 模拟写入大文件(超过磁盘剩余空间)

char buffer[1024*1024];

file.Write(buffer, sizeof(buffer)); // 可能触发异常

解决:用 try-catch 捕获 CFileException ,检查错误码 e->m_cause == CFileException::diskFull 。

四、多线程与同步异常

  1. 线程间非法访问UI控件(崩溃)

// 错误示例:工作线程直接操作UI控件

UINT WorkThread(LPVOID pParam) {

CMyDlg* pDlg = (CMyDlg*)pParam;

pDlg->m_EditCtrl.SetWindowText(_T("Thread")); // 跨线程访问,未加同步

return 0;

}

解决:通过 PostMessage 或 SendMessage 向主线程发送消息更新界面。

  1. 临界资源竞争(断言 AfxLockTempMap() )

// 错误示例:多线程同时操作全局CMap对象

CMap<int, int, CString, CString&> g_Map;

UINT Thread1(LPVOID) { g_Map.SetAt(1, _T("A")); return 0; }

UINT Thread2(LPVOID) { g_Map.SetAt(2, _T("B")); return 0; }

// 同时调用导致临时映射表竞争

解决:用 CCriticalSection 保护共享资源:

CCriticalSection g_CS;

UINT Thread1(LPVOID) {

g_CS.Lock();

g_Map.SetAt(1, _T("A"));

g_CS.Unlock();

return 0;

}

五、数据库与网络异常

  1. ODBC连接失败( CDBException )

// 错误示例:错误的数据库连接字符串

CDatabase db;

try {

db.Open(_T("DSN=WrongDSN;UID=sa;PWD=123")); // DSN不存在

} catch (CDBException* e) {

e->ReportError(); // 弹出ODBC错误对话框

e->Delete();

}

解决:检查ODBC数据源名称(DSN)、用户名和密码是否正确。

  1. 网络连接超时( CInternetException )

// 错误示例:未设置超时时间

CInternetSession session;

CHttpFile* pFile = NULL;

try {

pFile = (CHttpFile*)session.OpenURL(_T("http://www.baidu.com"), 1,

INTERNET_FLAG_RELOAD, _T(""), _T(""), 0);

} catch (CInternetException* e) {

DWORD dwError = e->m_dwError;

if (dwError == INTERNET_ERROR_TIMEOUT) {

AfxMessageBox(_T("连接超时!"));

}

e->Delete();

}

解决:通过 SetOption 设置超时时间:

session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 5000); // 5秒超时

六、自定义异常与断言

  1. 自定义断言失败( ASSERT 弹框)

// 错误示例:参数校验失败

void Func(int nValue) {

ASSERT(nValue > 0); // nValue为0时触发断言弹框

// ...

}

解决:确保输入参数合法,或用 ATLASSERT 在Release版禁用断言。

  1. 未捕获的C++异常(程序终止弹框)

// 错误示例:抛出异常未捕获

void Func() { throw _T("Error"); }

void CMyDlg::OnButtonClick() {

Func(); // 未用try-catch捕获,导致程序终止

}

解决:在函数调用层添加异常处理:

try { Func(); } catch (CString str) { AfxMessageBox(str); }

七、其他典型问题

  1. 菜单ID重复(运行时崩溃)

// 错误示例:多个菜单项使用相同ID

// 在资源编辑器中,菜单项"文件-打开"和"编辑-打开"均设为ID_FILE_OPEN

解决:确保所有菜单项ID唯一。

  1. 资源句柄泄漏(如图标、画笔)

// 错误示例:创建GDI对象后未释放

void OnPaint() {

CPaintDC dc(this);

CBrush brush(RGB(255, 0, 0));

dc.FillRect(CRect(0,0,100,100), &brush);

// brush.DeleteObject(); // 未释放画笔,导致资源泄漏

}

解决:使用完GDI对象后立即调用 DeleteObject 。

快速调试技巧

  1. 开启断言:在 stdafx.h 中确保定义 _DEBUG 宏。

  2. 使用调试工具:

  • 断点调试:在 ASSERT 或崩溃位置设断点,查看调用堆栈。

  • 内存调试:添加 #define _CRTDBG_MAP_ALLOC 和 _CrtDumpMemoryLeaks() 检测泄漏。

  1. 错误码查询:通过 GetLastError() 获取系统错误码,用 FormatMessage 转换为可读信息。
相关推荐
岁忧20 分钟前
(nice!!!)(LeetCode 每日一题) 3372. 连接两棵树后最大目标节点数目 I (贪心+深度优先搜索dfs)
java·c++·算法·leetcode·go·深度优先
一点.点2 小时前
针对C++开发工具推荐及分析(涵盖IDE、编译器、调试工具和辅助工具)
开发语言·c++·ide·开发工具
煤灰2423 小时前
C++的多态与继承
开发语言·c++
Echo``4 小时前
7:OpenCV—图像形态学处理
c++·图像处理·人工智能·opencv·计算机视觉
Tony小周4 小时前
QML与C++交互2
javascript·c++·交互
oioihoii4 小时前
C++23 <spanstream>:基于 std::span 的高效字符串流处理
c++·算法·c++23
Owen_Q5 小时前
AtCoder Beginner Contest 407
开发语言·c++·算法
灵典3365 小时前
C++与Java类和对象的异同
java·开发语言·c++
末日汐5 小时前
C++ vector的使用及模拟实现
开发语言·c++