【请关注】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 转换为可读信息。
相关推荐
岁忧14 分钟前
(LeetCode 面试经典 150 题) 169. 多数元素(哈希表 || 二分查找)
java·c++·算法·leetcode·go·散列表
fpcc26 分钟前
c++26新功能—hive容器
c++·hive
让我们一起加油好吗1 小时前
【基础算法】二分(二分查找 + 二分答案)
c++·算法·leetcode·二分·洛谷
范纹杉想快点毕业2 小时前
Qt、C++自定义按钮、组件、事件编程开发练习,万字实战解析!!
java·c语言·开发语言·c++·git·qt·github
让我们一起加油好吗3 小时前
【基础算法】贪心 (一) :简单贪心
c++·算法·贪心算法·stl·洛谷·牛客
真的想上岸啊3 小时前
学习C++、QT---03(C++的输入输出、C++的基本数据类型介绍)
开发语言·c++·学习
mxpan4 小时前
VC++ 与 Golang 的协作:实现 HTTP 文件传输服务
c++·go·mfc
听风lighting4 小时前
WevServer实现:异步日志写与HTTP连接
linux·网络·c++·网络协议·http·秋招·嵌入式面试
浮灯Foden5 小时前
算法-每日一题(DAY11)每日温度
开发语言·数据结构·c++·算法·leetcode·面试
闻缺陷则喜何志丹5 小时前
【并集查找】P10729 [NOISG 2023 Qualification] Dolls|普及+
c++·算法·图论·洛谷·并集查找