2216 lines
57 KiB
C++
2216 lines
57 KiB
C++
/*++
|
|
|
|
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;
|
|
}
|