Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2215 lines
57 KiB

/*++
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;
}