复合文件,是微软COM组件思想的起源。

一、其产生背景

文件的存储结构通常有三种格式:

1。非结构化文件:

如:打开记事本程序,输入了一篇文章后,保存所得的文件。

 2。标准结构化文件:

如:打开电子表格程序,输入一个班的学生姓名和考试成绩,保存所得的文件。

3。自定义结构化文件

在我们写的程序中,需要把特定的数据按照一定的结构和顺序写到文件中保存。比如 *.bmp 文件

以上三种类型的文件,大家都见的多了。那么文件存储就依靠上述的方式能满足所有的应用需求吗?恩~~~,至少从计算机发明后的50多年来,一直是够用的了。

下面看看商业利益的推动作用,对文件 的存储形式产生了什么变化吧。我估计以前都使用过以下几个著名的软件:WordStar(独霸DOS下的英文编辑软件),WPS(裘伯君写的中文编辑软件,据说当年的市场占有率高达90%,各种计算机培训班的必修课程),LOTUS-123(莲花公司出品的电子表格软件)......

微软在成功地推出 Windows 3.1 后,开始垂涎桌面办公自动化软件领域。微软的 OFFICE 开发部门,各小组分别独立地开发了 WORD 和 EXCEL 等软件,并采用“自定义结构”方式,对文件进行存储。在激烈的市场竞争下,为了打败竞争对手,微软自然地产生了一个念头------如果我能在 WORD 程序中嵌入 EXCEL,那么用户在购买了我 WORD 软件的情况下,不就没有必要再买 LOTUS-123 了吗?计划产生后,他们开始了实施工作,这就是 COM 的前身 OLE 的起源(注3)。但立刻就遇到了一个严重的技术问题:需要把 WORD 产生的 DOC 文件和 EXCEL 产生的 XLS 文件保存在一起。

方案

优点

缺点

建立一个子目录,把 DOC、XLS 存储在这同一个子目录中。

数据隔离性好,WORD 不用了解 EXCEL 的存储结构;容易扩展。

结构太松散,容易造成数据的损坏或丢失。
不易携带。

修改文件存储结构,在DOC结构基础上扩展出包容 XLS 的结构。

结构紧密,容易携带和统一管理。

WORD 的开发人员需要通晓 EXCEL 的存储格式;缺少扩展性,总不能新加一个类型就扩展一下结构吧?!

以上两个方案,都有严重的缺陷,怎么解决那?如果能有一个新方案,能够合并前两个方案的优点,消灭缺点,该多好呀......微软是作磁盘操作系统起家的,于是很自然地他们提出了一个非常完美的设计方案,那就是把磁盘文件的管理方式移植到文件中了------复合文件,俗称“文件中的文件系统”。连微软当年都没有想到,就这么一个简单的想法,居然最后就演变出了 COM 组件程序设计的方法。可以说,复合文件是 COM 的基石。下图是磁盘文件组织方式与复合文件组织方式的类比图:

图一、左侧表示一个磁盘下的文件组织方式,右侧表示一个复合文件内部的数据组织方式。

二、复合文件的特点

  1. 复合文件的内部是使用指针构造的一棵树进行管理的。
  2. 复合文件中的“流对象”,是真正保存数据的空间。
  3. 不同的进程,或同一个进程的不同线程可以同时访问一个复合文件的不同部分而互不干扰;

三、复合文件的编码实现

为了更好理解复合文档结构,下面程序片段,演示了建立一个复合文件,并在其下建立一个子存储,在该子存储中再建立一个流,写入数据。

