MFC 提供CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。

以下对CArchvie 的内部实现作分析。

1.概述
2.内部数据
3.基本数据读写
4.缓冲区的更新
5.指定长度数据段落的读写
6.字符串的读写
7.CObject派生对象的读写

1.概述

CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。

当建立CArchive对象时,应指定其模式是用于缓冲区读,还是用于缓冲区写。

可以这样理解,CArchive对象相当于铁路的货运练调度站,零散的货物被收集,当总量到达火车运量的时候,由火车装运走。

当接到火车的货物时,则货物由被分散到各自的货主。与货运不同的是,交货、取货是按时间循序执行的,而不是凭票据。因此必须保证送货的和取货的货主按同样的循序去存或取。

对于大型的货物,则是拆散成火车单位,运走,取货时,依次取各部分,组装成原物。

2.内部数据

缓冲区指针 BYTE* m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。

缓冲区尾部指针 BYTE* m_lpBufMax;

缓冲区当前位置指针 BYTE* m_lpBufCur;

初始化时,如果是读模式,当前位置在尾部,如果是写模式,当前位置在头部:

m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;

3.基本数据读写

对于基本的数据类型,例如字节、双字等,可以直接使用">>"、"<<"符号进行读出、写入。

//操作符定义捕:
 
 //插入操作
 CArchive& operator<<(BYTE by);
 CArchive& operator<<(WORD w);
 CArchive& operator<<(LONG l);
 CArchive& operator<<(DWORD dw);
 CArchive& operator<<(float f);
 CArchive& operator<<(double d);
 CArchive& operator<<(int i);
 CArchive& operator<<(short w);
 CArchive& operator<<(char ch);
 CArchive& operator<<(unsigned u);
 //提取操作
 CArchive& operator>>(BYTE& by);
 CArchive& operator>>(WORD& w);
 CArchive& operator>>(DWORD& dw);
 CArchive& operator>>(LONG& l);
 CArchive& operator>>(float& f);
 CArchive& operator>>(double& d);
 CArchive& operator>>(int& i);
 CArchive& operator>>(short& w);
 CArchive& operator>>(char& ch);
 CArchive& operator>>(unsigned& u);

下面以双字为例,分析原码

双字的插入(写)

CArchive& CArchive::operator<<(DWORD dw)
{
 if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //缓冲区空间不够
  Flush();  //缓冲区内容提交到实际存储煤质。
 if (!(m_nMode & bNoByteSwap))
  _AfxByteSwap(dw, m_lpBufCur);  //处理字节顺序
 else
  *(DWORD*)m_lpBufCur = dw;      //添入缓冲区
 m_lpBufCur += sizeof(DWORD);     //移动当前指针
 return *this;
}
双字的提取(读)

CArchive& CArchive::operator>>(DWORD& dw)
{
 if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax) //缓冲区要读完了
  FillBuffer(sizeof(DWORD) - (UINT)(m_lpBufMax - m_lpBufCur));  //重新读入内容到缓冲区
 dw = *(DWORD*)m_lpBufCur;  //读取双字
 m_lpBufCur += sizeof(DWORD); //移动当前位置指针
 if (!(m_nMode & bNoByteSwap))
  _AfxByteSwap(dw, (BYTE*)&dw);  //处理字节顺序
 return *this;
}

4.缓冲区的更新

以上操作中,当缓冲区将插入满或缓冲区将提取空时,都将对缓冲区进行更新处理

缓冲区将插入致满时调用Flush();

