首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

復雜結構化存取(一):Windows 復合文檔/結構化存儲 COM 接口全景指南

     編輯:Delphi

什麼是復合文檔?

當我們需要存儲的數據不再是扁平的字節流,而是具有層級嵌套、類型異構、並發訪問特徵時,傳統文件與關係數據庫都力不從心。Windows 通過 COM 提供的結構化存儲技術,在單個物理文件內構建出類似「文件系統」的邏輯結構——這就是復合文檔。

經典類比:文件系統在文件裡

文件系統概念結構化存儲對應概念COM 接口
目錄/資料夾存儲對象IStorage
文件流對象IStream
目錄枚舉存儲/流枚舉IEnumStatStg

一個復合文檔內部可以無限層級嵌套:Storage 包含 Sub-Storages 和 Streams,每個 Stream 至少佔用 512 字節(扇區大小),支持事務提交/回滾、多線程/多進程並發訪問不同部分、增量保存。

核心三大接口總覽

  1. IStorage:容器節點,對應「目錄」,管理子存儲與流的創建、打開、刪除、重命名、屬性、提交、枚舉。
  2. IStream:數據葉子節點,對應「文件」,提供 Read/Write/Seek/SetSize/CopyTo/LockRegion/Clone 等流操作。
  3. IEnumStatStg:枚舉器,遍歷 Storage 內的所有元素,獲取 STATSTG 統計信息。

關鍵 API 函數完整簽名參考

以下為 ActiveX.pas 中定義的核心接口方法簽名,供開發時速查。

IStorage 方法

{ 創建子存儲 }
function CreateStorage(
  pwcsName: POleStr; grfMode: Longint; dwStgFmt: Longint; reserved2: Longint;
  out stg: IStorage): HResult; stdcall;

{ 打開現有子存儲 }
function OpenStorage(
  pwcsName: POleStr; const stgPriority: IStorage; grfMode: Longint;
  snbExclude: TSNB; reserved: Longint; out stg: IStorage): HResult; stdcall;

{ 創建子流 }
function CreateStream(
  pwcsName: POleStr; grfMode: Longint; reserved1, reserved2: Longint;
  out stm: IStream): HResult; stdcall;

{ 打開現有子流 }
function OpenStream(
  pwcsName: POleStr; reserved1: Pointer; grfMode: Longint; reserved2: Longint;
  out stm: IStream): HResult; stdcall;

{ 複製整個存儲樹(可用於「整理碎片」) }
function CopyTo(
  ciidExclude: Longint; rgiidExclude: PIID; snbExclude: TSNB;
  const stgDest: IStorage): HResult; stdcall;

{ 移動/複製單個元素 }
function MoveElementTo(
  pwcsName: POleStr; const stgDest: IStorage; pwcsNewName: POleStr;
  grfFlags: Longint): HResult; stdcall;  { STGMOVE_MOVE / STGMOVE_COPY }

{ 事務提交 }
function Commit(grfCommitFlags: Longint): HResult; stdcall;
  { STGC_DEFAULT=0, STGC_OVERWRITE=1, STGC_ONLYIFCURRENT=2, STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE=4 }

{ 事務回滾 }
function Revert: HResult; stdcall;

{ 獲取枚舉器 }
function EnumElements(
  reserved1: Longint; reserved2: Pointer; reserved3: Longint;
  out enm: IEnumStatStg): HResult; stdcall;

{ 刪除/重命名/設置時間/設置 CLSID/狀態位/統計信息 }
function DestroyElement(pwcsName: POleStr): HResult; stdcall;
function RenameElement(pwcsOldName, pwcsNewName: POleStr): HResult; stdcall;
function SetElementTimes(pwcsName: POleStr; const ctime, atime, mtime: TFileTime): HResult; stdcall;
function SetClass(const clsid: TCLSID): HResult; stdcall;
function SetStateBits(grfStateBits, grfMask: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;

IStream 方法

function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
function Seek(dlibMove: Largeint; dwOrigin: Longint; out libNewPosition: Largeint): HResult; stdcall;
  { STREAM_SEEK_SET=0, STREAM_SEEK_CUR=1, STREAM_SEEK_END=2 }
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead, cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset, cb: Largeint; dwLockType: Longint): HResult; stdcall;
  { LOCK_WRITE=1, LOCK_EXCLUSIVE=2, LOCK_ONLYONCE=4 }
