FIX: Hanging Occurs When Using CArrayRowset

Article translations Article translations
Article ID: 191738
Expand all | Collapse all

On This Page

Symptoms

The CArrayRowset class included with the Visual C++ 6.0 OLE DB Templates might 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, causes the 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].name when there are only 50 records), the code deliberately causes an access violation as intended. Unfortunately, the code catches the access violation before it gets to the user's code, and it goes into an endless loop.
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 access violation doesn't occur. Unfortunately, m_pTop and m_pBase are equal to the same value so no memory gets allocated. This causes the access violation to keep 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 bounds have been exceeded. Or, if you want to handle this at run time, you may want to throw your own exception. In either case, you need to change the code in the operator [] method.

Resolution

To fix both of the problems described above, you should copy the code of the CArrayRowset template into your own .h file and rename the class. You then 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 indicate that the end of rowset has been reached if the index value specified is too big. If you want to avoid C++ exception handling, you may want to revise the code to throw the access violation to the user and let the user catch the 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:

194022 INFO: Visual Studio 6.0 Service Packs, What, Where, Why

194295 HOWTO: Tell That Visual Studio 6.0 Service Packs Are Installed

Properties

Article ID: 191738 - Last Review: December 27, 2013 - Revision: 3.0
Keywords: 
kbbug kbdatabase kbdtl kbfix kbmdacnosweep kbtemplate kbvs600sp3fix KB191738

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com