qq_41945416 2025-11-14 21:14 采纳率: 90.6%
浏览 3
已结题

关于mfc错误捕获的问题

请看以下的mfc代码 片段

BOOL CADO::RstAddNew(_RecordsetPtr &mf_precordset)
{
    try
    {
        mf_precordset->AddNew();
        return TRUE;
    }
    catch(_com_error &tmp_error)
    {
        m_error = tmp_error;
        m_errorvalue1 = (CONST TCHAR * )tmp_error.Source();
        m_errorvalue2 = (CONST TCHAR * )tmp_error.Description();
        m_errorvalue3 = tmp_error.ErrorMessage();
        m_errorvalue4.Format(_T("0x%08lp"),tmp_error.Error());
        m_errormessage.Format(_T("[CADOERROR]%s\r\n[错误信息] %s\r\n") \
                            _T("[错误代码] %s\r\n") \
                            _T("[HRESULT] %s\r\n") \
                            _T("[错误提示]数据库记录集发生错误!"),
                            m_errorvalue1,
                            m_errorvalue2,
                            m_errorvalue3,
                            m_errorvalue4);
        ::MessageBox(NULL,m_errormessage,_T("CADO"),MB_ICONWARNING|MB_OK);
        // 我能在这个位置抛出 throw m_error 吗?
        return FALSE;
    }
}

如上述代码 CADO类是我封装ado的某些方法,以便更快捷的连接和使用数据库的,m_error是CADO的成员变量 其原型为:_com_error CADO::m_error;我的问题如下:
1.1 _com_error 能够使用点操作符. 或指针的指向操作符->请问 _com_error 是类 还是类型 还是结构体?
继续请看以下代码

void CDemoDlg::OnBnClickedButton1()
{
  try
{
      g_app_ado.RstAddNew(); //CADO g_app_ado 是全局的,已事先定义清楚。
}
catch(_com_error * e)
{
 // 对封装后的成员函数仍然捕获错误 我想二次捕获的是CADO类 try catch 抛出的 m_error 错误,结合第一段代码 可能实现吗?
}
}

1.2 对封装后的成员函数仍然捕获错误 我想捕获的是CADO类 try catch 抛出的 throw m_error 错误,结合第一段代码 可能实现吗?可以将就使用_com_error吗?需不需要单独创建属于我程序的异常类呢?
1.3 假设用户现已经核实清楚原因 发现为网络断线,断线重连后,现在还在catch 块里边 因为当初执行g_app_ado.RstAddNew();已经失败了,现在更新了连接 应当如何跳出catch块 重新再次执行g_app_ado.RstAddNew();?
请继续看以下代码:

void CDemoDlg::OnBnClickedButton1()
{
  try
{
      //使用ado可能发生错误
}
catch(_com_error e //_com_error & e _com_error * e)
{
    //在此处理ado所发生的错误
}
}

1.4 捕获错误的时候方法五花八门的 有人用对象变量,有人用引用,有人用指针,这里应该用哪种形式才是正确的呢?

  • 写回答

2条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-11-14 23:12
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案能帮助您,请采纳,非常感谢!
    .
    每日一句正能量,让它带给您好心情:把日子过得有滋有味,就是对生活最好的回馈。
    .

    1.1 _com_error 类型解析

    _com_error 是 MFC/ATL 中的一个,定义在 <comdef.h> 头文件中。作为类,它支持点操作符(.)访问成员方法和属性,也支持指针的指向操作符(->)。

    主要成员方法包括:

    • Description() - 获取错误描述
    • ErrorMessage() - 获取错误消息
    • Error() - 获取 HRESULT 错误代码
    • Source() - 获取错误源

    1.2 二次捕获异常的实现

    可以实现二次捕获,但需要修改你的代码:

    BOOL CADO::RstAddNew(_RecordsetPtr &mf_precordset)
    {
        try
        {
            mf_precordset->AddNew();
            return TRUE;
        }
        catch(_com_error &tmp_error)
        {
            m_error = tmp_error;
            m_errorvalue1 = (CONST TCHAR * )tmp_error.Source();
            m_errorvalue2 = (CONST TCHAR * )tmp_error.Description();
            m_errorvalue3 = tmp_error.ErrorMessage();
            m_errorvalue4.Format(_T("0x%08lx"),tmp_error.Error()); // 修正了格式符
            m_errormessage.Format(_T("[CADOERROR]%s\r\n[错误信息] %s\r\n") \
                                _T("[错误代码] %s\r\n") \
                                _T("[HRESULT] %s\r\n") \
                                _T("[错误提示]数据库记录集发生错误!"),
                                m_errorvalue1,
                                m_errorvalue2,
                                m_errorvalue3,
                                m_errorvalue4);
            ::MessageBox(NULL,m_errormessage,_T("CADO"),MB_ICONWARNING|MB_OK);
            
            // 抛出异常供外层捕获
            throw m_error;  // 这里抛出的是拷贝
            // 或者使用 throw tmp_error; 抛出原始引用
            return FALSE;
        }
    }
    

    调用方代码:

    void CDemoDlg::OnBnClickedButton1()
    {
        try
        {
            g_app_ado.RstAddNew(mf_precordset); // 需要传入参数
        }
        catch(_com_error &e)  // 使用引用捕获
        {
            // 这里可以捕获到从CADO类抛出的_com_error异常
            CString errorMsg;
            errorMsg.Format(_T("二次捕获错误: %s"), (LPCTSTR)e.Description());
            AfxMessageBox(errorMsg);
        }
    }
    

    关于自定义异常类

    • 对于简单的ADO封装,直接使用_com_error是合适的
    • 如果需要更复杂的错误处理逻辑,可以创建自定义异常类,但会增加复杂度

    1.3 断线重连后的重新执行

    实现断线重连并重新执行的模式:

    void CDemoDlg::OnBnClickedButton1()
    {
        int retryCount = 0;
        const int maxRetries = 3;
        
        while(retryCount < maxRetries)
        {
            try
            {
                // 检查连接状态,必要时重新连接
                if(IsConnectionLost())
                {
                    ReconnectDatabase();
                }
                
                g_app_ado.RstAddNew(mf_precordset);
                break; // 成功则跳出循环
            }
            catch(_com_error &e)
            {
                retryCount++;
                
                if(IsConnectionError(e) && retryCount < maxRetries)
                {
                    // 网络断线错误,尝试重连
                    Sleep(1000); // 等待1秒后重试
                    continue;
                }
                else
                {
                    // 其他错误或超过重试次数
                    CString errorMsg;
                    errorMsg.Format(_T("操作失败: %s"), (LPCTSTR)e.Description());
                    AfxMessageBox(errorMsg);
                    break;
                }
            }
        }
    }
    
    
    bool CDemoDlg::IsConnectionError(_com_error &e)
    {
        // 判断是否为连接类错误
        HRESULT hr = e.Error();
        return (hr == E_FAIL || hr == 0x80004005 || 
                hr == 0x8004005 || hr == 0x80070005);
    }
    
    
    bool CDemoDlg::IsConnectionLost()
    {
        // 实现连接状态检查逻辑
        // 返回true表示连接已断开
        return false; // 实际实现需要具体逻辑
    }
    
    
    void CDemoDlg::ReconnectDatabase()
    {
        // 实现重新连接数据库的逻辑
        g_app_ado.Reconnect();
    }
    

    1.4 异常捕获的正确形式

    推荐使用引用形式

    catch(_com_error &e)  // ✅ 推荐:避免不必要的拷贝,效率最高
    

    其他形式的比较:

    catch(_com_error e)   // ❌ 不推荐:会产生对象拷贝,性能损失
    catch(_com_error *e)  // ⚠️ 可用但不推荐:需要确保指针有效,不符合C++异常处理惯例
    

    最佳实践

    try
    {
        // 可能抛出_com_error的操作
        g_app_ado.RstAddNew(mf_precordset);
    }
    catch(_com_error &e)  // 使用const引用更佳
    {
        // 处理异常
        CString msg = e.Description();
        AfxMessageBox(msg);
    }
    catch(...)  // 捕获其他所有异常
    {
        AfxMessageBox(_T("发生未知异常"));
    }
    

    总结建议

    1. 在CADO类中抛出_com_error异常
    2. 外层使用引用形式捕获异常
    3. 实现重试机制处理网络断线等临时性错误
    4. 对于简单的ADO封装,直接使用_com_error即可,无需创建自定义异常类
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 11月22日
  • 已采纳回答 11月14日
  • 修改了问题 11月14日
  • 创建了问题 11月14日