zoukankan      html  css  js  c++  java
  • 复杂的结构化存取(一)

    之所以说复杂, 就是区别与以前谈到的 结构化文件存取; 这种复杂的结构化文件也有叫做"复合文档".

    有些文档不是结构化的, 譬如记事本文件; 结构化的档可以分为以下几类:
    标准结构化文档、自定义结构化文档(譬如 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;
    
  • 相关阅读:
    如何在一个项目中同时包含mvc建站、webapi接口
    解决api、WebService跨域问题
    mvc接口、webapi、webservice 对比
    云服务器 远程mysql 无法连接
    c#快速写本地日志
    使用筛选器特性标记方法解决webapi 跨域问题
    流量控制(滑动窗口协议)
    解释Windows7“上帝模式”的原理
    Linux网络协议栈(二)——套接字缓存(socket buffer)
    理解MySQL——架构与概念
  • 原文地址:https://www.cnblogs.com/del/p/1252343.html
Copyright © 2011-2022 走看看