Serialize 的聯想(1)

侯捷 1998.12.13


Windows GDI 模組支援 DIB(Device Independent Bitmap)繪圖格式,而 MFC 卻沒有為 DIB 包裝一個好用的 class,這真令人扼腕(MFC 中只有一個 CBitmap,那是為 Device Dependent Bitmap 而準備的)。

●幸運的是...

幸運的是 David Kruglinski 所著的《Inside Visual C++ 4.0》(中譯《深入 Visual C++ 4.0》,侯俊傑/眳p)chap10 有一個好用的 CDib。有檔案讀寫功能,有顯示功能,有調色盤功能。書中範例程式 Ex10c 只使用了 CDib 的讀檔、顯示兩功能,未示範其寫檔功能。

●不幸的是...

不幸的是這個 CDib 的寫檔功能有問題,在 cdib.cpp 的 #344 的CDib::Write() 函式中,有以下動作(書上沒有列表,需自行看光碟檔案):

TRY {
  pFile->Write(LPVOID)&bmfh, sizeof(BITMAPFILEHEADER));
  pFile->Write(LPVOID)&m_lpBMIH, nSize);
}


怎麼看都不對勁兒,只寫了 DIB 檔頭和資訊表頭,卻沒有把DIB 的實際內容(image)寫到檔案去。

●幸運的是...

幸運的是我再檢查 <Inside Visual C++ 5.0>(此版本無中譯本)的光碟檔案,發現 David 已經修改過來了,新內容如下:

TRY {
  pFile->Write((LPVOID)&bmfh, sizeof(BITMAPFILEHEADER));
  pFile->Write((LPVOID)&m_lpBMIH, nSize);
 
pFile->Write((LPVOID)&m_lpImage, m_dwImageSize); // 新增此行
}


我寫了個測試程式,驗證此修改無誤。因此,<Inside Visual C++ 4.0> 或<深入 Visual C++ 4.0> 的讀者只需依樣修改,就有一個好用的 CDib 了。

●bitmap 格式

曾經在 BBS 板上看到有網友想瞭解 Bitmap 格式。您可參考<Inside Visual C++ 4.0> 或 <深入 Visual C++ 4.0> 第 10 章,或 <Inside Visual C++ 5.0> 第 10 章,以及 \msdev\include\wingdi.h,再拿此 CDib source 對照看,很快就可完全掌握 bitmap 格式了。這裡所謂的 bitmap,就是指 DIB(Device Independent Bitmap)。你所看到的 .bmp 檔,其實都是 DIB。

●源由...

我怎麼會發現 David 的這個錯誤呢?

上個月為華邦電子的朋友們開了一門 C++ 課程,順帶談到 MFC 的
Serialize()。在座資深的 MFC programmer,對於 Serialize() 的功能很感懷疑。他們懷疑:

(1) 該如何運用 Serialize() 來處理特定格式(如 Word 或 bitmap 格式)的檔案?

(2) Serialize() 的效率好嗎?從 <多型與虛擬> chap5 對 MFC Serialize 的模擬,或從 <深入淺出 MFC> chap8 對 MFC Serialize 的剖析,都可看出,被 serialized 出去的檔案,其中有些似乎不太能掌控的 overhead (就是那些讀檔時要用於 dynamic creation 的一些 class information),因此無法「精準計算出檔案中某筆資料的 offset,然後一舉到位直接讀取該資料」。這幾位朋友的經驗是,直接使用 CFile 做低階檔案動作,根本捨棄 Serialize()。

為了實驗以 Serialize() 處理 bitmap,我想到 <Inside Visual C++> 書中現成的 class CDib,於是才發現上述的錯誤與更新。

我對華邦工程師的疑惑,回答如下:

(1) 欲以 Serialize() 處理特定格式的檔案內容,只需觀察一下上述 CDib 的Serialize(),即可獲得靈感。應用上,假設自己的繪圖軟體需涵蓋 DIB 圖形,那麼就在自己的 CMyDoc::Serialize() 中呼叫 CDib::Serialize() 即可。

(2) 如果閱讀了 <深入淺出 MFC> chap8 對 MFC Serialize 的剖析,其實可以完全掌握 Serialize() 的 overhead,從而可以精確計算出某筆資料在檔案中的精確 offset,然後一舉到位地直接讀取該筆資料。不過,這種應用其實已經超越了 "serialize" 的字面意義。"serialize" 就是 "循序" 讀寫。因此,如果你的應用軟體確有必要跳躍式讀取資料,你需要一個 database (不管是自己寫的簡易 database 或是購買的成品),而不是 Serialize()。

註:凡能夠 random access, partial update 的,都是我上述所謂的 database。

--- the end