void CArchive::Flush()
{
 ASSERT_VALID(m_pFile);
 ASSERT(m_bDirectBuffer || m_lpBufStart != NULL);
 ASSERT(m_bDirectBuffer || m_lpBufCur != NULL);
 ASSERT(m_lpBufStart == NULL ||
  AfxIsValidAddress(m_lpBufStart, m_lpBufMax - m_lpBufStart, IsStoring()));
 ASSERT(m_lpBufCur == NULL ||
  AfxIsValidAddress(m_lpBufCur, m_lpBufMax - m_lpBufCur, IsStoring()));
 if (IsLoading())
 {
  // unget the characters in the buffer, seek back unused amount
  if (m_lpBufMax != m_lpBufCur)
   m_pFile-> Seek(-(m_lpBufMax - m_lpBufCur), CFile::current);
  m_lpBufCur = m_lpBufMax;    // 指向尾
 }
 else   //写模式
 {
  if (!m_bDirectBuffer)
  {
   // 内容写入到文件
   if (m_lpBufCur != m_lpBufStart)
    m_pFile-> Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
  }
  else
  {
   //如果是直接针对内存区域的的(例如CMemFile中) (只需移动相关指针,指向新的一块内存)
   if (m_lpBufCur != m_lpBufStart)
    m_pFile-> GetBufferPtr(CFile::bufferCommit, m_lpBufCur - m_lpBufStart);
   // get next buffer
   VERIFY(m_pFile-> GetBufferPtr(CFile::bufferWrite, m_nBufSize,
    (void**)&m_lpBufStart, (void**)&m_lpBufMax) == (UINT)m_nBufSize);
   ASSERT((UINT)m_nBufSize == (UINT)(m_lpBufMax - m_lpBufStart));
  }
  m_lpBufCur = m_lpBufStart; //指向缓冲区首
 }
}

缓冲区将提取空将调用GFileeBuffer,nBytesNeeded为当前剩余部分上尚有用的字节

void CArchive::FillBuffer(UINT nBytesNeeded)
{
 ASSERT_VALID(m_pFile);
 ASSERT(IsLoading());
 ASSERT(m_bDirectBuffer || m_lpBufStart != NULL);
 ASSERT(m_bDirectBuffer || m_lpBufCur != NULL);
 ASSERT(nBytesNeeded > 0);
 ASSERT(nBytesNeeded <= (UINT)m_nBufSize);
 ASSERT(m_lpBufStart == NULL ||
  AfxIsValidAddress(m_lpBufStart, m_lpBufMax - m_lpBufStart, FALSE));
 ASSERT(m_lpBufCur == NULL ||
  AfxIsValidAddress(m_lpBufCur, m_lpBufMax - m_lpBufCur, FALSE));
 UINT nUnused = m_lpBufMax - m_lpBufCur;
 ULONG nTotalNeeded = ((ULONG)nBytesNeeded) + nUnused;
 // 从文件中读取
 if (!m_bDirectBuffer)
 {
  ASSERT(m_lpBufCur != NULL);
  ASSERT(m_lpBufStart != NULL);
  ASSERT(m_lpBufMax != NULL);
  if (m_lpBufCur > m_lpBufStart)
  {
   //保留剩余的尚未处理的部分,将它们移动到头
   if ((int)nUnused > 0)
   {
    memmove(m_lpBufStart, m_lpBufCur, nUnused);
    m_lpBufCur = m_lpBufStart;
    m_lpBufMax = m_lpBufStart + nUnused;
   }
   // read to satisfy nBytesNeeded or nLeft if possible
   UINT nRead = nUnused;
   UINT nLeft = m_nBufSize-nUnused;
   UINT nBytes;
   BYTE* lpTemp = m_lpBufStart + nUnused;
   do
   {
    nBytes = m_pFile-> Read(lpTemp, nLeft);
    lpTemp = lpTemp + nBytes;
    nRead += nBytes;
    nLeft -= nBytes;
   }
   while (nBytes > 0 && nLeft > 0 && nRead < nBytesNeeded);
   m_lpBufCur = m_lpBufStart;
   m_lpBufMax = m_lpBufStart + nRead;
  }
 }
 else
 {
  // 如果是针对内存区域(CMemFile),移动相关指针,指向新的一块内存
  if (nUnused != 0)
   m_pFile-> Seek(-(LONG)nUnused, CFile::current);
  UINT nActual = m_pFile-> GetBufferPtr(CFile::bufferRead, m_nBufSize,
   (void**)&m_lpBufStart, (void**)&m_lpBufMax);
  ASSERT(nActual == (UINT)(m_lpBufMax - m_lpBufStart));
  m_lpBufCur = m_lpBufStart;
 }
 // not enough data to fill request?
 if ((ULONG)(m_lpBufMax - m_lpBufCur) < nTotalNeeded)
  AfxThrowArchiveException(CArchiveException::endOfFile);
}

