FIX: Deleting CRecordset Object Before Closing It

ID: Q113815


The information in this article applies to:


SYMPTOMS

If a CDatabase object is allocated and then passed into a CRecordset constructor the user may notice a memory leak if the CRecordset is deleted before calling CRecordset::Close(). Not only will memory leak, but the following ODBC error may eventually occur when using snapshots (or more specifically, when using the cursor library):

General Error: Unable to create file buffer
State:S1000


CAUSE

A bug in the CRecordset destructor prevents the ::SQLFreeStmt() from getting called for the ODBC statement handle (HSTMT) used by the CRecordset object.


RESOLUTION

To work around the problem, make sure that CRecordset::Close() is called before it is deleted. One way to handle this is to put a call to CRecordset::Close() in the destructor of the CRecordset-derived class.


STATUS

Microsoft has confirmed this to be a bug in the products listed at the beginning of this article. This bug was corrected in MFC version 3.0, included with Visual C++, 32-bit Edition, version 2.0.


MORE INFORMATION

The CRecordset destructor has the following code:


    CRecordset::~CRecordset()
    {
        if (!m_bRecordsetDb)
            m_pDatabase = NULL;

        ASSERT_VALID(this);

        TRY
        {
            Close();
            if (m_bRecordsetDb)
            {
                delete m_pDatabase;
                m_pDatabase = NULL;
            }
        }
        ... 
The m_bRecordsetDb is a flag that the CRecordset class uses to tell whether the CRecordset created the CDatabase object or whether it was passed to the CRecordset in the constructor. If a program passes in the CDatabase pointer to the CRecordset, looking at the code you can see that m_pDatabase will be set to NULL. Unfortunately this causes a problem when Close() is called later in the function. In CRecordset::Close() the following lines determine whether the HSTMT allocated by the CRecordset needs to be freed:

   if (m_pDatabase != SQL_NULL_HDBC)
      {
          AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
      } 
Since m_pDatabase has been set to NULL and the CDatabase object still exists, we run into a problem because SQL_NULL_HDBC is equal to NULL. Thus, the statement handle is never freed although the CRecordset goes away. This normally isn't too much of a problem because statement handles get freed when a connection goes away. However, if you are re-using a CDatabase for multiple CRecordset objects, many allocated HSTMTs will accumulate.

Additional query words: 1.50 2.50 2.51 odbc


Keywords          : kb16bitonly kbDatabase kbMFC kbODBC kbVC 
Version           : 1.50 1.51
Platform          : WINDOWS 
Issue type        : 

Last Reviewed: August 3, 1999