function UnlockRegion(libOffset, cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;

IEnumStatStg 方法

function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
function Skip(celt: Longint): HResult; stdcall;
function Reset: HResult; stdcall;
function Clone(out enm: IEnumStatStg): HResult; stdcall;

全局輔助函數

function StgCreateDocfile(pwcsName: POleStr; grfMode: Longint; reserved: Longint; out stgOpen: IStorage): HResult; stdcall;
function StgOpenStorage(pwcsName: POleStr; stgPriority: IStorage; grfMode: Longint; snbExclude: TSNB; reserved: Longint; out stgOpen: IStorage): HResult; stdcall;
function StgIsStorageFile(pwcsName: POleStr): HResult; stdcall;  { 判斷文件是否為復合文檔 }
{ ... 更多 LockBytes 相關函數 ... }
function ReadClassStg(stg: IStorage; out clsid: TCLSID): HResult; stdcall;
function WriteClassStg(stg: IStorage; const clsid: TIID): HResult; stdcall;
function ReadClassStm(stm: IStream; out clsid: TCLSID): HResult; stdcall;
function WriteClassStm(stm: IStream; const clsid: TIID): HResult; stdcall;

STATSTG 結構定義

tagSTATSTG = record
  pwcsName: POleStr;      { 元素名稱 }
  dwType: Longint;        { 類型:STGTY_STORAGE=1, STGTY_STREAM=2, STGTY_LOCKBYTES=3 }
  cbSize: Largeint;       { 流大小 / 存儲大小 }
  mtime: TFileTime;       { 修改時間 }
  ctime: TFileTime;       { 創建時間 }
  atime: TFileTime;       { 訪問時間 }
  grfMode: Longint;       { 訪問模式標志 }
  grfLocksSupported: Longint; { 支持的鎖類型 }
  clsid: TCLSID;          { 關聯的類 ID }
  grfStateBits: Longint;  { 用戶狀態位 }
  reserved: Longint;
end;
TStatStg = tagSTATSTG;

為什麼選擇結構化存儲?

  • OLE 嵌入標準:Word 文檔嵌入 Excel 表格、Visio 圖形,底層均為復合文檔。
  • 自定義複雜文件格式:如 CAD 圖紙(幾何流 + 屬性存儲 + 縮略圖流 + 版本歷史存儲),單文件分發,無需目錄管理。
  • 事務性:Commit/Revert 保證寫入原子性,適合關鍵配置文件。
  • 並發:不同線程可同時讀寫不同 Stream,無需應用層鎖。

Delphi 開發入門建議

  1. 引用 uses ActiveX, ComObj;
  2. 使用 StgCreateDocfile 創建根 IStorage,grfMode 常用 STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_CREATE。
  3. 所有接口調用檢查 HResult,失敗拋出 OleCheck 異常。
  4. 接口變量離開作用域自動 _Release,無需手動管理。
  5. 大流數據讀寫建議使用 TStreamAdapter 適配為 TStream,復用 TFileStream/TMemoryStream 邏輯。

結語

結構化存儲是 Windows 系統級能力的結晶,雖然 API 風格屬於 COM 經典時代,但其解決「單文件多對象、層級結構、事務並發」的架構思想至今不過時。掌握 IStorage/IStream/IEnumStatStg 三大接口與 STATSTG 結構,即可在 Delphi 中輕鬆實現專業級復合文檔讀寫。下一篇將展示具體的讀寫代碼實戰與性能優化技巧。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved