COM结构化存储

**COM 結構化存儲**（也稱「OLE 結構化存儲」）是微軟在 Windows 作業系統上開發的一種技術，以在一個文件內存儲層次化的數據。嚴格講，術語「structured storage」是指 COM 的一套接口。並提供了有限形式的原子事務數據訪問。

結構化存儲廣泛用於 Microsoft Office 應用程式。但從 Office 2007 開始默認使用基於 XML 的 Office Open XML。SQL Server 也使用結構化存儲。

## 概述

結構化存儲是為了解決在一個文件中存放多個數據對象的本質性困難。當修改一個對象從而改變其尺寸時，如果這些對象在文件內是順序存放的，則被修改對象的尺寸擴大時所有在其後的對象都需要向後移動以騰出空間，被修改對象縮小尺寸時所有在其後的對象都需要向前移動。這樣的困難有很多種辦法應對，但應用程式開發者並不希望處理這樣低層次的二進位文件格式問題。

結構化存儲把文件系統的概念引入到文件內部。在文件內部構造一個樹狀層次結構。由存儲對象和流對象構成。根節點為根存儲，根存儲下面可以有子存儲或流對象。子存儲下面可以再有子存儲或流對象。存儲對象本身不包含信息，它是子存儲和流對象的容器。流對象是葉子節點，是數據的載體。

## COM 接口

### 簡介

結構化存儲提供了一種抽象，稱為「流」（stream），用 COM 接口 `IStream` 表示。一個流在概念上類似一個文件，使用 `IStream` 接口的方法來讀寫。流可以存在於內存中、文件中，或其它流中。另外一個重要抽象是「storage」，表示為 COM 接口 `IStorage`。一個 storage 在概念上類似於文件系統的目錄。Storage 可以包含流或其他 storages。

一個應用程式如果要把幾個數據對象保存到一個文件中，一種辦法是打開一個 `IStorage` 以表示文件，然後把 每個對象存入單獨的 `IStream`（這可通過標準 COM 接口 `IPersistStream`）。

結構化存儲的事務機制，針對寫模式而不是讀模式。創建、打開存儲和流時可以指定事務模式。只有 Commit 後，所有的變化才提交到上級。revert 可以恢復到上次 commit 以來的狀態。事務的實現是在內存中或臨時文件中保存文件拷貝的方式。事務可以嵌套。

增量訪問，可以減少保存文件的操作時間。對於文件碎片，調用根存儲 CopyTo 解決。

### IStream 接口

```
  class IStream::public IUnknown 
```

方法：

```
   Read
   Write
   Seek
   CopyTo
   Commit 把所有变化提交到包容该流对象的存储对象。
   Revert   rollback取消上次提交以来的所有操作。针对事务模式的流
```

### IStorage 接口

```
   class IStorage：public IUnknown  
   CreateStream 
   OpenStream 操作子流对象，返回IStream接口指针
   CreateStorage 
   OpenStorage 操作子存储对象，返回IStorage接口指针
   EnumElements 枚举所有的子流对象和子存储对象。
   commit
   Revert。针对事务模式的存储。
   Stat 获取存储对象的CLSID标识符
   SetClass 为存储对象赋一个CLSID标识符，并可通过Stat函数获取此CLSID值。通过此CLSID值把存储对象与一段可执行代码联系起来，当客户程序希望执行与存储对象相联系的代码时，它利用CLSID值，并调用 CoCreateInstamce函数创建一个COM对象，再把存储对象交给COM对象，由它处理存储对象。 
```

### Windows API 函數用於 CLSID 有關操作

* WriteClassStg 和 ReadClassStg 函數封裝了 IStorage::SetClass 和 IStorage::Stat 成員函數，可以完成存儲對象的 CLSID 的設置和獲取操作。存儲對象只是個容器，它本身不包含數據信息，所以它的 CLSID 信息被寫在其下面的一個子流對象中，其名字為「\x01CompObj」。
* WriteClassStm 和 ReadClassStm 函數使用一致的格式在流對象的當前位置分別寫或者讀 CLSID 信息。通常情況下，在流的起始處放置 CLSID 信息。
* GetClassFile 函數返回一個與給定文件相聯繫的 CLSID。Windows 利用 CLSID 把數據文件與應用程式聯繫起來。

## 複合二進位文檔

