有些文档不是结构化的, 譬如记事本文件; 结构化的档可以分为以下几类:
标准结构化文档、自定义结构化文档(譬如 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;