当前位置: 首页 > news >正文

2507C++,结构化存储与复合文件

原文

结构化存储和复合文件

结构化存储是一个,抽象了COM接口背后的文件和目录主要是IStorageIStream的概念的窗口技术.它的主要目的是提供一个在单个物理文件中有文件系统层次的方法,这里.
这里
这里

结构化存储已有多年,其中最著名的用法是在办公文件,在使用扩展文件格式(*.docx,*.pptx等)之前的办公文件(*.doc,*.ppt,*.xls等).
当然,旧格式仍得到很大的支持.

结构化存储接口(IStorage表示目录,IStream表示文件)只是接口.要实际使用它们,必须要有一些实现.窗口提供了一个叫复合文件结构化存储实现,这里.

这些术语有时可互换使用,但区别很重要:复合文件只是结构化存储的一个实现,可能还有其他实现.复合文件并不是都基于定义的结构化存储,但它实现了很多,因此绝对够用.

可下载用图形方式查看使用复合文件实现创建的物理文件的内容的叫SSView的旧工具(但仍运行良好).
下面是一个更有趣的示例,使用Sysinternals自动运行工具保留的信息.
尽管它们都在一个文件中,更有趣的层次清晰可见!

主要接口

IStorage接口表示一个可包含其他目录和按IStream接口实现表示"文件""目录".要开始,可用StgCreateStorageEx创建物理文件,也可用StgOpenStorageEx打开现有文件.
这里
这里

成功时,两者都返回IStorage指针.从那里,可调用IStorage上的方法来创建或打开其他目录(存储)和/或文件(流).

IStorage上最有用的方法是CreateStorage,CreateStream,OpenStorageOpenStream.使用EnumElements可枚举存储/流.

以下是打开复合文件读取访问的示例,文件名来自命令行参数:

CComPtr<IStorage> spStg;
auto hr = ::StgOpenStorageEx(argv[1], STGM_READ | STGM_SHARE_EXCLUSIVE,STGFMT_STORAGE, 0, nullptr, nullptr, __uuidof(IStorage), reinterpret_cast<void**>(&spStg));
if (FAILED(hr)) {printf("Failed to open file (0x%X)\n", hr);return hr;
}

下面说明了如何递归枚举给定存储的层次:

void EnumItems(IStorage* stg, int indent = 0) {CComPtr<IEnumSTATSTG> spEnum;stg->EnumElements(0, nullptr, 0, &spEnum);if (spEnum == nullptr)return;STATSTG stat;while (S_OK == spEnum->Next(1, &stat, nullptr)) {if (indent)printf(std::string(indent, ' ').c_str());printf("%ws", stat.pwcsName);if (stat.type == STGTY_STORAGE) {printf(" [DIR]\n");CComPtr<IStorage> spSubStg;stg->OpenStorage(stat.pwcsName, nullptr,STGM_READ | STGM_SHARE_EXCLUSIVE, 0, 0, &spSubStg);if (spSubStg)EnumItems(spSubStg, indent + 1);}elseprintf(" (%u bytes)\n", stat.cbSize.LowPart);::CoTaskMemFree(stat.pwcsName);}
}

每一项都有一个名字,但流("文件")可有数据.STATSTGcbSize成员返回该大小.
流只是抽象一堆字节.要实际读取/写入流,需要先使用IStorage::OpenStream打开它,然后再使用IStream::Read,IStream::Write类似方法访问数据.

流的更多信息

窗口接口中到处使用IStream接口,而不仅是结构化存储的一部分.它代表了抽象了,理论上可以是任何地方缓冲,这就是抽象的好处.

给定IStream指针,你可读取,写入,查找,复制进另一个流,克隆,甚至只要实现支持,可提交/恢复事务.顺便,复合文件不支持流上的事务.

结构化存储之外,可通过多种方式取流.

CreateStreamOnHGlobalAPI在可选HGLOBAL上创建内存缓冲(可为无效以分配新缓冲),并返回该内存缓冲IStream指针.
这里