5.指定长度数据段落的读写

以下分析

UINT Read(void* lpBuf, UINT nMax); 读取长度为nMax的数据

void Write(const void* lpBuf, UINT nMax); 写入指定长度nMax的数据

对于大段数据的读写,先使用当前缓冲区中的内容或空间读取或写入,若这些空间够用了,则结束。

否则,从剩余的数据中找出最大的缓冲区整数倍大小的一块数据,直接读写到存储煤质(不反复使用缓冲区)。

剩余的余数部分,再使用缓冲区读写。

(说明:缓冲区读写的主要目的是将零散的数据以缓冲区大小为尺度来处理。对于大型数据,其中间的部分,不是零散的数据,使用缓冲区已经没有意思,故直接读写)

①读取

UINT CArchive::Read(void* lpBuf, UINT nMax)
{
 ASSERT_VALID(m_pFile);
 if (nMax == 0)
  return 0;
 UINT nMaxTemp = nMax;  //还需要读入的长度,读入一部分,就减相应数值,直到此数值变为零
 
 //处理当前缓冲区中剩余部分。
 //如果要求读入字节小于缓冲区中剩余部分,则第一部分为要求读入的字节数,
 //否则读入全部剩余部分
 UINT nTemp = min(nMaxTemp, (UINT)(m_lpBufMax - m_lpBufCur));  
 memcpy(lpBuf, m_lpBufCur, nTemp);
 m_lpBufCur += nTemp;
 lpBuf = (BYTE*)lpBuf + nTemp; //移动读出内容所在区域的指针
 nMaxTemp -= nTemp;
 //当前缓冲区中剩余部分不够要求读入的长度。
 //还有字节需要读,则需要根据需要执行若干次填充缓冲区,读出,直到读出指定字节。
 if (nMaxTemp != 0) 
 {
  //计算出去除尾数部分的字节大小(整数个缓冲区大小)
  //对于这些部分,字节从文件对象中读出,放到输出缓冲区
  nTemp = nMaxTemp - (nMaxTemp % m_nBufSize); 
  UINT nRead = 0;
  UINT nLeft = nTemp;
  UINT nBytes;
  do
  {
   nBytes = m_pFile-> Read(lpBuf, nLeft); //要求读入此整数缓冲区部分大小
   lpBuf = (BYTE*)lpBuf + nBytes;
   nRead += nBytes;
   nLeft -= nBytes;
  }
  while ((nBytes > 0) && (nLeft > 0)); 知道读入了预定大小,或到达文件尾
  nMaxTemp -= nRead;
  if (nRead == nTemp) //读入的字节等于读入的整数倍部分  该读最后的余数部分了
  {
   // 建立装有此最后余数部分的内容的CArchive的工作缓冲区。
   if (!m_bDirectBuffer)
   {
    UINT nLeft = max(nMaxTemp, (UINT)m_nBufSize);
    UINT nBytes;
    BYTE* lpTemp = m_lpBufStart;
    nRead = 0;
    do
    {
     nBytes = m_pFile-> Read(lpTemp, nLeft);  //从文件中读入到CArchive缓冲区
     lpTemp = lpTemp + nBytes;
     nRead += nBytes;
     nLeft -= nBytes;
    }
    while ((nBytes > 0) && (nLeft > 0) && nRead < nMaxTemp);
    m_lpBufCur = m_lpBufStart;
    m_lpBufMax = m_lpBufStart + nRead;
   }
   else
   {
    nRead = m_pFile-> GetBufferPtr(CFile::bufferRead, m_nBufSize,
     (void**)&m_lpBufStart, (void**)&m_lpBufMax);
    ASSERT(nRead == (UINT)(m_lpBufMax - m_lpBufStart));
    m_lpBufCur = m_lpBufStart;
   }
   //读出此剩余部分到输出
   nTemp = min(nMaxTemp, (UINT)(m_lpBufMax - m_lpBufCur));
   memcpy(lpBuf, m_lpBufCur, nTemp);
   m_lpBufCur += nTemp;
   nMaxTemp -= nTemp;
  }
 
 }
 return nMax - nMaxTemp;
}
②保存,写入

