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