MFC 序列化機制使用結構號進行版本控制。寫入檔案時標記結構號,讀取時比對,不符則拋出 CArchiveException (badSchema),導致「非預期的檔案格式」錯誤。若每次修改儲存格式都遞增結構號,可避免新版程式讀取舊版資料失敗。
但實務上常需「向下相容」:新版程式需同時支援舊版資料格式。此時可使用「可版本化結構號」。
將 IMPLEMENT_SERIAL 宏的結構號與 VERSIONABLE_SCHEMA 進行位元或運算:
IMPLEMENT_SERIAL(CLine, CObject, 2 | VERSIONABLE_SCHEMA)
此標記告知 MFC:該類別可處理多種序列化格式,抑制 CArchiveException,允許程式在 Serialize() 中透過 CArchive::GetObjectSchema() 判斷版本並對應處理。
class CLine : public CObject
{
DECLARE_SERIAL(CLine)
protected:
CPoint m_ptFrom, m_ptTo;
public:
CLine() {}
CLine(CPoint from, CPoint to) { m_ptFrom = from; m_ptTo = to; }
void Serialize(CArchive& ar);
};
void CLine::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
ar << m_ptFrom << m_ptTo;
else
ar >> m_ptFrom >> m_ptTo;
}
IMPLEMENT_SERIAL(CLine, CObject, 1)
class CLine : public CObject
{
DECLARE_SERIAL(CLine)
protected:
CPoint m_ptFrom, m_ptTo;
COLORREF m_clrLine; // 新增
public:
CLine() {}
CLine(CPoint from, CPoint to, COLORREF color)
{ m_ptFrom = from; m_ptTo = to; m_clrLine = color; }
void Serialize(CArchive& ar);
};
IMPLEMENT_SERIAL(CLine, CObject, 2 | VERSIONABLE_SCHEMA)
void CLine::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring()) {
ar << m_ptFrom << m_ptTo << m_clrLine; // 統一寫入最新版
}
else {
UINT nSchema = ar.GetObjectSchema();
switch (nSchema) {
case 1: // 版本 1 資料
ar >> m_ptFrom >> m_ptTo;
m_clrLine = RGB(0, 0, 0); // 預設顏色
break;
case 2: // 版本 2 資料
ar >> m_ptFrom >> m_ptTo >> m_clrLine;
break;
default:
AfxThrowArchiveException(CArchiveException::badSchema);
}
}
}
class CSquareDoc : public CDocument
{
DECLARE_SERIAL(CSquareDoc)
// ... 成員變數
};
IMPLEMENT_SERIAL(CSquareDoc, CDocument, 2 | VERSIONABLE_SCHEMA)
void CSquareDoc::Serialize(CArchive& ar)
{
ar.SerializeClass(RUNTIME_CLASS(CSquareDoc));
CDocument::Serialize(ar);
if (ar.IsStoring()) {
SaveData(ar); // 僅按最新版本儲存
}
else {
UINT nSchema = ar.GetObjectSchema();
switch (nSchema) {
case 1: LoadDataV1(ar); break;
case 2: LoadDataV2(ar); break;
default: AfxThrowArchiveException(CArchiveException::badSchema);
}
}
}
void CSquareDoc::LoadDataV1(CArchive& ar)
{
ar >> iSpeed >> fenshu;
iBomb = iMissile = iA_bomb = 0;
for (int i = 0; i < SQUARE_AMOUNT_y; i++)
for (int j = 0; j < SQUARE_AMOUNT_x; j++) {
ar >> mc[j][i].CellPos.x >> mc[j][i].CellPos.y
>> mc[j][i].IsFillFlag >> mc[j][i].size.cx >> mc[j][i].size.cy;
mc[j][i].rgb = RGB(0, 255, 0);
}
// 方塊與預知方塊同理,rgb 固定綠色
}
void CSquareDoc::LoadDataV2(CArchive& ar)
{
ar >> iSpeed >> fenshu >> iBomb >> iMissile >> iA_bomb;
// 完整載入地圖、方塊、預知方塊所有欄位含 rgb
}
使用 VERSIONABLE_SCHEMA 搭配 GetObjectSchema() 切換邏輯,即可優雅實現 MFC 序列化的多版本相容。未來擴充只需遞增版本號並新增 case 分支即可。