mirror of https://github.com/lianthony/NT4.0
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.
373 lines
10 KiB
373 lines
10 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Foundation Classes Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft Foundation Classes product.
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef AFX_CORE2_SEG
|
|
#pragma code_seg(AFX_CORE2_SEG)
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Archive support for polymorphic reading/writing of CObjects
|
|
|
|
// Note: Starting with MFC 4.0, the file format written/read has been
|
|
// extended to eliminate the previous 32k limit. Files previously written
|
|
// can still be read by old versions (even 16-bit versions). In addition,
|
|
// new files, unless they are large enough to take advantage of 32-bit tags,
|
|
// can be read by old versions.
|
|
|
|
// Pointer mapping constants
|
|
#define wNullTag ((WORD)0) // special tag indicating NULL ptrs
|
|
#define wNewClassTag ((WORD)0xFFFF) // special tag indicating new CRuntimeClass
|
|
#define wClassTag ((WORD)0x8000) // 0x8000 indicates class tag (OR'd)
|
|
#define dwBigClassTag ((DWORD)0x80000000) // 0x8000000 indicates big class tag (OR'd)
|
|
#define wBigObjectTag ((WORD)0x7FFF) // 0x7FFF indicates DWORD object tag
|
|
#define nMaxMapCount ((DWORD)0x3FFFFFFE) // 0x3FFFFFFE last valid mapCount
|
|
|
|
// Note: tag value 0x8000 could be used for something in the future, since
|
|
// it is currently an invalid tag (0x8000 means zero wClassTag, but zero
|
|
// index is always reserved for a NULL pointer, and a NULL runtime class
|
|
// does not make any sense).
|
|
|
|
// This is how the tags have been allocated currently:
|
|
//
|
|
// 0x0000 represents NULL pointer
|
|
// 0x0001 - 0x7FFE "small" object tags
|
|
// 0x7FFF header for "big" object/class tag
|
|
// 0x8000 reserved for future use
|
|
// 0x8001 - 0xFFFE "small" class tag
|
|
// 0xFFFF header for class definition
|
|
//
|
|
// The special value of 0x7FFF indicates that a DWORD tag follows. This
|
|
// two part "big" tag is used for 32-bit tag values 0x7FFF and above.
|
|
// The tag value of 0x7FFF was unused in MFC versions prior to MFC 4.0.
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CArchive::CheckCount()
|
|
{
|
|
if (m_nMapCount >= nMaxMapCount)
|
|
AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
|
|
}
|
|
|
|
void CArchive::WriteObject(const CObject* pOb)
|
|
{
|
|
// object can be NULL
|
|
ASSERT(IsStoring()); // proper direction
|
|
|
|
DWORD nObIndex;
|
|
ASSERT(sizeof(nObIndex) == 4);
|
|
ASSERT(sizeof(wNullTag) == 2);
|
|
ASSERT(sizeof(wBigObjectTag) == 2);
|
|
ASSERT(sizeof(wNewClassTag) == 2);
|
|
|
|
// make sure m_pStoreMap is initialized
|
|
MapObject(NULL);
|
|
|
|
if (pOb == NULL)
|
|
{
|
|
// save out null tag to represent NULL pointer
|
|
*this << wNullTag;
|
|
}
|
|
else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)
|
|
// assumes initialized to 0 map
|
|
{
|
|
// save out index of already stored object
|
|
if (nObIndex < wBigObjectTag)
|
|
*this << (WORD)nObIndex;
|
|
else
|
|
{
|
|
*this << wBigObjectTag;
|
|
*this << nObIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// write class of object first
|
|
CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
|
|
WriteClass(pClassRef);
|
|
|
|
// enter in stored object table, checking for overflow
|
|
CheckCount();
|
|
(*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;
|
|
|
|
// cause the object to serialize itself
|
|
((CObject*)pOb)->Serialize(*this);
|
|
}
|
|
}
|
|
|
|
CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
|
|
{
|
|
ASSERT(pClassRefRequested == NULL ||
|
|
AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
|
|
ASSERT(IsLoading()); // proper direction
|
|
ASSERT(wNullTag == 0);
|
|
ASSERT((wClassTag << 16) == dwBigClassTag);
|
|
ASSERT((wNewClassTag & wClassTag) == wClassTag);
|
|
|
|
// attempt to load next stream as CRuntimeClass
|
|
UINT nSchema;
|
|
DWORD obTag;
|
|
CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);
|
|
|
|
// check to see if tag to already loaded object
|
|
CObject* pOb;
|
|
if (pClassRef == NULL)
|
|
{
|
|
if (obTag > (DWORD)m_pLoadArray->GetUpperBound())
|
|
{
|
|
// tag is too large for the number of objects read so far
|
|
AfxThrowArchiveException(CArchiveException::badIndex,
|
|
m_strFileName);
|
|
}
|
|
|
|
pOb = (CObject*)m_pLoadArray->GetAt(obTag);
|
|
if (pOb != NULL && pClassRefRequested != NULL &&
|
|
!pOb->IsKindOf(pClassRefRequested))
|
|
{
|
|
// loaded an object but of the wrong class
|
|
AfxThrowArchiveException(CArchiveException::badClass,
|
|
m_strFileName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// allocate a new object based on the class just acquired
|
|
pOb = pClassRef->CreateObject();
|
|
if (pOb == NULL)
|
|
AfxThrowMemoryException();
|
|
|
|
// Add to mapping array BEFORE de-serializing
|
|
CheckCount();
|
|
m_pLoadArray->InsertAt(m_nMapCount++, pOb);
|
|
|
|
// Serialize the object with the schema number set in the archive
|
|
UINT nSchemaSave = m_nObjectSchema;
|
|
m_nObjectSchema = nSchema;
|
|
pOb->Serialize(*this);
|
|
m_nObjectSchema = nSchemaSave;
|
|
ASSERT_VALID(pOb);
|
|
}
|
|
|
|
return pOb;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// advanced versioning and back-pointer support
|
|
|
|
UINT CArchive::GetObjectSchema()
|
|
{
|
|
UINT nResult = m_nObjectSchema;
|
|
m_nObjectSchema = (UINT)-1; // can only be called once per Serialize
|
|
return nResult;
|
|
}
|
|
|
|
void CArchive::MapObject(const CObject* pOb)
|
|
{
|
|
if (IsStoring())
|
|
{
|
|
if (m_pStoreMap == NULL)
|
|
{
|
|
// initialize the storage map
|
|
// (use CMapPtrToPtr because it is used for HANDLE maps too)
|
|
m_pStoreMap = new CMapPtrToPtr(m_nGrowSize);
|
|
m_pStoreMap->InitHashTable(m_nHashSize);
|
|
m_pStoreMap->SetAt(NULL, (void*)(DWORD)wNullTag);
|
|
m_nMapCount = 1;
|
|
}
|
|
if (pOb != NULL)
|
|
{
|
|
CheckCount();
|
|
(*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_pLoadArray == NULL)
|
|
{
|
|
// initialize the loaded object pointer array and set special values
|
|
m_pLoadArray = new CPtrArray;
|
|
m_pLoadArray->SetSize(1, m_nGrowSize);
|
|
ASSERT(wNullTag == 0);
|
|
m_pLoadArray->SetAt(wNullTag, NULL);
|
|
m_nMapCount = 1;
|
|
}
|
|
if (pOb != NULL)
|
|
{
|
|
CheckCount();
|
|
m_pLoadArray->InsertAt(m_nMapCount++, (void*)pOb);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CArchive::WriteClass(const CRuntimeClass* pClassRef)
|
|
{
|
|
ASSERT(pClassRef != NULL);
|
|
ASSERT(IsStoring()); // proper direction
|
|
|
|
if (pClassRef->m_wSchema == 0xFFFF)
|
|
{
|
|
TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.\n",
|
|
pClassRef->m_lpszClassName);
|
|
AfxThrowNotSupportedException();
|
|
}
|
|
|
|
// make sure m_pStoreMap is initialized
|
|
MapObject(NULL);
|
|
|
|
// write out class id of pOb, with high bit set to indicate
|
|
// new object follows
|
|
|
|
// ASSUME: initialized to 0 map
|
|
DWORD nClassIndex;
|
|
if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)
|
|
{
|
|
// previously seen class, write out the index tagged by high bit
|
|
if (nClassIndex < wBigObjectTag)
|
|
*this << (WORD)(wClassTag | nClassIndex);
|
|
else
|
|
{
|
|
*this << wBigObjectTag;
|
|
*this << (dwBigClassTag | nClassIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// store new class
|
|
*this << wNewClassTag;
|
|
pClassRef->Store(*this);
|
|
|
|
// store new class reference in map, checking for overflow
|
|
CheckCount();
|
|
(*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;
|
|
}
|
|
}
|
|
|
|
CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested,
|
|
UINT* pSchema, DWORD* pObTag)
|
|
{
|
|
ASSERT(pClassRefRequested == NULL ||
|
|
AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
|
|
ASSERT(IsLoading()); // proper direction
|
|
|
|
if (pClassRefRequested != NULL && pClassRefRequested->m_wSchema == 0xFFFF)
|
|
{
|
|
TRACE1("Warning: Cannot call ReadClass/ReadObject for %hs.\n",
|
|
pClassRefRequested->m_lpszClassName);
|
|
AfxThrowNotSupportedException();
|
|
}
|
|
|
|
// make sure m_pLoadArray is initialized
|
|
MapObject(NULL);
|
|
|
|
// read object tag - if prefixed by wBigObjectTag then DWORD tag follows
|
|
DWORD obTag;
|
|
WORD wTag;
|
|
*this >> wTag;
|
|
if (wTag == wBigObjectTag)
|
|
*this >> obTag;
|
|
else
|
|
obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);
|
|
|
|
// check for object tag (throw exception if expecting class tag)
|
|
if (!(obTag & dwBigClassTag))
|
|
{
|
|
if (pObTag == NULL)
|
|
AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
|
|
|
|
*pObTag = obTag;
|
|
return NULL;
|
|
}
|
|
|
|
CRuntimeClass* pClassRef;
|
|
UINT nSchema;
|
|
if (wTag == wNewClassTag)
|
|
{
|
|
// new object follows a new class id
|
|
if ((pClassRef = CRuntimeClass::Load(*this, &nSchema)) == NULL)
|
|
AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
|
|
|
|
// check nSchema against the expected schema
|
|
if ((pClassRef->m_wSchema & ~VERSIONABLE_SCHEMA) != nSchema)
|
|
{
|
|
if (!(pClassRef->m_wSchema & VERSIONABLE_SCHEMA))
|
|
{
|
|
// schema doesn't match and not marked as VERSIONABLE_SCHEMA
|
|
AfxThrowArchiveException(CArchiveException::badSchema,
|
|
m_strFileName);
|
|
}
|
|
else
|
|
{
|
|
// they differ -- store the schema for later retrieval
|
|
if (m_pSchemaMap == NULL)
|
|
m_pSchemaMap = new CMapPtrToPtr;
|
|
ASSERT_VALID(m_pSchemaMap);
|
|
m_pSchemaMap->SetAt(pClassRef, (void*)nSchema);
|
|
}
|
|
}
|
|
CheckCount();
|
|
m_pLoadArray->InsertAt(m_nMapCount++, pClassRef);
|
|
}
|
|
else
|
|
{
|
|
// existing class index in obTag followed by new object
|
|
DWORD nClassIndex = (obTag & ~dwBigClassTag);
|
|
if (nClassIndex == 0 || nClassIndex > (DWORD)m_pLoadArray->GetUpperBound())
|
|
AfxThrowArchiveException(CArchiveException::badIndex,
|
|
m_strFileName);
|
|
|
|
pClassRef = (CRuntimeClass*)m_pLoadArray->GetAt(nClassIndex);
|
|
ASSERT(pClassRef != NULL);
|
|
|
|
// determine schema stored against objects of this type
|
|
void* pTemp;
|
|
if (m_pSchemaMap != NULL && (pTemp = m_pSchemaMap->GetValueAt(pClassRef)) != NULL)
|
|
nSchema = (UINT)pTemp;
|
|
else
|
|
nSchema = pClassRef->m_wSchema & ~VERSIONABLE_SCHEMA;
|
|
}
|
|
|
|
// check for correct derivation
|
|
if (pClassRefRequested != NULL &&
|
|
!pClassRef->IsDerivedFrom(pClassRefRequested))
|
|
{
|
|
AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
|
|
}
|
|
|
|
// store nSchema for later examination
|
|
if (pSchema != NULL)
|
|
*pSchema = nSchema;
|
|
else
|
|
m_nObjectSchema = nSchema;
|
|
|
|
// store obTag for later examination
|
|
if (pObTag != NULL)
|
|
*pObTag = obTag;
|
|
|
|
// return the resulting CRuntimeClass*
|
|
return pClassRef;
|
|
}
|
|
|
|
void CArchive::SerializeClass(const CRuntimeClass* pClassRef)
|
|
{
|
|
if (IsStoring())
|
|
WriteClass(pClassRef);
|
|
else
|
|
ReadClass(pClassRef);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|