Click here to Skip to main content
15,912,756 members
Articles / Desktop Programming / MFC
Article

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

Rate me:
Please Sign up or sign in to vote.
1.14/5 (40 votes)
3 Jun 20031 min read 169.2K   20   79
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<BASETYPE, StringTraits>& 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.

History

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Brazil Brazil
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralNo one is giving the solution Pin
tmjanarthanan12-Feb-09 3:23
tmjanarthanan12-Feb-09 3:23 
GeneralLet's start over - I think we can help him Pin
LarryLeonard5-Jun-03 6:20
LarryLeonard5-Jun-03 6:20 
Okay, we all agree that you can't use CString to hold binary data. That's a given. Let's see if we can help him anyway. I'm convinced he's not a troll. (He's stubborn, arrogant, and inexperienced, but not a troll.)

If I understand him, he's trying to use an already existing object, which he did not write himself, and which only accepts CStrings, to store encrypted data. I think this is why he's been saying he's "forced" to use CString.

If that's the case, he's got a little problem, because, as we all know, you can't store binary data in a CString. But, what he CAN do is convert that binary data into characters, and then pass that to his object.

An example: let's say we have an array of five BYTEs that represent an encrypted password:

ar[0] = 32
ar[1] = 0
ar[2] = 17
ar[3] = 254
ar[4] = 133

Note that this is not a string, so it's not NULL terminated; but, it just by chance happens to have a value of zero as the second member (which would hose a CString, we all agree). Now, what we have to do is translate this array into something that can be stored in a CString.

Since I'm lazy, I choose to translate each BYTE into a three-character value; I'm sure more clever programmers could come up with more efficient methods. Anyway, using the example above, we convert this into one big CString:

CString sWhole;

for (int i = 0; i < 5; ++i) {
CString sPiece;
sPiece.Format("%0d", ar[i]);
sWhole += sPiece;
}

(Forgive my bad style.) At this point we have a CString, 'sWhole', that we can send to his third-party encryption object. It looks like:

"032000017254133"

(I know, I know, it's terribly wasteful, but on a machine with an 80 Gig hard drive, I have better things to worry about. Unless you're talking about more than a few hundred KB of data.)

It is left as an exercise for the OP to figure out how to reverse this process; that is, to translate 'sWhole' back into an array of BYTEs.
GeneralRe: Let's start over - I think we can help him Pin
Marcel245-Jun-03 6:36
Marcel245-Jun-03 6:36 
GeneralRe: Let's start over - I think we can help him Pin
leandrobecker5-Jun-03 6:46
leandrobecker5-Jun-03 6:46 
GeneralRe: Let's start over - I think we can help him Pin
Anonymous5-Jun-03 6:59
Anonymous5-Jun-03 6:59 
GeneralRe: Let's start over - I think we can help him Pin
leandrobecker5-Jun-03 8:40
leandrobecker5-Jun-03 8:40 
GeneralRe: Let's start over - I think we can help him Pin
Anonymous5-Jun-03 19:16
Anonymous5-Jun-03 19:16 
GeneralRe: Let's start over - I think we can help him Pin
LarryLeonard5-Jun-03 7:07
LarryLeonard5-Jun-03 7:07 
GeneralRe: Let's start over - I think we can help him Pin
Lonnie McCullough5-Jun-03 20:46
Lonnie McCullough5-Jun-03 20:46 
GeneralThe guy doesn't deserve a break. Pin
LarryLeonard6-Jun-03 3:53
LarryLeonard6-Jun-03 3:53 
GeneralRe: The guy doesn't deserve a break. Pin
Lonnie McCullough6-Jun-03 5:18
Lonnie McCullough6-Jun-03 5:18 
GeneralRe: The guy doesn't deserve a break. Pin
LarryLeonard9-Jun-03 3:48
LarryLeonard9-Jun-03 3:48 
GeneralRe: The guy doesn't deserve a break. Pin
Anna-Jayne Metcalfe10-Jun-03 3:12
Anna-Jayne Metcalfe10-Jun-03 3:12 
GeneralRe: Let's start over - I think we can help him Pin
Anna-Jayne Metcalfe10-Jun-03 3:07
Anna-Jayne Metcalfe10-Jun-03 3:07 
GeneralRe: Let's start over - I think we can help him Pin
LarryLeonard10-Jun-03 3:43
LarryLeonard10-Jun-03 3:43 
GeneralRe: Let's start over - I think we can help him Pin
Anna-Jayne Metcalfe10-Jun-03 3:47
Anna-Jayne Metcalfe10-Jun-03 3:47 
GeneralGive him a break, please Pin
Wu Song Da Hu5-Jun-03 4:46
Wu Song Da Hu5-Jun-03 4:46 
General... Pin
TW5-Jun-03 4:55
TW5-Jun-03 4:55 
GeneralRe: ... Pin
leandrobecker6-Jun-03 12:44
leandrobecker6-Jun-03 12:44 
GeneralRe: ... Pin
TW7-Jun-03 2:10
TW7-Jun-03 2:10 
GeneralRe: ... Pin
Anna-Jayne Metcalfe10-Jun-03 3:03
Anna-Jayne Metcalfe10-Jun-03 3:03 
GeneralRe: ... Pin
TW10-Jun-03 6:24
TW10-Jun-03 6:24 
GeneralRe: ... Pin
Anna-Jayne Metcalfe10-Jun-03 7:50
Anna-Jayne Metcalfe10-Jun-03 7:50 
GeneralRe: Give him a break, please Pin
LarryLeonard6-Jun-03 3:55
LarryLeonard6-Jun-03 3:55 
GeneralRe: Give him a break, please Pin
Paolo Messina6-Jun-03 12:39
professionalPaolo Messina6-Jun-03 12:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.