FIX: Hanging Occurs When Using CArrayRowset

This article has been archived. It is offered "as is" and will no longer be updated.
Symptoms
The CArrayRowset class included with the Visual C++ 6.0 OLE DB Templatesmight hang when retrieving data.
Cause
There are two bugs that can cause a hang to occur:
  • A bug in CVirtualBuffer, which CArrayRowset is derived from, causesthe code to go into a loop when it fetches over one page of data (4K).
  • If you try to obtain a record that doesn't exist (i.e. prod[51].namewhen there are only 50 records), the code deliberately causes an accessviolation as intended. Unfortunately, the code catches the accessviolation before it gets to the user's code, and it goes into an endlessloop.
The following code in the CVirtualBuffer class reproduces the bug:
   int Except(LPEXCEPTION_POINTERS lpEP)   {      EXCEPTION_RECORD* pExcept = lpEP->ExceptionRecord;      if (pExcept->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)        return EXCEPTION_CONTINUE_SEARCH;      BYTE* pAddress = (LPBYTE) pExcept->ExceptionInformation[1];      VirtualAlloc(pAddress, ((BYTE*)m_pTop - (BYTE*)m_pBase), MEM_COMMIT,                   PAGE_READWRITE);      return EXCEPTION_CONTINUE_EXECUTION;   }				


The purpose of the function is to commit the memory so that the accessviolation doesn't occur. Unfortunately, m_pTop and m_pBase are equal to thesame value so no memory gets allocated. This causes the access violation tokeep occurring and the code gets stuck in an endless loop.

If you look at the CArrayRowset operator [] code for the second problem,you should add some method to let the programmer know that the array boundshave been exceeded. Or, if you want to handle this at run time, you maywant to throw your own exception. In either case, you need to change thecode in the operator [] method.
Resolution
To fix both of the problems described above, you should copy the code ofthe CArrayRowset template into your own .h file and rename the class. Youthen need to fix the class.

The following sample demonstrates what the resultant class might look like.The code has added C++ exception handling to throw an exception to indicatethat the end of rowset has been reached if the index value specified is toobig. If you want to avoid C++ exception handling, you may want to revisethe code to throw the access violation to the user and let the user catchthe exception and handle it.

Sample Code

   #include <oledberr.h>   class CDBEndOfRowset   {   };   template <class T, class TRowset = CRowset>   class CArrRowset:     public CVirtualBuffer<T>,     public TRowset   {     public:       CArrRowset(int nMax = 100000) : CVirtualBuffer<T>(nMax)       {         m_nRowsRead = 0;       }       int Except(LPEXCEPTION_POINTERS lpEP)       {          EXCEPTION_RECORD* pExcept = lpEP->ExceptionRecord;          if (pExcept->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)             return EXCEPTION_CONTINUE_SEARCH;          BYTE* pAddress = (LPBYTE) pExcept->ExceptionInformation[1];          VirtualAlloc(pAddress, sizeof(T), MEM_COMMIT, PAGE_READWRITE);          return EXCEPTION_CONTINUE_EXECUTION;       }       T& operator[](int nRow)       {         ATLASSERT(nRow >= 0);         HRESULT hr;         T* m_pCurrent = m_pBase + m_nRowsRead;         T* pTEndofRowset = NULL;         // Retrieve the row if you haven't retrieved it already.         while ((ULONG)nRow >= m_nRowsRead)         {           m_pAccessor->SetBuffer((BYTE*)m_pCurrent);           __try          {            // Get the row.            hr = MoveNext();            if (hr == DB_S_ENDOFROWSET)               throw CDBEndOfRowset();            if (hr != S_OK)            {               *((char*)0) = 0; // Force exception.            }          }          __except(Except(GetExceptionInformation()))          {          }          m_nRowsRead++;          m_pCurrent++;         }         return *(m_pBase + nRow);       }       HRESULT Snapshot()       {         ATLASSERT(m_nRowsRead == 0);         ATLASSERT(m_spRowset != NULL);         HRESULT hr = MoveFirst();         if (FAILED(hr))           return hr;         do         {            Write(*(T*)m_pAccessor->GetBuffer());            m_nRowsRead++;            hr = MoveNext();         } while (SUCCEEDED(hr) &&  hr != DB_S_ENDOFROWSET);         return (hr == DB_S_ENDOFROWSET) ? S_OK : hr;       }       // Implementation.       ULONG   m_nRowsRead;   };				
Status
Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

This bug was corrected in Visual Studio 6.0 Service Pack 3. For more information about Visual Studio service packs, please see the following articles in the Microsoft Knowledge Base:

Properties

Article ID: 191738 - Last Review: 12/28/2014 19:53:36 - Revision: 4.0

  • kbnosurvey kbarchive kbbug kbdatabase kbdtl kbfix kbmdacnosweep kbtemplate kbvs600sp3fix KB191738
Feedback