arcobj.cpp 11 KB

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