Microsoft KB Archive/140535

{|
 * width="100%"|

FIX: Only the First 64K Is Read from Binary Field

 * }

Q140535

1.50 1.51 WINDOWS kbprg kbbuglist kbfixlist --- The information in this article applies to: - The Microsoft Foundation Classes (MFC), included with: Microsoft Visual C++ for Windows, version 1.5, and 1.51 --- SYMPTOMS ======== Only the first 64K of data is read from the binary field. CAUSE ===== A bug in the RFX_LongBinary function prevents the CLongBinary object from receiving all of the field's data when a field has more than 64K of information. In Visual C++ 1.5, starting on line 1627 of the Dbrfx.cpp file, you can see the following code: AFX_SQL_ASYNC(pFX->m_prs, ::SQLGetData(pFX->m_prs->m_hstmt, (unsigned short int)nField, SQL_C_BINARY, (UCHAR FAR*)lpLongBinary, *plLength, plLength)); ::GlobalUnlock(value.m_hData); if (!pFX->m_prs->Check(nRetCode)) pFX->m_prs->ThrowDBException(nRetCode); The ODBC function SQLGetData is called only once. This is incorrect because SQLGetData cannot fetch more than 64K of data in a single call. In Visual C++ 1.51, the equivalent code starts on line 1629 of Dbrfx.cpp: DWORD dwDataLength = 0; do { DWORD dwChunkSize = value.m_dwDataLength - dwDataLength; if (dwChunkSize > 0x8000) dwChunkSize = 0x8000; // Ignore expected data truncated warnings AFX_SQL_ASYNC(pFX->m_prs, ::SQLGetData(pFX->m_prs->m_hstmt, (unsigned short int)nField, SQL_C_BINARY, (UCHAR FAR*)lpLongBinary, dwChunkSize, plLength)); dwDataLength += *plLength; lpLongBinary += *plLength; } while (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO); ::GlobalUnlock(value.m_hData); if (!pFX->m_prs->Check(nRetCode)) pFX->m_prs->ThrowDBException(nRetCode); } return; In this case, the code has a loop that tries to get the long binary data in 32K chunks. The problem with this code is that it uses the last argument to the ::SQLGetData call as the amount by which to move the data pointer and increment the data length. This argument is assumed to be set to the amount of data that was actually transferred by the call. Unfortunately, this parameter can return with the value SQL_NO_TOTAL (defined in Sqlext.h as -4), which indicates that the data was truncated or its length could not be determined and the amount requested should be used as the amount returned. This causes the incoming data to be corrupted when it is used to move the data pointer and update the data length. This problem was corrected in Visual C++ version 1.52, where the version 1.51 code: dwDataLength += *plLength; lpLongBinary += *plLength; was replaced with this code: dwDataLength += dwChunkSize; lpLongBinary += dwChunkSize; RESOLUTION ========== Follow these steps: 1. Copy the RFX_LongBinary function to another .cpp file that you will add to your project. Rename the function to something like RFX_NewLongBinary. 2. In the RFX_LongBinary routine from Visual C++ 1.5, look for this code: const BYTE FAR* lpLongBinary; lpLongBinary = (const BYTE FAR*)::GlobalLock(value.m_hData); if (lpLongBinary == NULL) { ::GlobalFree(value.m_hData); value.m_hData = NULL; AfxThrowMemoryException; } AFX_SQL_ASYNC(pFX->m_prs, ::SQLGetData(pFX->m_prs->m_hstmt, (unsigned short int)nField, SQL_C_BINARY, (UCHAR FAR*)lpLongBinary, *plLength, plLength)); ::GlobalUnlock(value.m_hData); if (!pFX->m_prs->Check(nRetCode)) pFX->m_prs->ThrowDBException(nRetCode); Or if you are in Visual C++ 1.51, look for this code: const BYTE _huge* lpLongBinary; lpLongBinary = (const BYTE _huge*)::GlobalLock(value.m_hData); if (lpLongBinary == NULL) { ::GlobalFree(value.m_hData); value.m_hData = NULL; AfxThrowMemoryException; } DWORD dwDataLength = 0; do { DWORD dwChunkSize = value.m_dwDataLength - dwDataLength; if (dwChunkSize > 0x8000) dwChunkSize = 0x8000; // Ignore expected data truncated warnings AFX_SQL_ASYNC(pFX->m_prs, ::SQLGetData(pFX->m_prs->m_hstmt, (unsigned short int)nField, SQL_C_BINARY, (UCHAR FAR*)lpLongBinary, dwChunkSize, plLength)); dwDataLength += *plLength; lpLongBinary += *plLength; } while (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO); ::GlobalUnlock(value.m_hData); if (!pFX->m_prs->Check(nRetCode)) pFX->m_prs->ThrowDBException(nRetCode); 3. In either version, replace the listed code with this code: const BYTE _huge* lpLongBinary; lpLongBinary = (const BYTE _huge*)::GlobalLock(value.m_hData); if (lpLongBinary == NULL) { ::GlobalFree(value.m_hData); value.m_hData = NULL; AfxThrowMemoryException; } DWORD dwDataLength = 0; do { DWORD dwChunkSize = value.m_dwDataLength - dwDataLength; if (dwChunkSize > 0x8000) dwChunkSize = 0x8000; // Ignore expected data truncated warnings AFX_SQL_ASYNC(pFX->m_prs, ::SQLGetData(pFX->m_prs->m_hstmt, (unsigned short int)nField, SQL_C_BINARY, (UCHAR FAR*)lpLongBinary, dwChunkSize, plLength)); dwDataLength += dwChunkSize; lpLongBinary += dwChunkSize; } while (nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO); ::GlobalUnlock(value.m_hData); if (!pFX->m_prs->Check(nRetCode)) pFX->m_prs->ThrowDBException(nRetCode); 4. In the CRecordset's DoFieldExchange method, move the call to RFX_LongBinary outside of the ClassWizard-tagged section (the section marked with "//AFX_FIELD_MAP"). Rename it RFX_NewLongBinary -- or whatever you called the function in step 1. NOTE: If you are using the SQL Server ODBC Driver, this resolution won't work because the SQL Server ODBC Driver returns only a maximum of 4K bytes by default. For more information on this issue, please see the following article in the Microsoft Knowledge Base: ARTICLE-ID: Q126264 TITLE : PRB: CLongBinary Field Truncated with SQL Server ODBC Driver STATUS ====== Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. This problem was corrected in Microsoft Visual C++ version 1.52 for Windows. Additional reference words: 2.50 2.51 KBCategory: kbprg kbfixlist kbbuglist KBSubcategory: MFCDatabase

Keywords : kb16bitonly kbDatabase kbMFC kbODBC kbVC

Issue type :

Technology : kbAudDeveloper kbMFC