程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> FMD開發文集 -- CArchive原理

FMD開發文集 -- CArchive原理

編輯:關於VC++

MFC 提供CArchive類實現數據的緩沖區讀寫,同時定義了類對象的存儲與讀取方案。 以下對CArchvie 的內部實現作分析。

一.概述 CArchive使用了緩沖區,即一段內存空間作為臨時數據存儲地,對CArchive的讀寫都先依次排列到此緩沖區,當緩沖區滿或用戶要求時,將此段整理後的數據讀寫到指定的存儲煤質。

當建立CArchive對象時,應指定其模式是用於緩沖區讀,還是用於緩沖區寫。

可以這樣理解,CArchive對象相當於鐵路的貨運練調度站,零散的貨物被收集,當總量到達火車運量的時候,由火車裝運走。

當接到火車的貨物時,則貨物由被分散到各自的貨主。與貨運不同的是,交貨、取貨是按時間循序執行的,而不是憑票據。因此必須保證送貨的和取貨的貨主按同樣的循序去存或取。

對於大型的貨物,則是拆散成火車單位,運走,取貨時,依次取各部分,組裝成原物。

二.內部數據

緩沖區指針 BYTE* m_lpBufStart,指向緩沖區,這個緩沖區有可能是底層CFile(如派生類CMemFile)對象提供的,但一般是CArchive自己建立的。

緩沖區尾部指針 BYTE* m_lpBufMax;

緩沖區當前位置指針 BYTE* m_lpBufCur;

初始化時,如果是讀模式,當前位置在尾部,如果是寫模式,當前位置在頭部:

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

三.基本數據讀寫

對於基本的數據類型,例如字節、雙字等,可以直接使用">>"、"<<"符號進行讀出、寫入。

//操作符定義捕:
  
//插入操作
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;
}

四.緩沖區的更新

以上操作中,當緩沖區將插入滿或緩沖區將提取空時,都將對緩沖區進行更新處理。

緩沖區將插入滿時調用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; //指向緩沖區首
  }
}

緩沖區將提取空,會調用FillBuffer。 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);
}

 

五.指定長度數據段落的讀寫

以下分析

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;
  }
}

 

六.字符串的讀寫

①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;
}

 

七.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;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved