Differences in the behaviour of CString >> and << using MFC 7 and double '\0' ended strings (like REG_MULTI_SZ)






1.14/5 (35 votes)
Jun 4, 2003
1 min read

171597
Differences in the behaviour of CString >> and << using MFC 7 and double '\0' ended strings (like REG_MULTI_SZ) and a workaround
Introduction
This article explain the behaviour diference using CString with binary buffers (GetBuffer or GetBufferSetLength using REG_MULTI_SZ) and the serialization (more precisally the read operation, the write works fine). The problem occurs when your buffer contains '\0' at the middle of the buffer. The CArchive << writes all buffer to the file but the CArchive >> reads all data from the file as expected in a temporary buffer and performs an simple assignment to the CString using the CString operator=. This is the problem, the CString operator = stops in a '\0'. Below is the MFC code with the bug and a workaround to try solve the problem.
Peoples can say, why this ? Use an array. Yes I agree with all of then, but, I my case I can't use an array. This is the first time in 5 years of MFC development that I must made this "heresy" like some people consider it.
I have changed the title of the article to behaviour diference because the opinion of the people.
Using the code
This is the MFC code:
template< typename BaseType, class StringTraits > CArchive& AFXAPI operator>>(CArchive& ar, ATL::CStringT& str) { int nCharSize; // 1 = char, 2 = wchar_t UINT nLength = UINT( AfxReadStringLength(ar, nCharSize) ); if (nCharSize == sizeof(char)) { ATL::CTempBuffer< char > pszBufferA(nLength+1); pszBufferA[nLength] = '\0'; UINT nBytesRead = ar.Read(pszBufferA, nLength*sizeof(char)); if (nBytesRead != (nLength*sizeof(char))) AfxThrowArchiveException(CArchiveException::endOfFile); str = pszBufferA; } else { ASSERT(nCharSize == sizeof(wchar_t)); ATL::CTempBuffer< wchar_t > pszBufferW( nLength+1 ); pszBufferW[nLength] = L'\0'; UINT nBytesRead = ar.Read(pszBufferW, nLength*sizeof(wchar_t)); if (nBytesRead != (nLength*sizeof(wchar_t))) AfxThrowArchiveException(CArchiveException::endOfFile); str = pszBufferW; } return ar; }
This is a workaround:
EXPORT_LCC UINT LCC_ReadBinaryCStringFromArchive(CArchive& ar, CString& str) { ASSERT(ar.IsLoading()); UINT nBytesRead; int nCharSize; // 1 = char, 2 = wchar_t UINT nLength = UINT( AfxReadStringLength(ar, nCharSize) ); if (nCharSize == sizeof(char)) { // is UNICODE ? if nLength is 10 chars we must 5 wchar_t // to hold 10 chars if (nCharSize != sizeof(TCHAR)) nBytesRead = ar.Read(str.GetBufferSetLength(nLength / sizeof (TCHAR) + (nLength % sizeof(TCHAR))), nLength * sizeof(char)); else nBytesRead = ar.Read(str.GetBufferSetLength(nLength), nLength*sizeof(char)); if (nBytesRead != (nLength*sizeof(char))) AfxThrowArchiveException(CArchiveException::endOfFile); } else { ASSERT(nCharSize == sizeof(wchar_t)); // not is UNICODE ? if nLength is 10 w_chars we must // 20 chars to hold 10 w_chars if (nCharSize != sizeof(TCHAR)) nBytesRead = ar.Read(str.GetBufferSetLength(nLength * sizeof(TCHAR)), nLength * sizeof(wchar_t)); else nBytesRead = ar.Read(str.GetBufferSetLength(nLength), nLength*sizeof(wchar_t)); if (nBytesRead != (nLength*sizeof(wchar_t))) AfxThrowArchiveException(CArchiveException::endOfFile); } return nBytesRead; }
I know that not is the same facility of CArchive overloaded operator >> but works for me. Use CString only if you need. If not, use an array.