BUG: DFX_Text() Sets CString Length Incorrectly in Unicode Build

Last reviewed: January 22, 1998
Article ID: Q179671
The information in this article applies to:
  • Microsoft Visual Studio versions 97, 97sp3
  • Microsoft Visual C++, 32-bit Enterprise Edition, versions 5.0, 5.0sp1, 5.0sp2, 5.0sp3

SYMPTOMS

In a Unicode build of an MFC DAO project, the Record Field Exchange function DFX_Text() incorrectly sets the length of its CString argument to twice the actual length of the data fetched from the database. This action causes any concatenation operation with the CString to behave unexpectedly.

NOTE: This article contains the source code to corrected versions of DFX_Text() and DAOStringAllocCallback.

CAUSE

At line 906 in the Daodfx.cpp file within the DFX_Text() function, the length of the CString is set to the number of bytes rather than the number of characters returned from the database:

   value.GetBufferSetLength(
   pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] - 1);

Also, at line 1391 in the Daodfx.cpp file within the DAOStringAllocCallback() function [used as a callback function by DFX_Text()], the length of the CString is set to the number of bytes rather than the number of characters returned from the database:

   pstr->ReleaseBuffer(dwLen);

RESOLUTION

To resolve this problem, divide the string length by sizeof(TCHAR). In an ANSI build, a TCHAR is 1 byte in length; in a Unicode build, a TCHAR is 2 bytes in length.

This article contains the source code to corrected versions of DFX_Text() and DAOStringAllocCallback(), which contain the two fixes shown below:

Line 906, Daodfx.cpp, DFX_Text()

Original version:

   pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] - 1);

Fixed version:

   (pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] / sizeof(TCHAR)) –
      sizeof(TCHAR));

Line 1391, Daodfx.cpp, DAOStringAllocCallback()

Original version:

   lpsz = pstr->GetBuffer(dwLen);

Fixed version:

   lpsz = pstr->GetBuffer(dwLen / sizeof(TCHAR));

STATUS

Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. We are researching this bug and will post new information here in the Microsoft Knowledge Base as it becomes available.

MORE INFORMATION

Copy and paste the following two functions and function declarations into your project:

////////////////////////////////////////////////

   STDAPI DaoStringAllocCallback2(DWORD dwLen, DWORD pData, void** ppv);

   STDAPI DaoStringAllocCallback2(DWORD dwLen, DWORD pData, void** ppv)
   {
      LPTSTR lpsz;
      CString* pstr = (CString*)pData;

   #ifndef _UNICODE
      // If using ANSI DAO interfaces, DAO reports field length
      // rather than data length. In this case there will not be space
      // for NULL terminator if data length equals field length. Make room.
      dwLen++;
   #endif

      TRY
      {
         // Only re-allocate if necessary
         lpsz = pstr->GetBuffer(dwLen / sizeof(TCHAR));
         pstr->ReleaseBuffer(dwLen);
         *ppv = (void*)(dwLen > 0 ? lpsz : NULL);
      }
      CATCH_ALL(e)
      {
         e->Delete();
         return E_OUTOFMEMORY;
      }
      END_CATCH_ALL

      return S_OK;
   }

   void AFXAPI DFX_Text2(CDaoFieldExchange* pFX, LPCTSTR lpszName,
      CString& value, int nPreAllocSize = AFX_DAO_TEXT_DEFAULT_SIZE,
      DWORD dwBindOptions = AFX_DAO_ENABLE_FIELD_CACHE);

   void AFXAPI DFX_Text2(CDaoFieldExchange* pFX, LPCTSTR lpszName,
      CString& value, int nPreAllocSize, DWORD dwBindOptions)
   {
      (pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
         ++pFX->m_nField: ++pFX->m_nParam;

      // Do nothing if op not supported for outputColumn or param type
      if (!pFX->IsValidOperation())
         return;

      DWORD dwDAOType;

   #ifdef _UNICODE
      dwDAOType = DAO_WCHAR;
   #else
      dwDAOType = DAO_CHAR;
   #endif

      switch (pFX->m_nOperation)
      {
      case CDaoFieldExchange::BindField:
         {
            // Pre-allocate buffer to prevent needless re-allocations
            value.GetBuffer(nPreAllocSize);
            value.ReleaseBuffer();

            LPDAOCOLUMNBINDING pcb =
               &pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
            pcb->dwDataType = dwDAOType;
            pcb->dwBinding = DAOBINDING_DIRECT | DAOBINDING_CALLBACK;
            pcb->dwUser = (DWORD)&value;
            pcb->cbDataOffset = (DWORD)DaoStringAllocCallback2;
            pcb->cbMaxLen = INT_MAX;
         }
         // Fall through to finish setting up column binding struct

      default:
         pFX->Default(lpszName, (void*)&value, AFX_RFX_TEXT,
            dwBindOptions);
         return;

      case CDaoFieldExchange::Fixup:
         if (pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] == 0 ||
            pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] == DAO_NULL)
         {
            // If null or empty string set length zero
            value.GetBufferSetLength(0);
         }
         else
         {
            // Make sure length correct (and doesn't include NULL
            //terminator)
            value.GetBufferSetLength(
     
        (pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] /
   sizeof(TCHAR))- sizeof(TCHAR));

         }

         pFX->Default(lpszName, (void*)&value,
            AFX_RFX_TEXT, dwBindOptions);
         return;

   #ifdef _DEBUG
      case CDaoFieldExchange::DumpField:
         *pFX->m_pdcDump << "\n" << lpszName << " = " << value;
         return;
   #endif //_DEBUG
      }
   }

//////////////////////////////////////////////

Below is an example of how to use the DFX_Text2 function in your project:

   void CDaoSet::DoFieldExchange(CDaoFieldExchange* pFX)
   {
   #ifdef _UNICODE
      //{{AFX_FIELD_MAP(CDaoSet)
      pFX->SetFieldType(CDaoFieldExchange::outputColumn);
      DFX_Short(pFX, _T("[Hours]"), m_Hours);
      //}}AFX_FIELD_MAP
      DFX_Text2(pFX, _T("[CourseID]"), m_CourseID);
      DFX_Text2(pFX, _T("[CourseTitle]"), m_CourseTitle);
   #else
   //{{AFX_FIELD_MAP(CDaoSet)
      pFX->SetFieldType(CDaoFieldExchange::outputColumn);
      DFX_Text(pFX, _T("[CourseID]"), m_CourseID);
      DFX_Text(pFX, _T("[CourseTitle]"), m_CourseTitle);
      DFX_Short(pFX, _T("[Hours]"), m_Hours);
      //}}AFX_FIELD_MAP
   #endif

   }


Additional query words: MFCDAO MFCDatabase
Component : dao
Version : WINDOWS:5.0,5.0sp1,5.0sp2,5.0sp3,97,97sp3
Platform : WINDOWS
Issue type : kbbug
Solution Type : kbpending


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: January 22, 1998
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.