MFC回调/事件处理函数异常捕获问题:TVN_SELCHANGED异常未被处理
MFC模态对话框中TVN_SELCHANGED事件抛出的异常无法被外层try/catch捕获
问题现象
基于Visual Studio 2022向导生成的MFC模态对话框项目,对话框包含按钮和CTreeControl控件。在程序入口处设置了try/catch语句,OnBnClickedButton1、OnTvnGetdispinfoTree中抛出的异常能被正常捕获处理,但OnTvnSelchangedTree中抛出的异常却触发未处理错误,无法进入外层catch。
主代码(含外层try/catch)
try { CMFCExceptDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { TRACE(traceAppMsg, 0, "OK...\n"); } else if (nResponse == IDCANCEL) { TRACE(traceAppMsg, 0, "Cancel...\n"); } else if (nResponse == -1) { TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n"); TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n"); } } catch(std::exception &exc) { const char *x = exc.what(); TRACE(traceAppMsg, 0, exc.what()); }
对话框头文件代码
class CMFCExceptDlg : public CDialogEx { public: CMFCExceptDlg(CWnd* pParent = nullptr); // standard constructor #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MFC_EXCEPT_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support protected: HICON m_hIcon; CTreeCtrl m_cTree; virtual BOOL OnInitDialog(); DECLARE_MESSAGE_MAP() public: afx_msg void OnTvnGetdispinfoTree(NMHDR *pNMHDR, LRESULT *pResult); afx_msg void OnBnClickedButton1(); afx_msg void OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult); };
对话框实现文件代码
CMFCExceptDlg::CMFCExceptDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_MFC_EXCEPT_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CMFCExceptDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_TREE, m_cTree); } BEGIN_MESSAGE_MAP(CMFCExceptDlg, CDialogEx) ON_NOTIFY(TVN_GETDISPINFO, IDC_TREE, &CMFCExceptDlg::OnTvnGetdispinfoTree) ON_BN_CLICKED(IDC_BUTTON1, &CMFCExceptDlg::OnBnClickedButton1) ON_NOTIFY(TVN_SELCHANGED, IDC_TREE, &CMFCExceptDlg::OnTvnSelchangedTree) END_MESSAGE_MAP() // CMFCExceptDlg message handlers BOOL CMFCExceptDlg::OnInitDialog() { CDialogEx::OnInitDialog(); m_cTree.InsertItem(LPSTR_TEXTCALLBACK, TVI_ROOT); m_cTree.InsertItem(LPSTR_TEXTCALLBACK, TVI_ROOT); m_cTree.InsertItem(LPSTR_TEXTCALLBACK, TVI_ROOT); m_cTree.InsertItem(LPSTR_TEXTCALLBACK, TVI_ROOT); return TRUE; } void CMFCExceptDlg::OnTvnGetdispinfoTree(NMHDR *pNMHDR, LRESULT *pResult) { LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR); *pResult = 0; throw std::exception("GetDispInfo"); } void CMFCExceptDlg::OnBnClickedButton1() { throw std::exception("In Click Button Error"); } void CMFCExceptDlg::OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); // TODO: Add your control notification handler code here *pResult = 0; throw std::exception("OnTvnSelchangedTree"); }
异常表现
触发OnTvnSelchangedTree时抛出的异常未进入外层catch,直接触发未处理异常错误:
原因及解决方案
原因
MFC消息分发机制中,不同控件通知的调用栈存在差异:
TVN_GETDISPINFO和按钮点击消息由对话框消息循环直接分发,异常可向上传播到外层try/catch;TVN_SELCHANGED在TreeView控件内部的消息处理流程中被调用,调用栈包含MFC未处理异常的框架代码(如控件窗口过程),导致异常无法穿透到外层try/catch,直接触发未处理异常。
解决方案
事件处理函数内部捕获异常
直接在OnTvnSelchangedTree中添加try/catch,处理异常后避免向上抛出:void CMFCExceptDlg::OnTvnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); *pResult = 0; try { throw std::exception("OnTvnSelchangedTree"); // 业务代码 } catch (const std::exception& exc) { TRACE(traceAppMsg, 0, exc.what()); // 异常处理逻辑 } }全局异常捕获
若需统一处理所有未捕获异常,可注册全局异常处理函数:- 针对C++异常,使用
std::set_terminate; - 针对Windows结构化异常,使用
SetUnhandledExceptionFilter。
- 针对C++异常,使用
修改MFC异常处理策略
在项目中定义_AFX_ENABLE_EXCEPTION_CATCHING宏(需确保MFC版本支持),或在对话框PreTranslateMessage中拦截消息并手动包裹try/catch分发,但该方式复杂度较高,不推荐作为首选。
内容的提问来源于stack exchange,提问作者Michal Hadraba
相关产品推荐
相关产品推荐