void SampleCreateDoc()
{       ::CoInitialize(NULL);  // COM 初始化
                           // 如果是MFC程序,可以使用AfxOleInit()替代
       HRESULT hr;            // 函数执行返回值
        IStorage *pStg = NULL; // 根存储接口指针
        IStorage *pSub = NULL; // 子存储接口指针
        IStream *pStm = NULL;  // 流接口指针
        hr = ::StgCreateDocfile(       // 建立复合文件
               L"c://a.stg",  // 文件名称
               STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,      // 打开方式
               0,             // 保留参数
               &pStg);        // 取得根存储接口指针
        ASSERT( SUCCEEDED(hr) );       // 为了突出重点,简化程序结构,所以使用了断言。
                               // 在实际的程序中则要使用条件判断和异常处理
        hr = pStg->CreateStorage(      // 建立子存储
               L"SubStg",     // 子存储名称
               STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
               0,0,
               &pSub);        // 取得子存储接口指针
        ASSERT( SUCCEEDED(hr) );
        hr = pSub->CreateStream(       // 建立流
               L"Stm",        // 流名称
               STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
               0,0,
               &pStm);        // 取得流接口指针
        ASSERT( SUCCEEDED(hr) );
        hr = pStm->Write(              // 向流中写入数据
               "Hello",               // 数据地址
               5,             // 字节长度(注意,没有写入字符串结尾的/0)
               NULL);         // 不需要得到实际写入的字节长度
        ASSERT( SUCCEEDED(hr) );
        if( pStm )     pStm->Release();// 释放流指针
        if( pSub )     pSub->Release();// 释放子存储指针
        if( pStg )     pStg->Release();// 释放根存储指针
        ::CoUninitialize()             // COM 释放
                               // 如果使用 AfxOleInit(),则不调用该函数
}

图二、运行示例程序一后,使用 DFView.exe 打开观察复合文件的效果图

示例二:打开一个复合文件,枚举其根存储下的所有对象。

#include <atlconv.h>   // ANSI、MBCS、UNICODE 转换
void SampleEnum() 
{       // 假设你已经做过 COM 初始化了
        LPCTSTR lpFileName = _T( "c://a.stg" );
        HRESULT hr;
        IStorage *pStg = NULL;
        USES_CONVERSION;                              // (注6)
        LPCOLESTR lpwFileName = T2COLE( lpFileName ); // 转换T类型为宽字符
        hr = ::StgIsStorageFile( lpwFileName );       // 是复合文件吗?
        if( FAILED(hr) )       return;
        hr = ::StgOpenStorage(                // 打开复合文件
               lpwFileName,                   // 文件名称
               NULL,
               STGM_READ | STGM_SHARE_DENY_WRITE,
               0,
               0,
               &pStg);                        // 得到根存储接口指针
        IEnumSTATSTG *pEnum=NULL;      // 枚举器
        hr = pStg->EnumElements( 0, NULL, 0, &pEnum );
        ASSERT( SUCCEEDED(hr) );
        STATSTG statstg;
        while( NOERROR == pEnum->Next( 1, &statstg, NULL) )
        {
               // statstg.type 保存着对象类型 STGTY_STREAM 或 STGTY_STORAGE
               // statstg.pwcsName 保存着对象名称
               // ...... 还有时间,长度等很多信息。请查看 MSDN
               ::CoTaskMemFree( statstg.pwcsName );  // 释放名称所使用的内存(注6)
        }
        if( pEnum )    pEnum->Release();
        if( pStg )     pStg->Release();
}

四、小结

  复合文件,结构化存储,是微软组件思想的起源,在此基础上继续发展出了持续性、命名、ActiveX、对象嵌入、现场激活......一系列的新技术、新概念。因此理解和掌握 复合文件是非常重要的,即使在你的程序中并没有全面使用组件技术,复合文件技术也是可以单独被应用的。

注:可以用 DFView.exe 打开 MSWORD 的 DOC 文件进行复合文件的浏览。但是该程序并没有实现国际化,不能打开中文文件名的复合文件,因此需要改名后才能浏览。

说明:(该文章,来自网上,经我整理得)