Microsoft 實現的一種稱為複合二進位文檔（Compound File Binary Format，簡稱「複合文檔」）的 COM 結構化存儲，廣泛用於結構化存儲實現。使用類似於 FAT 的結構表示存儲與流。文件片（chunk）被稱為扇區（sector）按需分配給新的流或增加尺寸的流。如果流被刪除或收縮尺寸，不再需要的扇區可以重新被其他流使用。

使用複合二進位文檔的應用程式有：

* Microsoft Office 97–2003 文檔：
  * Word 文檔 (.DOC, .DOT)
  * Excel spreadsheets (.XLS, .XLT)
  * PowerPoint presentations (.PPT, .POT)
  * Publisher 文件 (.PUB)
  * Visio 文件 (.VSD)
  * Project 文件 (.MPP)
  * Microsoft PhotoDraw 文件 (.MIX)
  * Microsoft Outlook 文件 (.MSG)
* Windows Installer 文件 (.MSI, .MSP, .MST)
* Microsoft Picture It! / Microsoft Digital Image 文件 (.MIX)
* Internet Explorer RSS Feeds Windows RSS Platform 文件 (.feed-ms)
* Windows 7 StickyNotes (.SNT)
* Windows 7 jumplists 文件
* Thumbs.db
* Microsoft SQL 2000 Server DTS packages

### LockBytes 對象

LockBytes 對象是所有存儲介質的一種抽象。存儲介質描述稱為一般化的字節流，LockBytes 對象負責從字節流中讀寫字節。複合文檔通過一個被稱為「LockBytes」的對象，把根存儲與底層的存儲介質聯繫起來，其他的子對象則通過根存儲與底層存儲介質進行數據通信，從而實現了整個結構化存儲體系結構。複合文檔通過 LockBytes 對象把根存儲對象與底層的文件操作隔離開來，在訪問存儲對象或者流對象時避開了文件句柄操作。

LockBytes 對象實現了 ILockBytes 接口以提供基本的讀寫操作。COM 庫提供了預設的基於文件句柄的 LockBytes 對象，以及基於內存的 LockBytes 對象。

例如，可按照如下步驟創建基於內存的複合文檔。

1. GlobalAlloc () 獲得一個內存句柄
2. CreateILockBytesOnHGlobal () 創建一個 LockBytes 對象。這步類似於 GlobleLock ()
3. StgCreateDocfileOnILockBytes () 創建一個基於指定的 LockBytes 對象的複合文檔

其他方法：

* 使用 StgCreateDocfile () 創建一個基於磁碟文件的複合文檔。
* StgCreateDocfileOnLockBytes()
* 使用 StgOpenStorage () 打開一個基於磁碟文件的複合文檔
* 使用 StgOpenStorageOnILockBytes () 打開一個基於內存的複合文檔
* GetHGlobalFromILockBytes
* CreateStreamOnHGlobal
* GetHGlobalFromStream
* StgIsStorageFile
* StgSetTimes

### 永久對象

概念上，永久對象與存儲對象和流對象沒有聯繫。永久對象可以把狀態信息存儲下來，以後可以重建這樣的對象，且恢復到以前的狀態。可以很方便地使用 COM 的結構化存儲機制或別的機制（如磁碟文件，系統註冊表，目錄服務，資料庫服務等）來保存永久對象的狀態。

COM 定義了四個常用的永久接口：IPersistStorage, IPersistStream, IPersistStreamInit 和 IPersistFile。前三個接口使用的結構化存儲機制。後一個接口直接使用文件。接口都包含三個基本函數

* IsDirty：反映了自上次保存後是否被修改
* Load：從存儲介質中裝入永久對象的狀態數據
* Save：把永久對象的狀態數據保存到存儲介質中

## 類似功能產品

* Microsoft .NET:
  * GemBox.CompoundFile（頁面存檔備份，存於網際網路檔案館） – .NET component for accessing structured storage files.
  * OpenMCDF（頁面存檔備份，存於網際網路檔案館） – Free .NET component for accessing OLE structured storage files, MPL licensed.
* Linux:
  * GNOME Structured File Library（頁面存檔備份，存於網際網路檔案館） – Can read Microsoft structured storage files.
  * POLE.
* 跨平台 C++ 用於 Window/MacOSX/Linux:
  * POLE v3 and up（頁面存檔備份，存於網際網路檔案館）.
* Java:
  * POIFS – Java implementation of the OLE 2 Compound Document format, part of Apache POI.
* Perl:
  * LAOLA Binary Structures

## 參考文獻

## 外部連結

* 官方網站
* Open Specifications: Compound File Binary File Format（頁面存檔備份，存於網際網路檔案館）