/*++

Copyright (C) 1996-2001 Microsoft Corporation

Module Name:

    STRM.CPP

Abstract:

  CMemStream implementation.

  This is a generic data stream for object/interface marshaling.

History:

  a-raymcc    10-Apr-96   Created.
  a-raymcc    06-Jun-96   CArena support.
  a-raymcc    11-Sep-96   Support NULL pointers

--*/

#include "precomp.h"

#include <stdio.h>
#include <string.h>

#include <strm.h>

//***************************************************************************
//
//  CMemStream::CMemStream
//
//  Standard constructor.
//
//  PARAMETERS:
//  nFlags
//      auto_delete or no_delete.  If auto_delete, the internal buffer
//      is automatically deallocated at destruct-time.  If no_delete,
//      it is assumed that another object has acquired the m_pBuffer
//      pointer and will deallocate it with m_pArena->Free().
//  pArena
//      The arena to use for allocation/deallocation.  If NULL, the
//      CWin32DefaultArena is used.
//  nInitialSize
//      The initial size of the stream in bytes.
//  nGrowBy
//      How much to grow the internal buffer by when the caller has written
//      past the end-of-stream.
//
//***************************************************************************
// ok
CMemStream::CMemStream(
    int nFlags,
    CArena *pArena,
    int nInitialSize,
    int nGrowBy
    )
{
    _ASSERT((nInitialSize >= 16), __TEXT("CMemStream: Initial size must be >= 16"));
    
    m_lRef = 0;
    m_nStatus = no_error;

    if (pArena == 0) 
        m_pArena = _new CWin32DefaultArena;
    else 
        m_pArena = pArena;
    m_pArena->AddRef();

    m_pBuffer = (BYTE *) m_pArena->Alloc((DWORD) nInitialSize + 
        sizeof(STREAM_HEADER));
    if (!m_pBuffer)
        m_nStatus = out_of_memory;

    m_dwGrowBy = (DWORD) nGrowBy;
    m_dwSize = nInitialSize;
    m_dwCurrentPos = 0;
    m_nFlags = nFlags;
    m_nStackPtr = -1;
    m_dwEndOfStream = 0;

    // Write the validation header for this new stream.
    // ================================================

    STREAM_HEADER hdr;
    WriteBytes(&hdr, sizeof(STREAM_HEADER));    
}

//***************************************************************************
//
//  CMemStream::CMemStream
//
//  Binding constructor.
//
//***************************************************************************
// ok
CMemStream::CMemStream(
    LPVOID pBindingAddress,
    CArena *pArena,
    int nFlags,
    int nGrowBy
    )
{
    m_nStatus = no_error;
    m_pBuffer = (LPBYTE) pBindingAddress;
    m_pArena = pArena;
    m_pArena->AddRef();
    m_nFlags = nFlags;
    m_lRef = 0;

    // The stream header must be present in the block which
    // is being bound to.
    // ====================================================

    STREAM_HEADER& hdr = *(STREAM_HEADER *) m_pBuffer;

    if (!hdr.Verify()) {
        m_nStatus = failed;
        return;
    }
    
    // Initialize the remaining member variables.
    // ==========================================

    m_dwSize = hdr.dwLength;
    m_dwGrowBy = nGrowBy;
    m_dwCurrentPos = sizeof(hdr);
    m_dwEndOfStream = hdr.dwLength;
    m_nFlags = nFlags;
    m_nStackPtr = -1;
}



//***************************************************************************
//
//  CMemStream::CMemStream
//
//  Copy constructor.
//
//***************************************************************************
// ok
CMemStream::CMemStream(CMemStream &Src)
{
    m_nStatus = 0;
    m_pBuffer = 0;
    m_dwGrowBy = 0;
    m_dwSize = 0;
    m_dwCurrentPos = 0;
    m_nFlags = 0;
    m_nStackPtr = -1;
    m_dwEndOfStream = 0; 
    m_lRef = 0;

    // The assignment operator does not copy the arena
    // so as to allow transfers of stream objects between
    // arenas.  So, we have to set up the arena here.
    // ==================================================
    m_pArena = Src.m_pArena;
    m_pArena->AddRef();

    *this = Src;
}

//***************************************************************************
//
//  CMemStream::operator =
//
//  Note that the arena is not copied as part of the assignment.  This
//  is to allow transfer of objects between arenas.
//
//***************************************************************************
// ok
CMemStream& CMemStream::operator =(CMemStream &Src)
{
    m_nStatus = Src.m_nStatus;
    m_dwSize = Src.m_dwSize;

    if (m_pBuffer)
        m_pArena->Free(m_pBuffer);

    m_pBuffer = (BYTE *) m_pArena->Alloc(m_dwSize);
    if (!m_pBuffer)
        m_nStatus = out_of_memory;
    else
        memcpy(m_pBuffer, Src.m_pBuffer, m_dwSize);

    m_dwGrowBy = Src.m_dwGrowBy;

    m_dwCurrentPos = Src.m_dwCurrentPos;
    m_nFlags = Src.m_nFlags;
    m_nStackPtr = Src.m_nStackPtr;
    m_dwEndOfStream = Src.m_dwEndOfStream;

    return *this;
}


//***************************************************************************
//
//  CMemStream::Deserialize
//
//  This function deserializes a stream from a Win32 file handle.
//  This function only works for files (including memory mapped files, etc.)
/// and pipes.  It will not work for mailslots since they must be read
//  in one operation.
//
//  PARAMETERS:
//  hFile
//      The Win32 file handle.
//
//  RETURN VALUES:
//  failed, out_of_memory, no_error
//
//***************************************************************************
// ok
int CMemStream::Deserialize(HANDLE hFile)
{
    Reset();    

    // Read the header.  Note that we read this separately
    // first before the stream proper.  This is because we
    // don't know how much memory to allocate until we have
    // read the header.
    // ====================================================

    STREAM_HEADER &hdr = *(STREAM_HEADER *) m_pBuffer;
    BOOL bRes;
    DWORD dwRead;

    bRes = ReadFile(hFile, &hdr, sizeof(hdr), &dwRead, 0);
    if (!bRes || dwRead != sizeof(hdr))
    {
        DWORD t_LastError = GetLastError () ;
        return failed;
    }
    if (!hdr.Verify())
        return failed;

    DWORD t_ReadLength = hdr.dwLength;

    // Read the rest.
    // ===============
    if (Resize(hdr.dwLength))
        return out_of_memory;

    BYTE *t_ReadPosition = m_pBuffer + sizeof ( hdr ) ;
    DWORD t_Remainder = m_dwSize - dwRead ;

    while ( t_Remainder )
    {
        bRes = ReadFile(hFile, t_ReadPosition , t_Remainder, &dwRead, 0);
        if (!bRes )
            return failed;

        t_Remainder = t_Remainder - dwRead ;
        t_ReadPosition = t_ReadPosition + dwRead ;
    }

    m_dwEndOfStream = t_ReadLength;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::Deserialize
//
//  This function deserializes a stream from an ANSI C file object.
//
//  PARAMETERS:
//  fStream
//      The ANSI C file object.
//
//  RETURN VALUES:
//  failed, out_of_memory, no_error
//
//***************************************************************************
// ok
int CMemStream::Deserialize(FILE *fStream)
{
    Reset();

    STREAM_HEADER &hdr = *(STREAM_HEADER *) m_pBuffer;
    
    if (1 != fread(&hdr, sizeof(STREAM_HEADER), 1, fStream))
        return failed;
    if (!hdr.Verify())
        return failed;
        
    if (Resize(hdr.dwLength) != no_error)
        return out_of_memory;

    DWORD dwRemainder = m_dwSize - sizeof(hdr);
    
    if (dwRemainder != fread(m_pBuffer + sizeof(hdr), sizeof(BYTE), 
        dwRemainder, fStream))
        return failed;

    m_dwEndOfStream = hdr.dwLength;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::Deserialize
//
//  This function deserializes a stream from a memory block.
//
//  PARAMETERS:
//  pBlock
//      The memory block.
//  dwSize
//      The size of the memory block.
//
//  RETURN VALUES:
//  failed, no_error
//
//***************************************************************************

int CMemStream::Deserialize(LPBYTE pBlock, DWORD dwSize)
{
    Reset();

    STREAM_HEADER *pHdr = (STREAM_HEADER *) pBlock;

    if (!pHdr->Verify())
        return failed;

    Resize(pHdr->dwLength);

    memcpy(
        m_pBuffer,
        pBlock,
        pHdr->dwLength
        );

    m_dwEndOfStream = pHdr->dwLength;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::Resize
//
//  Increases the size of the internal buffer.  Does not affect the
//  current offset or end-of-stream markers.
//
//  RETURN VALUES:
//  no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::Resize(DWORD dwNewSize)
{
    m_pBuffer = (BYTE *) m_pArena->Realloc(m_pBuffer, dwNewSize + 
        sizeof(STREAM_HEADER));

    if (!m_pBuffer) {
        m_nStatus = out_of_memory;
        return m_nStatus;
    }

    STREAM_HEADER& hdr = *(STREAM_HEADER *) m_pBuffer;
    hdr.dwSignature = SIGNATURE_STREAM ;
    hdr.dwLength = dwNewSize + sizeof ( STREAM_HEADER ) ;

    m_dwSize = dwNewSize;
    return no_error;
}

//***************************************************************************
//
//  CMemStream::Serialize
//
//  Writes the object to a Win32 file.
//
//  PARAMETERS:
//  hFile
//      The Win32 file handle to which to write the object.
//
//  RETURN VALUES:
//  failed, no_error
//
//***************************************************************************
// ok
int CMemStream::Serialize(HANDLE hFile)
{
    UpdateHdr();
    
    // Write body of stream.  This includes the header.
    // ================================================

    DWORD dwWritten = 0;
    BOOL bRes = WriteFile(hFile, m_pBuffer, m_dwEndOfStream, &dwWritten, 0);
    if (!bRes || m_dwEndOfStream != dwWritten)
        return failed;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::Serialize
//
//  Serializes the entire stream to the specified FILE object, which
//  must have been opened in binary mode for writing.
//
//  PARAMETERS:
//  fStream
//      The ANSI C FILE object to which to write the object.
//
//  RETURN VALUE:
//  no_error, failed
//
//***************************************************************************
// ok

int CMemStream::Serialize(FILE *fStream)
{
    UpdateHdr();
    
    // Write body of stream.
    // =====================

    int nRes = fwrite(m_pBuffer, sizeof(BYTE), m_dwEndOfStream, fStream);
    if (nRes != (int) m_dwEndOfStream)
        return failed;

    return no_error;
}


//***************************************************************************
//
//  CMemStream::Serialize
//
//  Serializes the entire object to a memory block which is allocated
//  with HeapAlloc using the default process heap. The caller must call
//  FreeMem() when the block is no longer needed.
//
//  PARAMETERS:
//  pBlock
//      Should point to NULL on entry and will be assigned to point to the
//      new memory block containing the serialized form of the object.
//      The receiver must call FreeMem() using the default process heap
//      when this block is no longer needed.
//  pdwSize
//      Points to a DWORD which will be set to the size in bytes
//      of the returned memory block.
//
//  RETURN VALUES:
//  out_of_memory, no_error
//
//***************************************************************************

int CMemStream::Serialize(BYTE **pBlock, DWORD *pdwSize, CArena *pArena)
{
    UpdateHdr();
    if (!pArena)
        pArena = m_pArena;

    LPVOID pMem = pArena->Alloc(m_dwEndOfStream);            
    if (!pMem)
        return out_of_memory;
        
    memcpy(pMem, m_pBuffer, m_dwEndOfStream);
    *pBlock = (BYTE *) pMem;
    *pdwSize = m_dwEndOfStream;
    
    return no_error;
}


//***************************************************************************
//
//  CMemStream::Append
//
//  Appends one CMemStream object onto the end of the current one.
//  When deserialization occurs, the stream will appear as one large object;
//  the original number of appended objects is lost information.
//
//  PARAMETERS:
//  pSubStream
//      The stream which needs to be appended to 'this' object.
//
//  RETURN VALUES:
//  no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::Append(CMemStream *pSubstream)
{
    DWORD dwNumBytesToCopy =  pSubstream->m_dwEndOfStream - sizeof(STREAM_HEADER);

    if (no_error != Resize(m_dwEndOfStream + dwNumBytesToCopy))
        return out_of_memory;

    memcpy(&m_pBuffer[m_dwEndOfStream], 
        pSubstream->m_pBuffer + sizeof(STREAM_HEADER),
        dwNumBytesToCopy
        );

    m_dwEndOfStream += dwNumBytesToCopy;
    m_dwCurrentPos = m_dwEndOfStream;

    return no_error;
}

// SAFE AREA

//***************************************************************************
//
//  CMemStream::WriteBlob
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteBlob(BLOB *pBlob)
{
    int nRes;
    nRes = WriteType(VT_BLOB);
    if (nRes) return nRes;

    DWORD dwSize = BlobLength(pBlob);
    if (WriteBytes(&dwSize, sizeof(DWORD)) != no_error)
        return out_of_memory;
    if (WriteBytes(BlobDataPtr(pBlob), dwSize) != no_error)
        return out_of_memory;

    return no_error;
}


//***************************************************************************
//
//  CMemStream::WriteDouble
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteDouble(double dblVal)
{
    int nRes;
    nRes = WriteType(VT_R8);
    if (nRes) return nRes;
    nRes = WriteBytes(&dblVal, sizeof(double));
    return nRes;
}


//***************************************************************************
//
//  CMemStream::WriteFloat
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteFloat(float fltVal)
{
    int nRes;
    nRes = WriteType(VT_R4);
    if (nRes) return nRes;
    nRes = WriteBytes(&fltVal, sizeof(float));
    return nRes;
}


//***************************************************************************
//
//  CMemStream::WriteFloat
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteBool(VARIANT_BOOL bVal)
{
    int nRes;
    nRes = WriteType(VT_BOOL);
    if (nRes) return nRes;
    nRes = WriteBytes(&bVal, sizeof(VARIANT_BOOL));
    return nRes;
}


//***************************************************************************
//
//  CMemStream::WriteByte
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteByte(BYTE b)
{
    int nRes;
    nRes = WriteType(VT_UI1);
    if (nRes) return nRes;
    nRes = WriteBytes(&b, sizeof(BYTE));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteChar
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteChar(char c)
{
    int nRes;
    nRes = WriteType(VT_I1);
    if (nRes) return nRes;
    nRes = WriteBytes(&c, sizeof(char));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteLong
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteLong(LONG l)
{
    int nRes;
    nRes = WriteType(VT_I4);
    if (nRes) return nRes;
    nRes = WriteBytes(&l, sizeof(LONG));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteDWORD
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteDWORD(DWORD dwVal)
{
    int nRes;
    nRes = WriteType(VT_UI4);
    if (nRes) return nRes;
    nRes = WriteBytes(&dwVal, sizeof(DWORD));
    return nRes;
}


//***************************************************************************
//
//  CMemStream::WriteDWORD
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteWORD(WORD wVal)
{
    int nRes;
    nRes = WriteType(VT_UI2);
    if (nRes) return nRes;
    nRes = WriteBytes(&wVal, sizeof(WORD));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteShort
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteShort(SHORT iVal)
{
    int nRes;
    nRes = WriteType(VT_I2);
    if (nRes) return nRes;
    nRes = WriteBytes(&iVal, sizeof(SHORT));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteLPSTR
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteLPSTR(LPSTR pStr)
{
    int nRes;
    nRes = WriteType(VT_LPSTR);
    if (nRes) return nRes;

    if (pStr)
    {
        DWORD dwLen = strlen(pStr);
        nRes = WriteBytes(&dwLen, sizeof(DWORD));
        if (nRes) return nRes;

        nRes = WriteBytes(pStr, strlen(pStr) + 1);
    }
    // Null pointers are encoded as 0xFFFFFFFF for the string length.
    // ==============================================================
    else 
    {
        DWORD dwNullEncoding = 0xFFFFFFFF;
        nRes = WriteBytes(&dwNullEncoding, sizeof(DWORD));
    }        
            
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteLPWSTR
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteLPWSTR(LPWSTR pStr)
{
    int nRes;
    nRes = WriteType(VT_LPWSTR);
    if (nRes) return nRes;
    
    // If a non-NULL pointer, the length prefixes the string data.
    // ============================================================
    if (pStr)
    {
        DWORD dwLen = wcslen(pStr);
        nRes = WriteBytes(&dwLen, sizeof(DWORD));
        if (nRes) return nRes;
        nRes = WriteBytes(pStr, (wcslen(pStr) + 1) * 2);
    }
    // Null pointers are encoded as 0xFFFFFFFF for the string length.
    // ==============================================================
    else 
    {
        DWORD dwNullEncoding = 0xFFFFFFFF;
        nRes = WriteBytes(&dwNullEncoding, sizeof(DWORD));
    }        

            
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteBSTR
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteBSTR(BSTR pStr)
{
    int nRes;
    nRes = WriteType(VT_BSTR);
    if (nRes) return nRes;
    if (pStr)
    {
        DWORD dwLen = wcslen(pStr);
        nRes = WriteBytes(&dwLen, sizeof(DWORD));
        if (nRes) return nRes;
        nRes = WriteBytes(pStr, (wcslen(pStr) + 1) * 2);
    }
    // NULL pointers are encoded as 0xFFFFFFFF for the length prefix.
    // ==============================================================
    else 
    {
        DWORD dwNullEncoding = 0xFFFFFFFF;
        nRes = WriteBytes(&dwNullEncoding, sizeof(DWORD));
    }        

    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteCLSID
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteCLSID(CLSID *pClsId)
{
    int nRes;
    nRes = WriteType(VT_CLSID);
    if (nRes) return nRes;
    return WriteBytes(pClsId, sizeof(CLSID));
}

//***************************************************************************
//
//  CMemStream::WriteCLSID
//
//  Serializes the literal value of a pointer.
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteUnknown(IUnknown *pObj)
{
    int nRes;
    nRes = WriteType(VT_UNKNOWN);
    if (nRes) return nRes;
    return WriteBytes(pObj, sizeof(void *));
}


//***************************************************************************
//
//  CMemStream::WriteFILETIME
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteFILETIME(FILETIME *pTime)
{
    int nRes;
    nRes = WriteType(VT_FILETIME);
    if (nRes) return nRes;
    nRes = WriteBytes(pTime, sizeof(FILETIME));
    return nRes;
}

//***************************************************************************
//
//  CMemStream::WriteCVar
//
//  Writes a CVar into the stream.
//
//  PARAMETERS:
//  pObj
//      Points to the CVar object to serialize.  The CVar can contains
//      an embedded CVarVector array which itself consists of arbitrary
//      CVar objects.
//
//  RETURN VALUES:
//  critical_error (unsupported type)
//  no_error
//  invalid_parameter
//  out_of_memory  (returned by embedded calls)
//
//***************************************************************************
// ok
int CMemStream::WriteCVar(CVar *pObj)
{
    if (!pObj)
        return invalid_parameter;

    // Write the CVar type indicator for the deserialization.
    // =======================================================

    int nRes = WriteType(VT_EX_CVAR);
    int nType = pObj->GetType();

    // Write out the field.
    // ====================

    switch (nType) {
        case VT_EMPTY:
        case VT_NULL: return WriteNull();

        case VT_I1:   return WriteChar(pObj->GetChar());
        case VT_UI1:  return WriteByte(pObj->GetByte());
        case VT_I2:   return WriteShort(pObj->GetShort());
        case VT_UI2:  return WriteWORD(pObj->GetWord());
        case VT_I4:   return WriteLong(pObj->GetLong());
        case VT_UI4:  return WriteDWORD(pObj->GetDWORD());
        case VT_BOOL: return WriteBool(pObj->GetBool());
        case VT_R4:   return WriteFloat(pObj->GetFloat());
        case VT_R8:   return WriteDouble(pObj->GetDouble());

        case VT_LPSTR:  return WriteLPSTR(pObj->GetLPSTR());
        case VT_LPWSTR: return WriteLPWSTR((LPWSTR) pObj->GetLPWSTR());

        case VT_BSTR:   return WriteBSTR((LPWSTR) pObj->GetLPWSTR());
            // Intentional type mismatch on pObj->GetLPWSTR
            // so we don't have to get a new BSTR and deallocate it.
            // CVar stores BSTR as LPWSTR internally, so this is ok.

        case VT_FILETIME:
            {
                FILETIME ft = pObj->GetFileTime();
                WriteFILETIME(&ft);
                return no_error;
            }

        case VT_BLOB:    return WriteBlob((BLOB *) pObj->GetBlob());
        case VT_CLSID:   return WriteCLSID((CLSID *) pObj->GetClsId());
        case VT_EX_CVARVECTOR: return WriteCVarVector((CVarVector *) pObj->GetVarVector());
    }

    return critical_error;
}

//***************************************************************************
//
//  CMemStream::WriteCVarVector
//
//  Write the incoming CVarVector to the stream.  For efficiency,
//  the type indicator is not repeated for each element.  However, each
//  element can be another embedded VT_EX_CVAR type.
//
//  PARAMETERS:
//  pVec
//      The pointer to the CVarVector object to serialize.
//  RETURN VALUE:
//  no_error, invalid_parameter, out_of_memory, critical_error
//
//***************************************************************************
// ok
int CMemStream::WriteCVarVector(IN CVarVector *pVec)
{
    if (!pVec)
        return invalid_parameter;

    // Write the CVarVector type indicator for the deserialization.
    // ============================================================

    if (WriteType(VT_EX_CVARVECTOR) != no_error)
        return out_of_memory;

    // Write the element type.
    // =======================

    int nElementType = pVec->GetType();

    if (WriteType(nElementType) != no_error)
        return out_of_memory;

    // Write out the vector length.
    // ============================

    DWORD dwSize = (DWORD) pVec->Size();
    int nRes = WriteBytes(&dwSize, sizeof(DWORD));

    // Write out the elements.
    // =======================

    for (int i = 0; i < pVec->Size(); i++) {
        CVar& v = pVec->GetAt(i);

        switch (pVec->GetType()) {
            case VT_EX_CVARVECTOR:
                if (WriteCVarVector((CVarVector *) v.GetVarVector()) != no_error)
                    return out_of_memory;
                break;

            case VT_EX_CVAR:
                if (WriteCVar(&v) != no_error)
                    return out_of_memory;
                break;

            case VT_I1:
                {
                    char c = v.GetChar();
                    if (WriteBytes(&c, sizeof(char)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_UI1:
                {
                    BYTE b = v.GetByte();
                    if (WriteBytes(&b, sizeof(BYTE)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_I2:
                {
                    SHORT i = v.GetShort();
                    if (WriteBytes(&i, sizeof(SHORT)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_UI2:
                {
                    WORD w = v.GetWord();
                    if (WriteBytes(&w, sizeof(WORD)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_I4:
                {
                    LONG l = v.GetLong();
                    if (WriteBytes(&l, sizeof(LONG)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_UI4:
                {
                    DWORD dw = v.GetDWORD();
                    if (WriteBytes(&dw, sizeof(DWORD)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_BOOL:
                {
                    VARIANT_BOOL b = v.GetBool();
                    if (WriteBytes(&b, sizeof(VARIANT_BOOL)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_R4:
                {
                    float f = v.GetFloat();
                    if (WriteBytes(&f, sizeof(float)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_R8:
                {
                    double d = v.GetDouble();
                    if (WriteBytes(&d, sizeof(double)) != no_error)
                        return out_of_memory;
                    break;
                }


            // NOTES: String types are written with a prefixed with
            // a DWORD length indicator so that during deserialization
            // the correct buffer length can be allocated before the
            // string is read back.  The length indicator is in characters,
            // not bytes.

            case VT_LPSTR:
                {
                    LPSTR pStr = v.GetLPSTR();
                    DWORD dwLength = strlen(pStr) + 1;
                    if (WriteBytes(&dwLength, sizeof(DWORD)) != no_error)
                        return out_of_memory;
                    if (WriteBytes(pStr, dwLength) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_LPWSTR:
                {
                    LPWSTR pStr = v.GetLPWSTR();
                    DWORD dwLength = wcslen(pStr) + 1;
                    if (WriteBytes(&dwLength, sizeof(DWORD)) != no_error)
                        return out_of_memory;
                    if (WriteBytes(pStr, dwLength * 2) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_BSTR:
                {
                    // Even though the type is BSTR, we request as
                    // an LPWSTR so as to avoid the lost time of calling
                    // SysAllocString/SysFreeString, etc.
                    LPWSTR pStr = v.GetLPWSTR();
                    DWORD dwLength = wcslen(pStr) + 1;
                    if (WriteBytes(&dwLength, sizeof(DWORD)) != no_error)
                        return out_of_memory;
                    if (WriteBytes(pStr, dwLength * 2) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_FILETIME:
                {
                    FILETIME ft = v.GetFileTime();
                    if (WriteBytes(&ft, sizeof(FILETIME)) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_BLOB:
                {
                    BLOB *p = v.GetBlob();
                    DWORD dwLen = BlobLength(p);
                    if (WriteBytes(&dwLen, sizeof(DWORD)) != no_error)
                        return out_of_memory;
                    if (WriteBytes(BlobDataPtr(p), dwLen) != no_error)
                        return out_of_memory;
                    break;
                }

            case VT_CLSID:
                {
                    CLSID *p = v.GetClsId();
                    if (WriteBytes(p, sizeof(CLSID)) != no_error)
                        return out_of_memory;
                    break;
                }

            // This should never execute.
            default:
                return critical_error;
        }
    }

    return no_error;
}


//***************************************************************************
//
//  CMemStream::ReadNull
//
//  Return values:
//      end_of_stream, type_mismatch, no_error
//
//***************************************************************************
// ok
int CMemStream::ReadNull()
{
    int nRes = ReadType();
    if (nRes == VT_EMPTY)
        return end_of_stream;
    else if (nRes != VT_NULL)
        return type_mismatch;
    return no_error;
}

//***************************************************************************
//
//  CMemStream::ReadBytes
//
//  Return value:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadBytes(LPVOID pBlock, DWORD dwLength)
{
    if (dwLength + m_dwCurrentPos > m_dwEndOfStream) {
        m_dwCurrentPos = m_dwEndOfStream;
        return end_of_stream;
    }

    memcpy(pBlock, &m_pBuffer[m_dwCurrentPos], dwLength);
    m_dwCurrentPos += dwLength;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::WriteBytes
//
//  Writes the specified bytes at the current offset. The
//  end-of-stream marker is unchanged, unless the current position
//  has been advanced beyond the previous end-of-stream marker by
//  the write.  In the latter case, the end-of-stream marker is
//  set to the byte immediately after the write.
//
//  Return values:
//      no_error, out_of_memory
//
//***************************************************************************
// ok
int CMemStream::WriteBytes(LPVOID pBlock, DWORD dwLength)
{
    while (m_dwCurrentPos + dwLength > m_dwSize) {
        m_dwSize += m_dwGrowBy;
        if (Resize(m_dwSize) != no_error)
            return out_of_memory;
    }

    memcpy(&m_pBuffer[m_dwCurrentPos], pBlock, dwLength);
    m_dwCurrentPos += dwLength;

    // Reset the end of stream pointer if we have grown 
    if (m_dwCurrentPos > m_dwEndOfStream)
        m_dwEndOfStream = m_dwCurrentPos;
    return no_error;
}

//***************************************************************************
//
//  Macro TYPE_CHECK
//
//  Checks that the next value in the stream is a type indicator which
/// matches the current type.
//
//  Returns end_of_stream or type_mismatch on errors.
//  On error, the current stream pointer is set back to where it was
//  on entry.  It is only allowed to advance on success.
//
//***************************************************************************

#define TYPE_CHECK(vt)          \
    {                           \
    Push();                     \
    int nType = ReadType();     \
    if (nType == VT_EMPTY) {    \
        Pop(FALSE);             \
        return end_of_stream;   \
    }                           \
    if (nType != vt)          { \
        Pop(FALSE);             \
        return type_mismatch;   \
    }                           \
    Pop(TRUE);                  \
    }


//***************************************************************************
//
//  CMemStream::ReadBlob
//
//  Reads a BLOB and dynamically allocates buffer to hold it. The caller
//  must call FreeMem to free the memory block.
//
//  Parameters:
//      pBytes
//          A pointer to the user's pointer, which should point to NULL
//          on entry. This will be assigned to point to the new block.
//      pdwSize
//          Points to a DWORD which will be assigned to the size of the
//          returned block.
//
//  Return values:
//      end_of_stream,  type_mismatch, no_error
//
//***************************************************************************
// ok
int CMemStream::ReadBlob(BLOB *pBlob)
{
    TYPE_CHECK(VT_BLOB);

    DWORD dwSize = 0;
    int nRes = ReadBytes(&dwSize, sizeof(DWORD));
    if (nRes != no_error)
        return nRes;

    LPVOID pBlock = _new BYTE[dwSize];
    if (pBlock == NULL)
        return out_of_memory;

    nRes = ReadBytes(pBlock, dwSize);
    if (nRes != no_error)
        return end_of_stream;

    pBlob->cbSize = dwSize;
    pBlob->pBlobData = (BYTE *) pBlock;

    return no_error;
}


//***************************************************************************
//
//  CMemStream::ReadDouble
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadDouble(double *pdblVal)
{
    TYPE_CHECK(VT_R8);
    return ReadBytes(pdblVal, sizeof(double));
}

//***************************************************************************
//
//  CMemStream::ReadByte
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadByte(BYTE *pByte)
{
    TYPE_CHECK(VT_UI1);
    return ReadBytes(pByte, sizeof(BYTE));
}

//***************************************************************************
//
//  CMemStream::ReadFloat
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadFloat(float *pfltVal)
{
    TYPE_CHECK(VT_R4);
    return ReadBytes(pfltVal, sizeof(float));
}

//***************************************************************************
//
//  CMemStream::ReadFloat
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadBool(VARIANT_BOOL *pBool)
{
    TYPE_CHECK(VT_BOOL);
    return ReadBytes(pBool, sizeof(VARIANT_BOOL));
}

//***************************************************************************
//
//  CMemStream::ReadWORD
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadWORD(WORD *pw)
{
    TYPE_CHECK(VT_UI2);
    return ReadBytes(pw, sizeof(WORD));
}

//***************************************************************************
//
//  CMemStream::ReadChar
//
//  Return values:
//      no_error, end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadChar(char *pc)
{
    TYPE_CHECK(VT_I1);
    return ReadBytes(pc, sizeof(char));
}


//***************************************************************************
//
//  CMemStream::ReadLong
//
//***************************************************************************
// ok
int CMemStream::ReadLong(LONG *plVal)
{
    TYPE_CHECK(VT_I4);
    return ReadBytes(plVal, sizeof(LONG));
}

//***************************************************************************
//
//  CMemStream::ReadDWORD
//
//***************************************************************************
// ok
int CMemStream::ReadDWORD(DWORD *pdwVal)
{
    TYPE_CHECK(VT_UI4);
    return ReadBytes(pdwVal, sizeof(DWORD));
}

//***************************************************************************
//
//  CMemStream::ReadShort
//
//***************************************************************************
// ok
int CMemStream::ReadShort(SHORT *piVal)
{
    TYPE_CHECK(VT_I2);
    return ReadBytes(piVal, sizeof(SHORT));
}

//***************************************************************************
//
//  CMemStream::ReadLPSTR
//
//***************************************************************************
// ok
int CMemStream::ReadLPSTR(LPSTR *pStr)
{
    TYPE_CHECK(VT_LPSTR);
    DWORD dwLength = 0;
    int nRes = ReadBytes(&dwLength, sizeof(DWORD));
    if (nRes)
        return nRes;

    // Check for encoded NULL pointer.
    // ===============================        
    if (dwLength == 0xFFFFFFFF)
    {
        *pStr = 0;
        return no_error;
    }

    // If here, there is at least a string of some kind,
    // possibly zero length.
    // ==================================================
                    
    *pStr = _new char[dwLength + 1];
    nRes = ReadBytes(*pStr, dwLength + 1);  // Include read of NULL
    if (nRes != no_error)
        return nRes;
    return no_error;
}

//***************************************************************************
//
//  CMemStream::ReadLPWSTR
//
//***************************************************************************
// ok
int CMemStream::ReadLPWSTR(LPWSTR *pStr)
{
    TYPE_CHECK(VT_LPWSTR);

    DWORD dwLength = 0;
    int nRes = ReadBytes(&dwLength, sizeof(DWORD));
    if (nRes)
        return nRes;

    // Check for encoded NULL pointer.
    // ===============================        
    if (dwLength == 0xFFFFFFFF)
    {
        *pStr = 0;
        return no_error;
    }

    // If here, there is at least a string of some kind,
    // possibly zero length.
    // ==================================================
    
    *pStr = _new wchar_t[dwLength + 1];
    nRes = ReadBytes(*pStr, (dwLength + 1) * 2);
    if (nRes != no_error)
        return nRes;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::ReadBSTR
//
//***************************************************************************
// ok
int CMemStream::ReadBSTR(BSTR *pStr)
{
    *pStr = 0;

    TYPE_CHECK(VT_BSTR);

    DWORD dwLength = 0;
    int nRes = ReadBytes(&dwLength, sizeof(DWORD));
    if (nRes)
        return nRes;

    // Check for encoded NULL pointer.
    // ===============================        
    if (dwLength == 0xFFFFFFFF)
    {
        *pStr = 0;
        return no_error;
    }

    // If here, there is at least a string of some kind,
    // possibly zero length.
    // ==================================================

    wchar_t* pTemp = _new wchar_t[dwLength + 1];
    nRes = ReadBytes(pTemp, (dwLength + 1) * 2);
    if (nRes != no_error)
        return nRes;

    *pStr = SysAllocString(pTemp);
    delete pTemp;

    return no_error;
}

//***************************************************************************
//
//  CMemStream::ReadCLSID
//
//***************************************************************************
// ok
int CMemStream::ReadCLSID(CLSID *pClsId)
{
    TYPE_CHECK(VT_CLSID);
    return ReadBytes(pClsId, sizeof(CLSID));
}


//***************************************************************************
//
//  CMemStream::ReadUnknown
//
//***************************************************************************
// ok
int CMemStream::ReadUnknown(IUnknown **pObj)
{
    TYPE_CHECK(VT_UNKNOWN);
    return ReadBytes(*pObj, sizeof(LPVOID));
}

//***************************************************************************
//
//  CMemStream::ReadFILETIME
//
//  Reads a Win32 FILETIME struct.
//
//***************************************************************************
// ok
int CMemStream::ReadFILETIME(OUT FILETIME *pTime)
{
    TYPE_CHECK(VT_FILETIME);
    return ReadBytes(pTime, sizeof(FILETIME));
}


//***************************************************************************
//
//  CMemStream::Read
//
//  Reads a CVar from the stream.
//
//  RETURN VALUES:
//  no_error
//  end_of_stream
//
//***************************************************************************
// ok
int CMemStream::ReadCVar(OUT CVar **pObj)
{
    TYPE_CHECK(VT_EX_CVAR);

    // Now read the internal type of the CVar. We read ahead, and then
    // move the current pointer back so that subsequent calls to
    // deserialize the typed value will work properly (they all need
    // the type prefixes).
    // ================================================================

    CVar *pVar = _new CVar;

    DWORD dwPos = GetCurrentPos();
    int nVarType = ReadType();
    int nErrorCode = no_error;
    SetCurrentPos(dwPos);

    switch (nVarType) {
        case VT_EMPTY:
        case VT_NULL:
            pVar->SetAsNull();
            break;

        case VT_I1:
            {
                char c = 0;
                if (ReadChar(&c) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetChar(c);
            }
            break;

        case VT_UI1:
            {
                BYTE b = 0;
                if (ReadByte(&b) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetByte(b);
            }
            break;

        case VT_I2:
            {
                SHORT i = 0;
                if (ReadShort(&i) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetShort(i);
            }
            break;

        case VT_UI2:
            {
                WORD w = 0;
                if (ReadWORD(&w) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetWord(w);
            }
            break;

        case VT_I4:
            {
                LONG l = 0;
                if (ReadLong(&l) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetLong(l);
            }
            break;

        case VT_UI4:
            {
                DWORD dw = 0;
                if (ReadDWORD(&dw) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetDWORD(dw);
            }
            break;

        case VT_BOOL:
            {
                VARIANT_BOOL b = 0;
                if (ReadBool(&b) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetBool(b);
            }
            break;

        case VT_R4:
            {
                float f = (float) 0.0;
                if (ReadFloat(&f) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetFloat(f);
            }
            break;

        case VT_R8:
            {
                double d = 0.0;
                if (ReadDouble(&d) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetDouble(d);
            }
            break;

        case VT_LPSTR:
            {
                LPSTR pStr = 0;
                if (ReadLPSTR(&pStr) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetLPSTR(pStr, TRUE);
            }
            break;

        case VT_LPWSTR:
            {
                LPWSTR pStr = 0;
                if (ReadLPWSTR(&pStr) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetLPWSTR(pStr, TRUE);
            }
            break;


        case VT_BSTR:
            {
                BSTR Str = 0;
                if (ReadBSTR(&Str) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else {
                    pVar->SetBSTR(Str, FALSE);
                    SysFreeString(Str);
                }
            }
            break;

        case VT_FILETIME:
            {
                FILETIME f = {0};
                if (ReadFILETIME(&f) != no_error) {
                    nErrorCode = end_of_stream;
                }
                else
                    pVar->SetFileTime(&f);
            }
            break;

        case VT_BLOB:
            {
                BLOB b;
                BlobInit(&b);
                if (ReadBlob(&b) != no_error)
                    nErrorCode = end_of_stream;
                else
                    pVar->SetBlob(&b, TRUE);
            }
            break;

        case VT_CLSID:
            {
                CLSID ClsId = {0};
                if (ReadCLSID(&ClsId) != no_error)
                    nErrorCode = end_of_stream;
                else
                    pVar->SetClsId(&ClsId, FALSE);
            }
            break;

        case VT_EX_CVARVECTOR:
            {
                CVarVector *pVec = 0;
                if (ReadCVarVector(&pVec) != no_error)
                    nErrorCode = end_of_stream;
                else
                    pVar->SetVarVector(pVec, TRUE);
            }
            break;
    }

    if (nErrorCode != no_error)
        delete pVar;
    else
        *pObj = pVar;

    return nErrorCode;
}

//***************************************************************************
//
//  CMemStream::ReadCVarVector
//
//
//***************************************************************************
// ok
int CMemStream::ReadCVarVector(CVarVector **pObj)
{
    *pObj = 0;

    TYPE_CHECK(VT_EX_CVARVECTOR);

    // Read the element type.
    // ======================

    DWORD dwPos = GetCurrentPos();
    int nType = ReadType();
    CVarVector *pVec = _new CVarVector(nType);

    // Read the size of the vector.
    // ============================
    DWORD dwVecSize = 0;
    if (ReadBytes(&dwVecSize, sizeof(DWORD)) != no_error)
        return end_of_stream;

    // Read each element.
    // ==================

    for (DWORD dwIx = 0; dwIx < dwVecSize; dwIx++) {
        switch (nType) {
            case VT_EX_CVARVECTOR:
                {
                    CVarVector *pTmpVec = 0;
                    if (ReadCVarVector(&pTmpVec) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(pTmpVec, TRUE));
                    break;
                }

            case VT_EX_CVAR:
                {
                    CVar *pVar = 0;
                    if (ReadCVar(&pVar) != no_error)
                        return end_of_stream;
                    pVec->Add(pVar);
                    break;
                }

            case VT_I1:
                {
                    char c = 0;
                    if (ReadBytes(&c, sizeof(char)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(c));
                    break;
                }

            case VT_UI1:
                {
                    BYTE b = 0;
                    if (ReadBytes(&b, sizeof(BYTE)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(b));
                    break;
                }

            case VT_I2:
                {
                    SHORT i = 0;
                    if (ReadBytes(&i, sizeof(SHORT)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(i));
                    break;
                }

            case VT_UI2:
                {
                    WORD w = 0;
                    if (ReadBytes(&w, sizeof(WORD)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(w));
                    break;
                }

            case VT_I4:
                {
                    LONG l = 0;
                    if (ReadBytes(&l, sizeof(LONG)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(l));
                    break;
                }

            case VT_UI4:
                {
                    DWORD dw = 0;
                    if (ReadBytes(&dw, sizeof(DWORD)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(dw));
                    break;
                }

            case VT_BOOL:
                {
                    VARIANT_BOOL b = 0;
                    if (ReadBytes(&b, sizeof(VARIANT_BOOL)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(b));
                    break;
                }

            case VT_R4:
                {
                    float f = (float) 0.0;
                    if (ReadBytes(&f, sizeof(float)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(f));
                    break;
                }

            case VT_R8:
                {
                    double d = 0.0;
                    if (ReadBytes(&d, sizeof(double)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(d));
                    break;
                }


            // NOTE: String types were written with a prefixed with
            // a DWORD length indicator so that during deserialization
            // the correct buffer length can be allocated before the
            // string is read back.  The length indicator is in characters,
            // not bytes.
            // ============================================================
            case VT_LPSTR:
                {
                    DWORD dwLen = 0;
                    if (ReadBytes(&dwLen, sizeof(DWORD)) != no_error)
                        return end_of_stream;
                    LPSTR pStr = _new char[dwLen];
                    if (ReadBytes(pStr, dwLen) != no_error)
                        return out_of_memory;
                    pVec->Add(CVar(pStr, TRUE));
                    break;
                }

            case VT_LPWSTR:
                {
                    DWORD dwLen = 0;
                    if (ReadBytes(&dwLen, sizeof(DWORD)) != no_error)
                        return end_of_stream;
                    LPWSTR pStr = _new wchar_t[dwLen];
                    if (ReadBytes(pStr, dwLen * 2) != no_error)
                        return out_of_memory;
                    pVec->Add(CVar(pStr, TRUE));
                    break;
                }

            case VT_BSTR:
                {
                    DWORD dwLen = 0;
                    if (ReadBytes(&dwLen, sizeof(DWORD)) != no_error)
                        return end_of_stream;
                    LPWSTR pStr = _new wchar_t[dwLen];
                    if (ReadBytes(pStr, dwLen * 2) != no_error)
                        return out_of_memory;
                    pVec->Add(CVar(VT_BSTR, pStr, FALSE));
                    delete pStr;
                    break;
                }

            case VT_FILETIME:
                {
                    FILETIME ft = {0};
                    if (ReadBytes(&ft, sizeof(FILETIME)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(&ft));
                    break;
                }

            case VT_BLOB:
                {
                    BLOB b;
                    BlobInit(&b);
                    DWORD dwLen = 0;
                    if (ReadBytes(&dwLen, sizeof(DWORD)) != no_error)
                        return end_of_stream;
                    LPBYTE pBuf = _new BYTE[dwLen];
                    if (ReadBytes(pBuf, dwLen) != no_error)
                        return end_of_stream;
                    BlobAssign(&b, pBuf, dwLen, TRUE);
                    pVec->Add(CVar(&b, TRUE));
                }

            case VT_CLSID:
                {
                    CLSID clsid = {0};
                    if (ReadBytes(&clsid, sizeof(CLSID)) != no_error)
                        return end_of_stream;
                    pVec->Add(CVar(&clsid));
                    break;
                }

            // This should never execute.
            default:
                return critical_error;
        }
    }

    *pObj = pVec;
    return no_error;
}

//***************************************************************************
//
//   CMemStream::ReadError
//
//***************************************************************************
// ok
int CMemStream::ReadError(SCODE *pVal)
{
    TYPE_CHECK(VT_ERROR);
    return ReadBytes(pVal, sizeof(SCODE));
}

//***************************************************************************
//
//  CMemStream::NextType
//
//***************************************************************************
// ok
int CMemStream::NextType()
{
    Push();
    int nType = ReadType();
    Pop(FALSE);
    return nType;
}

//***************************************************************************
//
//  CMemStream::ReadType
//
//  Returns a VT_ type indicator or VT_EMPTY on end-of-stream.
//
//***************************************************************************
// ok
int CMemStream::ReadType()
{
    DWORD dwType = VT_EMPTY;

    if (ReadBytes(&dwType, sizeof(DWORD)) == end_of_stream)
        return VT_EMPTY;

    return dwType;
}


//***************************************************************************
//
//  CMemStream::Pop
//
//***************************************************************************
// ok
void CMemStream::Pop(BOOL bDiscard)
{
    if (bDiscard)
        m_nStackPtr--;
    else
        m_dwCurrentPos = m_dwStack[m_nStackPtr--];
}


//***************************************************************************
//
//  CMemStream::~CMemStream
//
//***************************************************************************
// ok
CMemStream::~CMemStream()
{
    //_ASSERT(m_lRef == 0, "CMemStream used for COM deleted without Release");
    if (m_nFlags == auto_delete)
        m_pArena->Free(m_pBuffer);
    m_pArena->Release();
}

//***************************************************************************
//
//  CMemStream::IStream implementation
//
//***************************************************************************

STDMETHODIMP CMemStream::QueryInterface(REFIID riid, void** ppv)
{
    if(riid == IID_IUnknown || riid == IID_IStream)
    {
        *ppv = (void*)(IStream*)this;
        AddRef();
        return S_OK;
    }
    else return E_NOINTERFACE;
}

STDMETHODIMP CMemStream::Read(
     void *pv,
     ULONG cb,
     ULONG *pcbRead)
{
    if(ReadBytes(pv, cb) == no_error)
    {
        if(pcbRead) *pcbRead = cb;
        return S_OK;
    }
    else 
    {
        if(pcbRead) *pcbRead = 0;
        return S_FALSE;
    }
}
    
STDMETHODIMP CMemStream::Write(
     const void *pv,
     ULONG cb,
     ULONG *pcbWritten)
{
    if(WriteBytes((void*)pv, cb) == no_error)
    {
        if(pcbWritten) *pcbWritten = cb;
        return S_OK;
    }
    else
    {
        if(pcbWritten) *pcbWritten = 0;
        return S_FALSE;
    }
}

STDMETHODIMP CMemStream::Seek(
     LARGE_INTEGER dlibMove,
     DWORD dwOrigin,
     ULARGE_INTEGER *plibNewPosition)
{
    switch(dwOrigin)
    {
    case STREAM_SEEK_SET:
        SetCurrentPos(dlibMove.LowPart);
        break;
    case STREAM_SEEK_CUR:
        SetCurrentPos(GetCurrentPos() + (long)dlibMove.QuadPart);
        break;
    case STREAM_SEEK_END:
        SetCurrentPos(Size() + (long)dlibMove.QuadPart);
        break;
    }

    if(plibNewPosition)
    {
        plibNewPosition->QuadPart = (LONGLONG)GetCurrentPos();
    }
    return S_OK;
}

STDMETHODIMP CMemStream::SetSize(
     ULARGE_INTEGER libNewSize)
{
    return S_OK;
}

STDMETHODIMP CMemStream::CopyTo(
     IStream *pstm,
     ULARGE_INTEGER cb,
     ULARGE_INTEGER *pcbRead,
     ULARGE_INTEGER *pcbWritten)
{
    _ASSERT(0, __TEXT("CopyTo is called on CMemStream!"));
    return STG_E_INVALIDFUNCTION;
}

STDMETHODIMP CMemStream::Commit(
     DWORD grfCommitFlags)
{
    return S_OK;
}

STDMETHODIMP CMemStream::Revert()
{
    return S_OK;
}


STDMETHODIMP CMemStream::LockRegion(
     ULARGE_INTEGER libOffset,
     ULARGE_INTEGER cb,
     DWORD dwLockType)
{
    return S_OK;
}

STDMETHODIMP CMemStream::UnlockRegion(
     ULARGE_INTEGER libOffset,
     ULARGE_INTEGER cb,
     DWORD dwLockType)
{
    return STG_E_INVALIDFUNCTION;
}

STDMETHODIMP CMemStream::Stat(
     STATSTG *pstatstg,
     DWORD grfStatFlag)
{
    pstatstg->pwcsName = NULL;
    pstatstg->type = STGTY_STREAM;
    pstatstg->cbSize.QuadPart = (LONGLONG)Size();
    
    return S_OK;
}
    
    

STDMETHODIMP CMemStream::Clone(
     IStream **ppstm)
{
    *ppstm = new CMemStream(*this);
    (*ppstm)->AddRef();
    return S_OK;
}
