home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / arcobj.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-16  |  10.8 KB  |  380 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CORE2_SEG
  14. #pragma code_seg(AFX_CORE2_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. ////////////////////////////////////////////////////////////////////////////
  25. // Archive support for polymorphic reading/writing of CObjects
  26.  
  27. // Note: Starting with MFC 4.0, the file format written/read has been
  28. //  extended to eliminate the previous 32k limit.  Files previously written
  29. //  can still be read by old versions (even 16-bit versions).  In addition,
  30. //  new files, unless they are large enough to take advantage of 32-bit tags,
  31. //  can be read by old versions.
  32.  
  33. // Pointer mapping constants
  34. #define wNullTag        ((WORD)0)           // special tag indicating NULL ptrs
  35. #define wNewClassTag    ((WORD)0xFFFF)      // special tag indicating new CRuntimeClass
  36. #define wClassTag       ((WORD)0x8000)      // 0x8000 indicates class tag (OR'd)
  37. #define dwBigClassTag   ((DWORD)0x80000000) // 0x8000000 indicates big class tag (OR'd)
  38. #define wBigObjectTag   ((WORD)0x7FFF)      // 0x7FFF indicates DWORD object tag
  39. #define nMaxMapCount    ((DWORD)0x3FFFFFFE) // 0x3FFFFFFE last valid mapCount
  40.  
  41. // Note: tag value 0x8000 could be used for something in the future, since
  42. //  it is currently an invalid tag (0x8000 means zero wClassTag, but zero
  43. //  index is always reserved for a NULL pointer, and a NULL runtime class
  44. //  does not make any sense).
  45.  
  46. // This is how the tags have been allocated currently:
  47. //
  48. //  0x0000              represents NULL pointer
  49. //  0x0001 - 0x7FFE     "small" object tags
  50. //  0x7FFF              header for "big" object/class tag
  51. //  0x8000              reserved for future use
  52. //  0x8001 - 0xFFFE     "small" class tag
  53. //  0xFFFF              header for class definition
  54. //
  55. // The special value of 0x7FFF indicates that a DWORD tag follows.  This
  56. // two part "big" tag is used for 32-bit tag values 0x7FFF and above.
  57. // The tag value of 0x7FFF was unused in MFC versions prior to MFC 4.0.
  58.  
  59. ////////////////////////////////////////////////////////////////////////////
  60.  
  61. void CArchive::CheckCount()
  62. {
  63.     if (m_nMapCount >= nMaxMapCount)
  64.         AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
  65. }
  66.  
  67. void CArchive::WriteObject(const CObject* pOb)
  68. {
  69.     // object can be NULL
  70.     ASSERT(IsStoring());    // proper direction
  71.  
  72.     DWORD nObIndex;
  73.     ASSERT(sizeof(nObIndex) == 4);
  74.     ASSERT(sizeof(wNullTag) == 2);
  75.     ASSERT(sizeof(wBigObjectTag) == 2);
  76.     ASSERT(sizeof(wNewClassTag) == 2);
  77.  
  78.     // make sure m_pStoreMap is initialized
  79.     MapObject(NULL);
  80.  
  81.     if (pOb == NULL)
  82.     {
  83.         // save out null tag to represent NULL pointer
  84.         *this << wNullTag;
  85.     }
  86.     else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)
  87.         // assumes initialized to 0 map
  88.     {
  89.         // save out index of already stored object
  90.         if (nObIndex < wBigObjectTag)
  91.             *this << (WORD)nObIndex;
  92.         else
  93.         {
  94.             *this << wBigObjectTag;
  95.             *this << nObIndex;
  96.         }
  97.     }
  98.     else
  99.     {
  100.         // write class of object first
  101.         CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
  102.         WriteClass(pClassRef);
  103.  
  104.         // enter in stored object table, checking for overflow
  105.         CheckCount();
  106.         (*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;
  107.  
  108.         // cause the object to serialize itself
  109.         ((CObject*)pOb)->Serialize(*this);
  110.     }
  111. }
  112.  
  113. CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
  114. {
  115.     ASSERT(pClassRefRequested == NULL ||
  116.         AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
  117.     ASSERT(IsLoading());    // proper direction
  118.     ASSERT(wNullTag == 0);
  119.     ASSERT((wClassTag << 16) == dwBigClassTag);
  120.     ASSERT((wNewClassTag & wClassTag) == wClassTag);
  121.  
  122.     // attempt to load next stream as CRuntimeClass
  123.     UINT nSchema;
  124.     DWORD obTag;
  125.     CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);
  126.  
  127.     // check to see if tag to already loaded object
  128.     CObject* pOb;
  129.     if (pClassRef == NULL)
  130.     {
  131.         if (obTag > (DWORD)m_pLoadArray->GetUpperBound())
  132.         {
  133.             // tag is too large for the number of objects read so far
  134.             AfxThrowArchiveException(CArchiveException::badIndex,
  135.                 m_strFileName);
  136.         }
  137.  
  138.         pOb = (CObject*)m_pLoadArray->GetAt(obTag);
  139.         if (pOb != NULL && pClassRefRequested != NULL &&
  140.              !pOb->IsKindOf(pClassRefRequested))
  141.         {
  142.             // loaded an object but of the wrong class
  143.             AfxThrowArchiveException(CArchiveException::badClass,
  144.                 m_strFileName);
  145.         }
  146.     }
  147.     else
  148.     {
  149.         // allocate a new object based on the class just acquired
  150.         pOb = pClassRef->CreateObject();
  151.         if (pOb == NULL)
  152.             AfxThrowMemoryException();
  153.  
  154.         // Add to mapping array BEFORE de-serializing
  155.         CheckCount();
  156.         m_pLoadArray->InsertAt(m_nMapCount++, pOb);
  157.  
  158.         // Serialize the object with the schema number set in the archive
  159.         UINT nSchemaSave = m_nObjectSchema;
  160.         m_nObjectSchema = nSchema;
  161.         pOb->Serialize(*this);
  162.         m_nObjectSchema = nSchemaSave;
  163.         ASSERT_VALID(pOb);
  164.     }
  165.  
  166.     return pOb;
  167. }
  168.  
  169. /////////////////////////////////////////////////////////////////////////////
  170. // advanced versioning and back-pointer support
  171.  
  172. UINT CArchive::GetObjectSchema()
  173. {
  174.     UINT nResult = m_nObjectSchema;
  175.     m_nObjectSchema = (UINT)-1; // can only be called once per Serialize
  176.     return nResult;
  177. }
  178.  
  179. void CArchive::MapObject(const CObject* pOb)
  180. {
  181.     if (IsStoring())
  182.     {
  183.         if (m_pStoreMap == NULL)
  184.         {
  185.             // initialize the storage map
  186.             //  (use CMapPtrToPtr because it is used for HANDLE maps too)
  187.             m_pStoreMap = new CMapPtrToPtr(m_nGrowSize);
  188.             m_pStoreMap->InitHashTable(m_nHashSize);
  189.             m_pStoreMap->SetAt(NULL, (void*)(DWORD)wNullTag);
  190.             m_nMapCount = 1;
  191.         }
  192.         if (pOb != NULL)
  193.         {
  194.             CheckCount();
  195.             (*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;
  196.         }
  197.     }
  198.     else
  199.     {
  200.         if (m_pLoadArray == NULL)
  201.         {
  202.             // initialize the loaded object pointer array and set special values
  203.             m_pLoadArray = new CPtrArray;
  204.             m_pLoadArray->SetSize(1, m_nGrowSize);
  205.             ASSERT(wNullTag == 0);
  206.             m_pLoadArray->SetAt(wNullTag, NULL);
  207.             m_nMapCount = 1;
  208.         }
  209.         if (pOb != NULL)
  210.         {
  211.             CheckCount();
  212.             m_pLoadArray->InsertAt(m_nMapCount++, (void*)pOb);
  213.         }
  214.     }
  215. }
  216.  
  217. void CArchive::WriteClass(const CRuntimeClass* pClassRef)
  218. {
  219.     ASSERT(pClassRef != NULL);
  220.     ASSERT(IsStoring());    // proper direction
  221.  
  222.     if (pClassRef->m_wSchema == 0xFFFF)
  223.     {
  224.         TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.\n",
  225.             pClassRef->m_lpszClassName);
  226.         AfxThrowNotSupportedException();
  227.     }
  228.  
  229.     // make sure m_pStoreMap is initialized
  230.     MapObject(NULL);
  231.  
  232.     // write out class id of pOb, with high bit set to indicate
  233.     // new object follows
  234.  
  235.     // ASSUME: initialized to 0 map
  236.     DWORD nClassIndex;
  237.     if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)
  238.     {
  239.         // previously seen class, write out the index tagged by high bit
  240.         if (nClassIndex < wBigObjectTag)
  241.             *this << (WORD)(wClassTag | nClassIndex);
  242.         else
  243.         {
  244.             *this << wBigObjectTag;
  245.             *this << (dwBigClassTag | nClassIndex);
  246.         }
  247.     }
  248.     else
  249.     {
  250.         // store new class
  251.         *this << wNewClassTag;
  252.         pClassRef->Store(*this);
  253.  
  254.         // store new class reference in map, checking for overflow
  255.         CheckCount();
  256.         (*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;
  257.     }
  258. }
  259.  
  260. CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested,
  261.     UINT* pSchema, DWORD* pObTag)
  262. {
  263.     ASSERT(pClassRefRequested == NULL ||
  264.         AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
  265.     ASSERT(IsLoading());    // proper direction
  266.  
  267.     if (pClassRefRequested != NULL && pClassRefRequested->m_wSchema == 0xFFFF)
  268.     {
  269.         TRACE1("Warning: Cannot call ReadClass/ReadObject for %hs.\n",
  270.             pClassRefRequested->m_lpszClassName);
  271.         AfxThrowNotSupportedException();
  272.     }
  273.  
  274.     // make sure m_pLoadArray is initialized
  275.     MapObject(NULL);
  276.  
  277.     // read object tag - if prefixed by wBigObjectTag then DWORD tag follows
  278.     DWORD obTag;
  279.     WORD wTag;
  280.     *this >> wTag;
  281.     if (wTag == wBigObjectTag)
  282.         *this >> obTag;
  283.     else
  284.         obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);
  285.  
  286.     // check for object tag (throw exception if expecting class tag)
  287.     if (!(obTag & dwBigClassTag))
  288.     {
  289.         if (pObTag == NULL)
  290.             AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
  291.  
  292.         *pObTag = obTag;
  293.         return NULL;
  294.     }
  295.  
  296.     CRuntimeClass* pClassRef;
  297.     UINT nSchema;
  298.     if (wTag == wNewClassTag)
  299.     {
  300.         // new object follows a new class id
  301.         if ((pClassRef = CRuntimeClass::Load(*this, &nSchema)) == NULL)
  302.             AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
  303.  
  304.         // check nSchema against the expected schema
  305.         if ((pClassRef->m_wSchema & ~VERSIONABLE_SCHEMA) != nSchema)
  306.         {
  307.             if (!(pClassRef->m_wSchema & VERSIONABLE_SCHEMA))
  308.             {
  309.                 // schema doesn't match and not marked as VERSIONABLE_SCHEMA
  310.                 AfxThrowArchiveException(CArchiveException::badSchema,
  311.                     m_strFileName);
  312.             }
  313.             else
  314.             {
  315.                 // they differ -- store the schema for later retrieval
  316.                 if (m_pSchemaMap == NULL)
  317.                     m_pSchemaMap = new CMapPtrToPtr;
  318.                 ASSERT_VALID(m_pSchemaMap);
  319.                 m_pSchemaMap->SetAt(pClassRef, (void*)nSchema);
  320.             }
  321.         }
  322.         CheckCount();
  323.         m_pLoadArray->InsertAt(m_nMapCount++, pClassRef);
  324.     }
  325.     else
  326.     {
  327.         // existing class index in obTag followed by new object
  328.         DWORD nClassIndex = (obTag & ~dwBigClassTag);
  329.         if (nClassIndex == 0 || nClassIndex > (DWORD)m_pLoadArray->GetUpperBound())
  330.             AfxThrowArchiveException(CArchiveException::badIndex,
  331.                 m_strFileName);
  332.  
  333.         pClassRef = (CRuntimeClass*)m_pLoadArray->GetAt(nClassIndex);
  334.         ASSERT(pClassRef != NULL);
  335.  
  336.         // determine schema stored against objects of this type
  337.         void* pTemp;
  338.         BOOL bFound = FALSE;
  339.         nSchema = 0;
  340.         if (m_pSchemaMap != NULL)
  341.         {
  342.             bFound = m_pSchemaMap->Lookup( pClassRef, pTemp );
  343.             if (bFound)
  344.                 nSchema = (UINT)pTemp;
  345.         }
  346.         if (!bFound)
  347.             nSchema = pClassRef->m_wSchema & ~VERSIONABLE_SCHEMA;
  348.    }
  349.  
  350.     // check for correct derivation
  351.     if (pClassRefRequested != NULL &&
  352.         !pClassRef->IsDerivedFrom(pClassRefRequested))
  353.     {
  354.         AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
  355.     }
  356.  
  357.     // store nSchema for later examination
  358.     if (pSchema != NULL)
  359.         *pSchema = nSchema;
  360.     else
  361.         m_nObjectSchema = nSchema;
  362.  
  363.     // store obTag for later examination
  364.     if (pObTag != NULL)
  365.         *pObTag = obTag;
  366.  
  367.     // return the resulting CRuntimeClass*
  368.     return pClassRef;
  369. }
  370.  
  371. void CArchive::SerializeClass(const CRuntimeClass* pClassRef)
  372. {
  373.     if (IsStoring())
  374.         WriteClass(pClassRef);
  375.     else
  376.         ReadClass(pClassRef);
  377. }
  378.  
  379. ////////////////////////////////////////////////////////////////////////////
  380.