void CArchive::Write(const void* lpBuf, UINT nMax)
{
 if (nMax == 0)
  return;
 
 //读入可能的部分到缓冲区当前的剩余部分
 UINT nTemp = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
 memcpy(m_lpBufCur, lpBuf, nTemp);
 m_lpBufCur += nTemp;
 lpBuf = (BYTE*)lpBuf + nTemp;
 nMax -= nTemp;
 if (nMax > 0)  //还有未写入的部分
 {
  Flush();    //将当前缓冲区写入到存储煤质
  //计算出整数倍缓冲区大小的字节数
  nTemp = nMax - (nMax % m_nBufSize);
  m_pFile-> Write(lpBuf, nTemp);  //直接写到文件
  lpBuf = (BYTE*)lpBuf + nTemp;
  nMax -= nTemp;

//剩余部分添加到缓冲区
  if (m_bDirectBuffer)
  {
   // sync up direct mode buffer to new file position
   VERIFY(m_pFile-> GetBufferPtr(CFile::bufferWrite, m_nBufSize,
    (void**)&m_lpBufStart, (void**)&m_lpBufMax) == (UINT)m_nBufSize);
   ASSERT((UINT)m_nBufSize == (UINT)(m_lpBufMax - m_lpBufStart));
   m_lpBufCur = m_lpBufStart;
  }
  // copy remaining to active buffer
  ASSERT(nMax < (UINT)m_nBufSize);
  ASSERT(m_lpBufCur == m_lpBufStart);
  memcpy(m_lpBufCur, lpBuf, nMax);
  m_lpBufCur += nMax;
 }
}
6.字符串的读写

①CArchive提供的WriteString和ReadString

字符串写

void CArchive::WriteString(LPCTSTR lpsz)
{
 ASSERT(AfxIsValidString(lpsz));
 Write(lpsz, lstrlen(lpsz) * sizeof(TCHAR));  //调用Write,将字符串对应的一段数据写入
}
字符串读(读取一行字符串)

LPTSTR CArchive::ReadString(LPTSTR lpsz, UINT nMax)
{
 // if nMax is negative (such a large number doesn't make sense given today's
 // 2gb address space), then assume it to mean "keep the newline".
 int nStop = (int)nMax < 0 ? -(int)nMax : (int)nMax;
 ASSERT(AfxIsValidAddress(lpsz, (nStop+1) * sizeof(TCHAR)));
 _TUCHAR ch;
 int nRead = 0;
 TRY
 {
  while (nRead < nStop)
  {
   *this >> ch;  //读出一个字节
   // stop and end-of-line (trailing '/n' is ignored)  遇换行—回车
   if (ch == '/n' || ch == '/r')
   {
    if (ch == '/r')
     *this >> ch;
    // store the newline when called with negative nMax
    if ((int)nMax != nStop)
     lpsz[nRead++] = ch;
    break;
   }
   lpsz[nRead++] = ch;
  }
 }
 CATCH(CArchiveException, e)
 {
  if (e-> m_cause == CArchiveException::endOfFile)
  {
   DELETE_EXCEPTION(e);
   if (nRead == 0)
    return NULL;
  }
  else
  {
   THROW_LAST();
  }
 }
 END_CATCH
 lpsz[nRead] = '/0';
 return lpsz;
}
ReadString到CString对象,可以多行字符
BOOL CArchive::ReadString(CString& rString)
{
 rString = &afxChNil;    // empty string without deallocating
 const int nMaxSize = 128;
 LPTSTR lpsz = rString.GetBuffer(nMaxSize);
 LPTSTR lpszResult;
 int nLen;
 for (;;)
 {
  lpszResult = ReadString(lpsz, (UINT)-nMaxSize); // store the newline
  rString.ReleaseBuffer();
  // if string is read completely or EOF
  if (lpszResult == NULL ||
   (nLen = lstrlen(lpsz)) < nMaxSize ||
   lpsz[nLen-1] == '/n')
  {
   break;
  }
  nLen = rString.GetLength();
  lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen;
 }
 // remove '/n' from end of string if present
 lpsz = rString.GetBuffer(0);
 nLen = rString.GetLength();
 if (nLen != 0 && lpsz[nLen-1] == '/n')
  rString.GetBufferSetLength(nLen-1);
 return lpszResult != NULL;
}
②使用CString对象的"<<"与">>"符读写字符串

CString定义了输入输出符,可以象基本类型的数据一样使用CArchive

的操作符定义

friend CArchive& AFXAPI operator<<(CArchive& ar, const CString& string);
friend CArchive& AFXAPI operator>>(CArchive& ar, CString& string);
// CString serialization code
// String format:
//      UNICODE strings are always prefixed by 0xff, 0xfffe
//      if < 0xff chars: len:BYTE, TCHAR chars
//      if >= 0xff characters: 0xff, len:WORD, TCHAR chars
//      if >= 0xfffe characters: 0xff, 0xffff, len:DWORD, TCHARs
CArchive& AFXAPI operator<<(CArchive& ar, const CString& string)
{
 // special signature to recognize unicode strings
#ifdef _UNICODE
 ar << (BYTE)0xff;
 ar << (WORD)0xfffe;
#endif
 if (string.GetData()-> nDataLength < 255)
 {
  ar << (BYTE)string.GetData()-> nDataLength;
 }
 else if (string.GetData()-> nDataLength < 0xfffe)
 {
  ar << (BYTE)0xff;
  ar << (WORD)string.GetData()-> nDataLength;
 }
 else
 {
  ar << (BYTE)0xff;
  ar << (WORD)0xffff;
  ar << (DWORD)string.GetData()-> nDataLength;
 }
 ar.Write(string.m_pchData, string.GetData()-> nDataLength*sizeof(TCHAR));
 return ar;
}
// return string length or -1 if UNICODE string is found in the archive
AFX_STATIC UINT AFXAPI _AfxReadStringLength(CArchive& ar)
{
 DWORD nNewLen;
 // attempt BYTE length first
 BYTE bLen;
 ar >> bLen;
 if (bLen < 0xff)
  return bLen;
 // attempt WORD length
 WORD wLen;
 ar >> wLen;
 if (wLen == 0xfffe)
 {
  // UNICODE string prefix (length will follow)
  return (UINT)-1;
 }
 else if (wLen == 0xffff)
 {
  // read DWORD of length
  ar >> nNewLen;
  return (UINT)nNewLen;
 }
 else
  return wLen;
}
CArchive& AFXAPI operator>>(CArchive& ar, CString& string)
{
#ifdef _UNICODE
 int nConvert = 1;   // if we get ANSI, convert
#else
 int nConvert = 0;   // if we get UNICODE, convert
#endif
 UINT nNewLen = _AfxReadStringLength(ar);
 if (nNewLen == (UINT)-1)
 {
  nConvert = 1 - nConvert;
  nNewLen = _AfxReadStringLength(ar);
  ASSERT(nNewLen != -1);
 }
 // set length of string to new length
 UINT nByteLen = nNewLen;
#ifdef _UNICODE
 string.GetBufferSetLength((int)nNewLen);
 nByteLen += nByteLen * (1 - nConvert);  // bytes to read
#else
 nByteLen += nByteLen * nConvert;    // bytes to read
 if (nNewLen == 0)
  string.GetBufferSetLength(0);
 else
  string.GetBufferSetLength((int)nByteLen+nConvert);
#endif
 // read in the characters
 if (nNewLen != 0)
 {
  ASSERT(nByteLen != 0);
  // read new data
  if (ar.Read(string.m_pchData, nByteLen) != nByteLen)
   AfxThrowArchiveException(CArchiveException::endOfFile);
  // convert the data if as necessary
  if (nConvert != 0)
  {
#ifdef _UNICODE
   CStringData* pOldData = string.GetData();
   LPSTR lpsz = (LPSTR)string.m_pchData;
#else
   CStringData* pOldData = string.GetData();
   LPWSTR lpsz = (LPWSTR)string.m_pchData;
#endif
   lpsz[nNewLen] = '/0';    // must be NUL terminated
   string.Init();   // don't delete the old data
   string = lpsz;   // convert with operator=(LPWCSTR)
   CString::FreeData(pOldData);
  }
 }
 return ar;
}

