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