Microsoft KB Archive/191738

From BetaArchive Wiki
Knowledge Base


Article ID: 191738

Article Last Modified on 3/2/2005



APPLIES TO

  • Microsoft OLE DB 2.7, when used with:
    • Microsoft Visual C++ 6.0 Enterprise Edition
    • Microsoft Visual C++ 6.0 Professional Edition
    • Microsoft Visual C++ 6.0 Standard Edition



This article was previously published under Q191738

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:

Keywords: kbbug kbfix kbconsumer kbdatabase kbtemplate kbdtl kbmdacnosweep kbvs600sp3fix KB191738