之所以說復雜, 就是區別與以前談到的 結構化文件存取; 這種復雜的結構化文件也有叫做"復合文檔".
有些文檔不是結構化的, 譬如記事本文件; 結構化的檔可以分為以下幾類:
標准結構化文檔、自定義結構化文檔(譬如 bmp 文件)和復合文檔.
這裡要談到的結構化儲存(復合文檔)是由 Windows 系統通過 COM 提供的, 它能完成像 Windows 目錄結構一樣復雜的文件結構的存取; 提示一下 Windows 的目錄結構: 一個目錄下可以包含子目錄和文件, 然後層層嵌套...
有時我們要存儲的文件也可能會層層分支, 具體的文件內容也可能五花八門, 譬如分支當中的某個文件是張圖片、是一個字符串列表、是一個記錄(或叫結構)等等, 存儲這樣的文件內容恐怕用數據庫也是無能為力的.
這種復合文件支持多線程, 不同的進程中的不同線程可以同時訪問一個復合文件的不同部分.
復合文件最典型的實例就是 OLE(譬如在 Word 中可以嵌入電子表格); 這也或許是這種復合文件的來由.
或許有了這個東西, 出品屬於自己的文件格式就成了輕而易舉的事情了.
存取和訪問復合文檔主要使用定義在 Activex 單元的三個 COM 接口:
IStorage (類似於 Windows 的目錄, 也就是文件夾);
IStream (類似於目錄中的文件, 不過在這裡都是"流", 每個流至少要占用 512 字節);
IEnumStatStg (用於列舉 IStorage 的層次結構)
"接口" 又是一個復雜的概念, 暫時把它認作是一組函數的集合吧.
下面羅列出了所有相關的函數(現在還沒有全部掌握, 學習過程中再慢慢注釋):
IStorage 中的函數:
//創建一個子 IStorage 接口
function CreateStorage(
pwcsName: POleStr; {指定子 IStorage 接口的名稱}
grfMode: Longint; {指定訪問模式}
dwStgFmt: Longint; {保留, 須是 0}
reserved2: Longint; {保留, 須是 0}
out stg: IStorage {返回子 IStorage 接口}
): HResult; stdcall;
//打開當前 IStorage 的子 IStorage
function OpenStorage(
pwcsName: POleStr; {指定子 IStorage 接口的名稱}
const stgPriority: IStorage; {已存在的 IStorage 接口, 一般為 nil}
grfMode: Longint; {指定訪問模式}
snbExclude: TSNB; {是個指針, 一般為 nil; 好像是指定要排除的元素}
reserved: Longint; {保留, 須是 0}
out stg: IStorage {返回打開的子 IStorage 接口}
): HResult; stdcall;
//創建一個子 IStream 接口
function CreateStream(
pwcsName: POleStr; {指定子 IStream 接口的名稱}
grfMode: Longint; {指定訪問模式}
reserved1: Longint; {保留, 須是 0}
reserved2: Longint; {保留, 須是 0}
out stm: IStream {返回子 IStream 接口}
): HResult; stdcall;
//打開當前 IStorage 的子 IStream
function OpenStream(
pwcsName: POleStr; {指定子 IStream 接口的名稱}
reserved1: Pointer; {保留, 須為 nil}
grfMode: Longint; {指定訪問模式}
reserved2: Longint; {保留, 須是 0}
out stm: IStream {返回子 IStream 接口}
): HResult; stdcall;
//復制 IStorage, 該函數可以實現“整理文件,釋放碎片空間”的功能
function CopyTo(
ciidExclude: Longint; {要排除的元素數, 可以是 0}
rgiidExclude: PIID; {好像是以 PIID 的方式指定要排除的元素, 可以是 nil}
snbExclude: TSNB; {指定要被排除的元素, 一般為 nil}
const stgDest: IStorage {目標 IStorage}
): HResult; stdcall;
//復制或移動 "子 IStorage" 或 "子 IStream"
function MoveElementTo(
pwcsName: POleStr; {要復制或移動的 IStorage 或 IStream 的名稱}
const stgDest: IStorage; {目標 IStorage 的名稱}
pwcsNewName: POleStr; {給復制或移動後的 IStorage 或 IStream 指定新的名稱}
grfFlags: Longint {指定是復制還是移動, 可選值: STGMOVE_MOVE、STGMOVE_COPY}
): HResult; stdcall;
//提交更改, 確保更改後的流能反映在父級存儲中
function Commit(
grfCommitFlags: Longint {提交方式, 四個可選值見下面}
): HResult; stdcall;
//grfCommitFlags 可選值:
STGC_DEFAULT = 0;
STGC_OVERWRITE = 1;
STGC_ONLYIFCURRENT = 2;
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4;
//放棄自從上次 Commit 調用以來對事務處理流所做的所有更改
function Revert: HResult; stdcall;
//獲取當前 IStorage 的 IEnumStatStg 接口變量; IEnumStatStg 列舉了 IStorage 的層次結構
function EnumElements(
reserved1: Longint; {保留, 須為 0}
reserved2: Pointer; {保留, 須為 nil}
reserved3: Longint; {保留, 須為 0}
out enm: IEnumStatStg {返回 IEnumStatStg 接口變量}
): HResult; stdcall;
//刪除 "子 IStorage" 或 "子 IStream"
function DestroyElement(
pwcsName: POleStr {指定名稱}
): HResult; stdcall;
//重命名 "子 IStorage" 或 "子 IStream"
function RenameElement(
pwcsOldName: POleStr; {原名}
pwcsNewName: POleStr {新名}
): HResult; stdcall;
//設置元素的時間信息
function SetElementTimes(
pwcsName: POleStr; {元素名}
const ctime: TFileTime; {創建時間}
const atime: TFileTime; {訪問時間}
const mtime: TFileTime {修改時間}
): HResult; stdcall;
//在當前存儲中建立一個特殊的流對象,用來保存 CLSID
function SetClass(
const clsid: TCLSID {}
): HResult; stdcall;
//設置狀態位
function SetStateBits(
grfStateBits: Longint; {}
grfMask: Longint {}
): HResult; stdcall;
//返回一個 TStatStg 結構, 此結構包含該 IStorage 詳細信息, 結構框架附下
function Stat(
out statstg: TStatStg; {TStatStg 結構變量}
grfStatFlag: Longint {選項, 此值可決定是否返回成員值, 見下}
): HResult; stdcall;
//TStatStg 結構:
tagSTATSTG = record
pwcsName: POleStr;
dwType: Longint;
cbSize: Largeint;
mtime: TFileTime;
ctime: TFileTime;
atime: TFileTime;
grfMode: Longint;
grfLocksSupported: Longint;
clsid: TCLSID;
grfStateBits: Longint;
reserved: Longint;
end;
TStatStg = tagSTATSTG;
//grfStatFlag 可選值:
STATFLAG_DEFAULT = 0;
STATFLAG_NONAME = 1;
IStream 中的函數:
//從 IStream 中讀取數據
function Read(
pv: Pointer; {接受數據的變量的指針}
cb: Longint; {要讀取的字節數}
pcbRead: PLongint {實際讀出的字節數}
): HResult; stdcall;
//向 IStream 寫入數據
function Write(
pv: Pointer; {要寫入的數據的指針}
cb: Longint; {要寫入的字節數}
pcbWritten: PLongint {實際寫入的字節數}
): HResult; stdcall;
//移動指針
function Seek(
dlibMove: Largeint; {要移動的字節數}
dwOrigin: Longint; {指定移動的起點, 三種取值分別是: 開始、當前、結尾}
out libNewPosition: Largeint {返回新位置指針}
): HResult; stdcall;
//dwOrigin 可選值:
STREAM_SEEK_SET = 0; {開始}
STREAM_SEEK_CUR = 1; {當前}
STREAM_SEEK_END = 2; {結尾}
//更改流對象的大小
function SetSize(
libNewSize: Largeint {指定新的大小, 以字節為單位}
): HResult; stdcall;
//復制部分數據到另一個 IStream
function CopyTo(
stm: IStream; {目標 IStream}
cb: Largeint; {要復制的字節數}
out cbRead: Largeint; {從源中實際讀出的字節數}
out cbWritten: Largeint {向目標實際寫入的字節數}
): HResult; stdcall;
//提交更改, 確保更改後的流能反映在父級存儲中
function Commit(
grfCommitFlags: Longint {提交方式, 四個可選值見下面}
): HResult; stdcall;
//grfCommitFlags 可選值:
STGC_DEFAULT = 0;
STGC_OVERWRITE = 1;
STGC_ONLYIFCURRENT = 2;
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4;
//放棄自從上次 Commit 調用以來對事務處理流所做的所有更改
function Revert: HResult; stdcall;
//限制對流中指定字節范圍的訪問
function LockRegion(
libOffset: Largeint; {起始字節(從頭算)}
cb: Largeint; {范圍長度, 以字節為單位}
dwLockType: Longint {限制類型, 可選值附下面}
): HResult; stdcall;
//dwLockType 可選值:
LOCK_WRITE = 1;
LOCK_EXCLUSIVE = 2;
LOCK_ONLYONCE = 4;
//移除用 LockRegion 設定的字節范圍的訪問限制, 參數同 LockRegion
function UnlockRegion(
libOffset: Largeint; {}
cb: Largeint; {}
dwLockType: Longint {}
): HResult; stdcall;
//返回一個 TStatStg 結構, 此結構包含該 IStream 詳細信息, 結構框架附下
function Stat(
out statstg: TStatStg; {TStatStg 結構變量}
grfStatFlag: Longint {選項, 此值可決定是否返回成員值, 見下}
): HResult; stdcall;
//TStatStg 結構:
tagSTATSTG = record
pwcsName: POleStr;
dwType: Longint;
cbSize: Largeint;
mtime: TFileTime;
ctime: TFileTime;
atime: TFileTime;
grfMode: Longint;
grfLocksSupported: Longint;
clsid: TCLSID;
grfStateBits: Longint;
reserved: Longint;
end;
TStatStg = tagSTATSTG;
//grfStatFlag 可選值:
STATFLAG_DEFAULT = 0;
STATFLAG_NONAME = 1;
//再制一個與指定 IStream 相同的副本
function Clone(
out stm: IStream {指定 IStream}
): HResult; stdcall;
IEnumStatStg 中的函數:
//檢索枚舉序列中指定數目的項
function Next(
celt: Longint; {}
out elt; {}
pceltFetched: PLongint {}
): HResult; stdcall;
//跳過枚舉序列中指定數目的項
function Skip(
celt: Longint {枚舉中要跳過的元素數目}
): HResult; stdcall;
//將枚舉序列重置到開始處
function Reset: HResult; stdcall;
//再制一個相同的 IEnumStatStg
function Clone(
out enm: IEnumStatStg {}
): HResult; stdcall;
相關的函數還有:
//創建一個復合文檔, 並通過參數返回 IStorage 接口
function StgCreateDocfile(
pwcsName: POleStr; {指定文件名}
grfMode: Longint; {指定訪問模式}
reserved: Longint; {保留, 須是 0}
out stgOpen: IStorage {返回 IStorage 接口}
): HResult; stdcall;
//打開一個復合文檔, 並通過參數返回 IStorage 接口
function StgOpenStorage(
pwcsName: POleStr; {指定文件名}
stgPriority: IStorage; {已存在的 IStorage 接口, 一般為 nil}
grfMode: Longint; {指定訪問模式}
snbExclude: TSNB; {是一個 POleStr(雙字節字符串)類型的指針, 一般為 nil}
reserved: Longint; {保留, 須是 0}
out stgOpen: IStorage {返回 IStorage 接口}
): HResult; stdcall;
//判斷指定文件是否是按照結構化方式存儲的
function StgIsStorageFile(
pwcsName: POleStr {文件名}
): HResult; stdcall;
//
function StgCreateDocfileOnILockBytes(
lkbyt: ILockBytes; {}
grfMode: Longint; {}
reserved: Longint; {}
out stgOpen: IStorage {}
): HResult; stdcall;
//
function StgOpenStorageOnILockBytes(
lkbyt: ILockBytes; {}
stgPriority: IStorage; {}
grfMode: Longint; {}
snbExclude: TSNB; {}
reserved: Longint; {}
out stgOpen: IStorage {}
): HResult; stdcall;
//
function StgIsStorageILockBytes(
lkbyt: ILockBytes {}
): HResult; stdcall;
//
function StgSetTimes(
pszName: POleStr; {}
const ctime: TFileTime; {}
const atime: TFileTime; {}
const mtime: TFileTime {}
): HResult; stdcall;
//
function StgOpenAsyncDocfileOnIFillLockBytes(
flb: IFillLockBytes; {}
grfMode, asyncFlags: Longint; {}
var stgOpen: IStorage {}
): HResult; stdcall;
//
function StgGetIFillLockBytesOnILockBytes(
ilb: ILockBytes; {}
var flb: IFillLockBytes {}
): HResult; stdcall;
//
function StgGetIFillLockBytesOnFile(
pwcsName: POleStr; {}
var flb: IFillLockBytes {}
): HResult; stdcall;
//
function StgOpenLayoutDocfile(
pwcsDfName: POleStr; {}
grfMode, reserved: Longint; {}
var stgOpen: IStorage {}
): HResult; stdcall;
//讀出 WriteClassStg 寫入的 CLSID, 相當於簡化調用 IStorage.Stat
function ReadClassStg(
stg: IStorage; {}
out clsid: TCLSID {}
): HResult; stdcall;
//寫 CLSID 到存儲中, 同IStorage.SetClass
function WriteClassStg(
stg: IStorage; {}
const clsid: TIID {}
): HResult; stdcall;
//讀出 WriteClassStm 寫入的 CLSID
function ReadClassStm(
stm: IStream; {}
out clsid: TCLSID {}
): HResult; stdcall;
//寫 CLSID 到流的開始位置
function WriteClassStm(
stm: IStream; {}
const clsid: TIID {}
): HResult; stdcall;
//寫入用戶指定的剪貼板格式和名稱到存儲中
function WriteFmtUserTypeStg(
stg: IStorage; {}
cf: TClipFormat; {}
pszUserType: POleStr {}
): HResult; stdcall;
//讀出 WriteFmtUserTypeStg 寫入的信息
function ReadFmtUserTypeStg(
stg: IStorage; {}
out cf: TClipFormat; {}
out pszUserType: POleStr {}
): HResult; stdcall;