在 Close() 检测到 MFC CRecordset 堆损坏

Posted

技术标签:

【中文标题】在 Close() 检测到 MFC CRecordset 堆损坏【英文标题】:MFC CRecordset heap corruption detected at Close() 【发布时间】:2013-10-28 20:33:13 【问题描述】:

我最近将我的操作系统从 Windows XP 升级到了 Windows 7 SP1 64 位。我们正在使用 Visual Studio 2008 Professional Edition 和 Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production。

当我尝试执行此代码时,出现以下异常

试试 CDatabase *pDatabase = CDatabaseConnection::getDatabaseConnectionProcessLog();

    ORSProcessLog rsProcessLog(pDatabase);
    CString strFilter = _T("SELECT PROCESS_ID, MESSAGE FROM OP_PROCESS_LOG");
    rsProcessLog.SetRowsetSize(1);
    if( !rsProcessLog.Open(CRecordset::dynaset, strFilter, CRecordset::appendOnly) )        
        return;

    if( !rsProcessLog.CanAppend() )
        return; 

    rsProcessLog.AddNew();          

    rsProcessLog.m_PROCESS_ID = gcsProcessID;
    rsProcessLog.m_MESSAGE = csMessageA;

    rsProcessLog.Update();
    rsProcessLog.Close();

catch ( CDBException* pEx )

    bException = true;
    pEx->GetErrorMessage(szCause, 255);
 
catch( CException* pEx )

    bException = true;
    pEx->GetErrorMessage(szCause, 255);
 

其中 rsProcessLog 是使用成功连接的数据库指针 pDatabase 的 CRecordset 对象

在 32 位调试版本中,我在 rsProcessLog.Close(); 处收到一个消息框;用下面的文字 调试错误

程序:......\Test.exe

检测到堆损坏:在 0x0087F628 的正常块 (#506) 之后。 CRT 检测到应用程序在堆缓冲区结束后写入内存。

内存分配在 f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\dbcore.cpp(2626)

(请重试调试应用程序)

在 32 位发布版本中,我在 rsProcessLog.Close(); 处收到一个消息框;用下面的文字 Windows 已在 Test.exe 中触发断点

这可能是由于堆损坏,这表明 Test.exe 或其已加载的任何 DLLS 中存在错误。

这也可能是由于用户在 Test.exe 获得焦点时按 F12。

输出窗口可能有更多的诊断信息。

以上代码是 Windows XP 中的工作代码,其余环境保持不变,它继续在 Windows XP 中运行,但在 Windows 7 中不运行。任何帮助将不胜感激。

【问题讨论】:

您确定您的 ORSProcessLog::Close() 中没有任何内容吗?另外,检查 ORSProcessLog 的析构函数。它可能有一些意想不到的代码 在 dbcore.cpp(2626) 处执行的内容。了解这一点可能会帮助您找出谁覆盖了这部分内存。您还可以设置内存断点。 【参考方案1】:

在对 MFC CRecordest 类进行一些分析后,我注意到在调用 ::SqlSetPos 后出现问题,其中分配在堆上的 m_rgRowStatus 数组损坏(在堆上多写了一个字节)。

解决问题的第一个干净的方法是强制 CRecordset 类使用更新/删除 SQL 语句而不是 ::SqlSetPos 函数。要实现这一点,您需要使用带有CDatabase::userCursorLib 选项的OpenEx 方法打开数据库。

CDatabase db;
db.OpenEx(ConnectionString, CDatabase:useCursorLib);

这将改变一些 CRecordset 功能。查看更多信息:http://msdn.microsoft.com/en-us/library/c689y99f.aspx

第二种方法(脏)是重新分配m_rgRowStatus 字段,以在堆上使用更多内存,因此对::SqlSetPos 的调用不会写入未分配的内存。

一种方法是覆盖CRecordset::PreBindFields 函数:

void CMyDerivedRecordset::PreBindFields()

    if  ( ! (m_dwOptions & useMultiRowFetch) )
    
        delete [] m_rgRowStatus;
        m_rgRowStatus = new WORD[2];
    

    CRecordset::PreBindFields();

您可以在所有CRecordset 派生类中这样做,或者您可以创建一个新的CRecordset 派生类,它将成为所有现有CRecordset 派生类的基类。

【讨论】:

以上是关于在 Close() 检测到 MFC CRecordset 堆损坏的主要内容,如果未能解决你的问题,请参考以下文章

多字节 MFC 库未检测到 Visual Studio

inotify IN_CLOSE_WRITE 仅检测复制到目录的文件

mfc 关于Accept函数

当模式对话框处于活动状态时,检测主应用程序窗口上的 WM_CLOSE 事件?

在使用它之前检测 window.close() 是不是可以工作(JavaScript)

C++ MFC 键盘加速器