7.CObject派生对象的读写

MFC中多数类都从CObject类派生,CObject类与CArchive类有着良好的合作关系,能实现将对象序列化储存到文件或其他媒介中去,或者读取预先储存的对象,动态建立对象等功能。

①CObject定义了针对CArvhive的输入输出操作符,可以向其他基本数据类型一样使用"<<"、"<<"符号

CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
 { ar.WriteObject(pOb); return ar; }
CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb)
 { pOb = ar.ReadObject(NULL); return ar; }
当使用这些符号时,实际上执行的是CArchive的WriteObject和ReadObject成员

②WriteObject与ReadObject

在WriteObject与ReadObject中先写入或读取运行时类信息(CRuntimeClas),再调用Serialze(..),按其中的代码读写具体的对象数据。

因此,只要在CObject派生类中重载Serilize()函数,写入具体的读写过程,就可以使对象具有存储与创建能力。

//将对象写入到缓冲区
void CArchive::WriteObject(const CObject* pOb)
{
 DWORD nObIndex;
 // 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++;
  // 调用CObject的Serialize成员,按其中的代码写入类中数据。
  ((CObject*)pOb)-> Serialize(*this);
 }
}

CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
{
 // 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
 {
  // 建立对象
  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); //调用CObject的Serialize,按其中代码读入对象数据。
  m_nObjectSchema = nSchemaSave;
  ASSERT_VALID(pOb);
 }
 return pOb;
}

③运行时类信息的读写

为了避免众多重复的同类对象写入重复的类信息,CArchive中使用CMap对象储存和检索类信息。

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;
  BOOL bFound = FALSE;
  nSchema = 0;
  if (m_pSchemaMap != NULL)
  {
   bFound = m_pSchemaMap-> Lookup( pClassRef, pTemp );
   if (bFound)
    nSchema = (UINT)pTemp;
  }
  if (!bFound)
   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;
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/FMD/archive/2001/06/16/5530.aspx

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

