c++streamstoragecompound-file

LPSTREAM unable to read into CString


I'm attempting to read text into a CString using an LPSTREAM, but it doesn't seem to be working correctly, here is the code that I'm calling:

static HRESULT UTL_ReadStreamTxt(MyStorage* pSrcStg, const char* pszStream, CString* myCStr, int size)
{
        HRESULT     hrRet = STG_E_INVALIDPARAMETER;
        LPSTREAM    lpSrc = NULL;
        ULONG ul;

        TRY
        { 
            USES_CONVERSION;
            HRESULT hrSrc = pSrcStg->GetStg()->OpenStream(CT2COLE(strSrc),          
                                                    NULL,
                                                    STGM_READ | STGM_SHARE_EXCLUSIVE,
                                                    0,
                                                    &lpSrc);
            if (hrSrc != NOERROR)
            {
                hrRet = hrSrc;
            }
            else
            {
                hrRet = lpSrc->Read(&myCStr, size, NULL); // Read into CString
            }

        }

        CATCH_ALL(e)
        {
            hrRet = STG_E_UNKNOWN;
        }
        END_CATCH_ALL


        _AfxRelease((LPUNKNOWN*)&lpSrc);

        return hrRet;
}

When it reads into the string, Visual Studio says that the data in the CString is corrupted.

The compound storage's stream contents are the following:

abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz

I'm not entirely sure I'm using Read() correctly, How do I fix this issue?


Solution

  • The main problem is that you are passing a bad pointer to Read(). You are passing the memory address of the myCStr parameter itself, not the address of the CString being pointed at, or more accurately, the memory address of a character buffer that the CString owns. The code compiles only because Read() takes a simple void* pointer to a buffer, and any pointer is implicitly convertible to void*.

    Also note that CString is based on TCHAR, which maps to either char or wchar_t depending on whether you are compiling your project for ANSI/MBCS or Unicode. So, reading from the stream directly into a CString will only work correctly if:

    1. the stream contains ANSI characters and TCHAR maps to char.

    2. the stream contains UTF-16 characters and TCHAR maps to wchar_t.

    If the stream's character type does not match the character type used by CString, you would have to first read the stream into an intermediate buffer and then convert that to TCHAR before it can be stored in the CString.

    Try something more like this:

    static HRESULT UTL_ReadStreamTxt(MyStorage* pSrcStg, const char* pszStream, CString* myCStr, int size)
    {
        HRESULT     hrRet = STG_E_INVALIDPARAMETER;
        LPSTREAM    lpSrc = NULL;
        ULONG       ul;
        LPVOID      buffer;
    
        TRY
        { 
            USES_CONVERSION;
            HRESULT hrSrc = pSrcStg->GetStg()->OpenStream(CT2COLE(strSrc),          
                                                        NULL,
                                                        STGM_READ | STGM_SHARE_EXCLUSIVE,
                                                        0,
                                                        &lpSrc);
            if (hrSrc != S_OK)
            {
                hrRet = hrSrc;
            }
            else
            {
                // if the stream's character type matches TCHAR...
    
                buffer = myCStr->GetBuffer(size / sizeof(TCHAR));
                hrRet = lpSrc->Read(buffer, size, &ul);
                myCStr->ReleaseBuffer(ul / sizeof(TCHAR));
    
                // else, if the stream's character type is 'char' and TCHAR is 'wchar_t'...
    
                CStringA tmp;
                buffer = tmp.GetBuffer(size);
                hrRet = lpSrc->Read(buffer, size, &ul);
                tmp.ReleaseBuffer(ul);
                *myCStr = CString((LPSTR)tmp, tmp.GetLength());
    
                // else, if the stream's character type is 'wchar_t' and TCHAR is 'char'...
    
                CStringW tmp;
                buffer = tmp.GetBuffer(size / sizeof(wchar_t));
                hrRet = lpSrc->Read(buffer, size, &ul);
                tmp.ReleaseBuffer(ul / sizeof(wchar_t));
                *myCStr = CString((LPWSTR)tmp, tmp.GetLength());
    
                // alternatively, you can do the above 2 cases more generically...
    
                typedef CStringT<char or wchar_t> CStreamString;
                CStreamString tmp;
                buffer = tmp.GetBuffer(size / sizeof(CStreamString::XCHAR));
                hrRet = lpSrc->Read(buffer, size, &ul);
                tmp.ReleaseBuffer(ul / sizeof(CStreamString::XCHAR));
                *myCStr = CString((CStreamString::PXSTR)tmp, tmp.GetLength());
            }
        }
    
        CATCH_ALL(e)
        {
            hrRet = STG_E_UNKNOWN;
        }
        END_CATCH_ALL
    
        _AfxRelease((LPUNKNOWN*)&lpSrc);
    
        return hrRet;
    }