OLE技术专题——第二讲:复合文件相关推荐

  1. OLE技术专题——第一讲:OLE概述

    引言-概述 OLE/ActiveX/COM技术是MS的核心应用技术,只有彻底洞察其理论精髓,才能以不变应万变. 我们首先从OLE谈起. 一.过去的OLE和今天的OLE 作为COM技术前身的OLE,其最 ...

  2. 青岛科技大学|物联网工程|物联网定位技术(第二讲)|15:00

    目录 物联网定位技术(第二讲) 1. 卫星的轨道高度与覆盖区域有何关系,试画图给予说明覆盖区地心角与覆盖面积的关系 2. 试给出实际的卫星地面覆盖区和用户空间可视区所对应的半地心角的公式并请给予解释 ...

  3. 智能智造技术理论 第二讲 智能制造定义与现状

    智能制造定义与现状 1.四次工业革命历史 2.智能制造的定义与特征 3.中国制造2025主要内容 4.中国企业智能制造现状 四次工业革命历史: 第一次工业革命:18世纪中期-19世纪,蒸汽机革命 技术 ...

  4. 无线网络技术导论笔记(第二讲)

    无线网络技术导论 主讲教师:张亮老师 第二讲 无线传输技术基础 https://blog.csdn.net/Wjwstruggle/article/details/90757351 目录 无线网络技术 ...

  5. Windows中的“OLE”技术,是什么技术,它可以实现多个文件之间的住处传递和共享...

    我看的不太懂这个东西但是看哪个mfc初始化的时候有调用这个某个函数初始化这个东西所以百度了下看不懂.然后也把转载的放上来了. Windows中的"OLE"技术,是什么技术,它可以实 ...

  6. 【阿里聚安全·安全周刊】阿里双11技术十二讲直播预约|AWS S3配置错误曝光NSA陆军机密文件

    原文链接:点击打开链接 摘要: 关键词:阿里双11技术十二讲直播丨雪人计划丨亚马逊AWS S3配置错误丨2018威胁预测丨MacOS漏洞丨智能风控平台MTEE3丨黑客窃取<权利的游戏>剧本 ...

  7. 【阿里聚安全·安全周刊】阿里双11技术十二讲直播预约|AWS S3配置错误曝光NSA陆军机密文件...

    关键词:阿里双11技术十二讲直播丨雪人计划丨亚马逊AWS S3配置错误丨2018威胁预测丨MacOS漏洞丨智能风控平台MTEE3丨黑客窃取<权利的游戏>剧本|Android 8.1   本 ...

  8. 第二讲 命令源码文件

    此为 <极客时间&Go语言核心36讲> 个人笔记,具体课程详见极客时间官网. Table of Contents generated with DocToc 第二讲 命令源码文件 ...

  9. 求两条轨迹间的hausdorff距离_「中考专题」瓜豆原理|第二讲 线段型路径轨迹...

    第二讲 线段型路径轨迹 上一讲: [中考专题]瓜豆原理|第一讲 什么是瓜豆原理? 例1: 如图,等边ΔABC的边长为4,点D是边AC上的一个动点,连接BD,以BD为斜边向上作RtΔBDE,其中∠DBE ...

最新文章

  1. 牛客小白月赛6 水题 求n!在m进制下末尾0的个数 数论
  2. “MIDI机器狗”的木马正在疯狂传播
  3. STM32F4 HAL库开发 -- GPIO
  4. mysql group by 集合_MySQL高级查询之与Group By集合使用介绍
  5. android 8 esp8266,微信硬件平台(八) 1 esp8266从自己的服务器获取token
  6. 参数控制c语言代码走向,C语言可变参数完全解读
  7. nginx配置文件中location与root访问时的联系
  8. C#中Equals和==的比较
  9. Gh0st源码学习(二)去除硬盘锁和驱动
  10. ASP.NET Core Razor Pages
  11. ASP.NET生成WORD文档服务器部署注意事项
  12. 14.深度学习练习:Face Recognition for the Happy House
  13. 【ECS最佳实践】基于多块云盘构建LVM逻辑卷
  14. 安装ceston8出现timeout_瓦罗兰特Valorant:显示逾时怎么办 瓦罗兰特time out解决
  15. 通云之路 从虚拟化迈向企业私有云
  16. SoapUI、Jmeter、Postman三种接口测试工具的比较分析
  17. 【IoT】产品模型:基于 ARM 的音视频采集与传输系统
  18. 爱立信面试前的GitHub项目如何上传
  19. 基于SSM小说阅读网站设计带爬虫功能
  20. 如何获取Mysql的根目录

热门文章

  1. 《黑色沙漠》游戏系统拆解
  2. 安装编译好的Android镜像到模拟器上 (android 7.0)
  3. linux怎么退出tail命令,Linux系统tail命令怎么使用
  4. mysql锁(全局锁、表锁、行锁、页锁、排他锁、共享锁)
  5. 使用无标注的数据训练Bert
  6. ibm服务器无显示器,[维修经验]图解IBM 17液晶显示器黑屏的维修
  7. 基于内容推荐python_用 Python 实现一个简单的基于内容的推荐引擎
  8. 开源移动护理_开源让您的健康护理倍感美好
  9. 计算机与护理信息学,迎接信息时代的挑战——护理信息学的兴起与发展
  10. 树莓派+lora_gateway