MFC浅析 8 CArchive 原理相关推荐

  1. 数据结构 — 浅析红黑树原理以及实现

    浅析红黑树原理以及实现 我们在上一篇博客认识到了平衡二叉树(AVLTree),了解到平衡二叉树的性质,其实平衡二叉树最大的作用就是查找,AVL树的查找.插入 和删除在平均 和 最坏情况下都是O(log ...

  2. MFC中CArray类原理及其应用

    1.CArray类应用 函数简介 CArray::GetSize int GetSize( ) const; 取得当前数组元素个数. CArray::GetUpperBound int GetUppe ...

  3. 光电编码器的原理及应用场合_【技术浅析】编码器原理在数控系统维修中的应用...

    摘要:本文分析了编码器工作原理及其在数控系统中的应用,  结合维修工作中常见的机床零点丢失故障案例,找出有效的解决方法.      关键词:编码器 FANUC 数控系统 参考点 目前数控机床采用日本 ...

  4. 浅析React Hooks原理

    React Hooks 简介 React Hooks 是 React 16.8 以及之后版本的产物,React Hooks 就是一堆钩子函数,不同的钩子函数提供了不同的功能,React 通过这些钩子函 ...

  5. 浅析 Servlet 执行原理

    在JavaWeb学习研究中,Servlet扮演重要的作用,学好它,是后续JavaWeb学习的良好基础.无论是SSH,还是SSM,微服务JavaWeb技术,都应先学好Servlet,从而达到事半功倍的效 ...

  6. 【网络安全】浅析跨域原理及如何实现跨域

    前言 我们在解决一个问题的时候应该先去了解这个问题是如何产生的,为什么会有跨域的存在呢?其实,最终的罪魁祸首都是浏览器的同源策略,浏览器的同源策略限制我们只能在相同的协议.IP地址.端口号相同,如果有 ...

  7. 浅析laravel门面原理与实现

    Facade模式 描述:Facade模式要求一个子系统的外部与其内部的通信必须通过一个统一的Facade对象进行.Facade模式提供一个高层次的接口,使得子系统更易于使用. 意义:将一个系统划分成为 ...

  8. 浅析HTTP代理原理--转

    代理服务器是HTTP协议中一个重要的组件,发挥着重要的作用. 关于HTTP代理的文章有很多,本文不再赘述,如果不清楚的可以看一下 HTTP代理的基础知识. 本文主要介绍代理的事例,分析一个真实的案例来 ...

  9. 浅析webpack的原理

    一.前言 ​ 现在随着前端开发的复杂度和规模越来越大,鹰不能抛开工程化来独立开发,比如:react的jsx代码必须编译后才能在浏览器中使用,比如sass和less代码浏览器是不支持的.如果摒弃这些开发 ...

最新文章

  1. oracle 转化为整数,字符串转换成整数——从源码学习
  2. SAP MM 中级之事务代码MICN的相关逻辑
  3. golang从stdin中读取一行
  4. 2008.04.14狼图腾
  5. 小米平板android版本,除了安卓MIUI7,小米平板2为什么要推出Win10版?
  6. RxLifecycle详细解析
  7. 库,表,记录的相关操作
  8. SQL数据去重复 Distinct 和 row_number() over()
  9. 微信硬件平台对接--蓝牙
  10. js创建对象,用函数实现对象创建,并实现内函数共享
  11. python取反运算
  12. 【职业】什么是架构师?
  13. 沙盒沙箱技术,移动沙盒等
  14. a标签去掉下划线,html,超链接去掉下划线
  15. 高德地图 js自动定位到当前城市
  16. EasyUI filebox 限制文件上传的类型
  17. 企业正确导入BPM系统要注意什么
  18. memory management 蓝屏win10,要怎么解决这个蓝屏问题
  19. 吕文翰 php,自己动手写一个 iOS 网络请求库(三)——降低耦合
  20. Python 输入一个包含多个单词的英文句子

热门文章

  1. Docker-Compose快速搭建Oracle-11G系统
  2. Spring ActiveMQ教程
  3. Kubernetes教程 - Kubernetes综合指南(Use Guide)
  4. 【nginx】【小记】泛解析大量域名的情况下 将不带www的域名,301到与之对应的www前缀的域名
  5. 判断两个数组有相同的数据
  6. C#LeetCode刷题-二分查找​​​​​​​
  7. golang package 是什么意思?一份来自初学者的golang package体验指南
  8. 如何使用PyCharm调试Python代码
  9. 如何从0开始开源项目参与_如何开始一个开源项目
  10. arduino扩展板的使用_如何使用Arduino和React为烧烤构建仪表板