如,这在处理剪切板时很有用,因为它需要一个可能不方便使用的HGLOBAL.
通过取IStream指针,代码可用它(可能从另一个流中读取它,或手动填充数据),然后调用GetHGlobalFromStream以取底层HGLOBAL,然后再(如SetClipboardData)把它传递进剪切板.

只要可方便访问按IStream抽象的文件数据,还可调用SHCreateStreamOnFile直接取文件的流.
这里
活扩控件持久化中也使用IStream.

使用IStream另一例是,按COM对象状态信息"打包",这允许调用CoMarshalInterThreadInterfaceInStream(可能是名最长的COMAPI),从不同单元创建该对象的代理,该API(按IStream)抓要传递给另一个单元(A)要求的状态,如果需要,A可调用相应的CoGetInterfaceAndReleaseStream来生成原对象的代理.
这里
这里

案例研究:自动运行

早在2021年,当我在Sysinternals团队工作时,任务之一是从GUI的角度来现代化改造自动运行.我想我会借机重大重写,这样更容易维护该工具并按需改进.
这里

自动运行的功能之一是可保存工具提供的信息,这样以后可在不同机器上加载.这很难,因为某些信息如图标,不容易持久化.

我不记得旧的自动运行是否保留了它们,但我绝对想这样做.

旧的自动运行格式是顺序的,在文件中线性保存数据结构.要偏移更改需要添加的任何新属性,这会强制更改格式"版本",并在读取各种"旧"格式的文件时做出正确的决定.

我想让持久化更加灵活,所以我决定完全按复合文件更改格式.使用此方案,添加新属性不会导致任何问题,可添加新流,而不干扰其他流.

代码可忽略它不关心的属性(可能是存储和/或流).这使得该格式在定义上是可扩展的,不受任何偏移更改的影响,且可用(如SSView)工具,非常容易的查看.

顺便,持久化图标非常容易,因为可通过单个调用函数持久化,自动运行用来保存图标集合图片列表对象到流中:ImageList_Write;这非常方便!
这里

结论

结构化存储的想法非常强大,窗口提供的复合文件实现非常好且灵活.微软Office移动至新格式的原因之一是需要使文件更小,因此新的扩展格式ZIP压缩的.

它们的内部格式也有变化,且大部分时间都不使用复合文件.可压缩结构化存储文件,从而节省磁盘空间,同时仍可用存储和流方便访问.

http://www.dtcms.com/a/301061.html

相关文章:

  • JavaWeb(苍穹外卖)--学习笔记13(微信小程序开发,缓存菜品,Spring Cache)
  • epoll_event 事件类型详解
  • Python折线图
  • Spring 核心流程
  • 问津集 #2:High Compression and Fast Search on Semi-Structured Logs
  • 网络基础19:OSPF多区域实验
  • 小黑课堂计算机二级 WPS Office题库安装包2.52_Win中文_计算机二级考试_安装教程
  • C++算法竞赛篇(五)循环嵌套题型讲解
  • java开闭原则 open-closed principle
  • 商品中心—1.B端建品和C端缓存
  • 内网服务器实现从公网穿透
  • NVMe高速传输之摆脱XDMA设计16:队列管理模块设计(上)
  • Python 列表推导式与生成器表达式
  • 激光SLAM技术综述(2025版)
  • Python入门构建网页
  • Linux驱动20 --- FFMPEG视频API
  • 基于Django的天气数据可视化分析预测系统
  • Coze:字节跳动AI开发平台功能和架构解析
  • 第五章 中央处理器(CPU)知识体系与考法总结
  • 虚拟机ubuntu20.04共享安装文件夹
  • ubuntu 部署 coze-loop
  • C语言函数递归详解
  • 运行时长和内存优化:混合精度训练(MPT)案例和梯度检查点(GCP)
  • LWGJL教程(6)——GL20源码
  • Python初学OpenCV:图像预处理进阶指南(二)
  • 使用frp实现免费内网穿透
  • 【2025CVPR-扩散模型方向】TKG-DM:免训练的色度关键内容生成扩散模型
  • 区块链:工作量证明与联邦学习
  • ArkTS 模块通信全解析:用事件总线实现页面消息联动
  • rapidocr v3.3.0发布了