COM组件设计与应用学习1-------复合文档

      最近由于项目的需要 涉及到了 COM技术,所以就进行了以下学习 ,看关于COM方面的书籍不是很多,于是我便从网上学习,本来对COM感觉很神秘,等当真正接触的时候发现也不是那么难,以上扯淡中......兴趣就是动力嘛,本人比较懒,文中部分内容直接COPY原文 作者体谅。。

     还有COM中都是以接口的形式提供的,Java中大家都学过接口  ,还有C++虚函数中大家都学过 其实都差不多 ,只是COM有自己的内存分配方式而已 。

 COM——到底是什么

  简单地说,COM是一种跨应用和语言共享二进制代码的方法。与C++不同,它提倡源代码重用。ATL便是一个很好的例证。源码级重用虽然好,但只能用于C++。它还带来了名字冲突的可能性,更不用说不断拷贝重用代码而导致工程膨胀和臃肿。
  Windows使用DLLs在二进制级共享代码。这也是Windows程序运行的关键——重用kernel32.dll, user32.dll等。但DLLs是针对C接口而写的,它们只能被C或理解C调用规范的语言使用。由编程语言来负责实现共享代码,而不是由DLLs本身。这样的话DLLs的使用受到限制。
MFC引入了另外一种MFC扩展DLLs二进制共享机制。但它的使用仍受限制——只能在MFC程序中使用。
  COM通过定义二进制标准解决了这些问题,即COM明确指出二进制模块(DLLs和EXEs)必须被编译成与指定的结构匹配。这个标准也确切规定了在内存中如何组织COM对象。COM定义的二进制标准还必须独立于任何编程语言(如C++中的命名修饰)。一旦满足了这些条件,就可以轻松地从任何编程语言中存取这些模块。由编译器负责所产生的二进制代码与标准兼容。这样使后来的人就能更容易地使用这些二进制代码。
  在内存中,COM对象的这种标准形式在C++虚函数中偶尔用到,所以这就是为什么许多COM代码使用C++的原因。但是记住,编写模块所用的语言是无关的,因为结果二进制代码为所有语言可用。
  此外,COM不是Win32特有的。从理论上讲,它可以被移植到Unix或其它操作系统。但是我好像还从来没有在Windows以外的地方听说过COM。

     据说COM最初的起源的微软的复合文档,关于什么是复合文档呢?  大家都用过WORD,知道这个文件可以包含图片 图标等等资源信息 ,很方便使用。但是对于开发的微软来说,

 COM最初只是为了 在word中嵌入 EXCEL等。那么现在出现了一个问题? 如何在word中引入Excel呢 ?

下面是两种方法 :(此处从作者那里拷贝的)


方案


优点


缺点

建立一个子目录,把 DOC、XLS 存储在这同一个子目录中。 数据隔离性好,WORD 不用了解 EXCEL 的存储结构;容易扩展。 结构太松散,容易造成数据的损坏或丢失。
不易携带。
修改文件存储结构,在DOC结构基础上扩展出包容 XLS 的结构。 结构紧密,容易携带和统一管理。 WORD 的开发人员需要通晓 EXCEL 的存储格式;缺少扩展性,总不能新加一个类型就扩展一下结构吧?!

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

三、复合文件的特点

  1. 复合文件的内部是使用指针构造的一棵树进行管理的。编写程序的时候要注意,由于使用的是单向指针,因此当做定位操作的时候,向后定位比向前定位要快;
  2. 复合文件中的“流对象”,是真正保存数据的空间。它的存储单位为512字节。也就是说,即使你在流中只保存了一个字节的数据,它也要占据512字节的文件空间。啊~~~,这也太浪费了呀?不浪费!因为文件保存在磁盘上,即使一个字节也还要占用一个“簇”的空间那;
  3. 不同的进程,或同一个进程的不同线程可以同时访问一个复合文件的不同部分而互不干扰;
  4. 大家都有这样的体会,当需要往一个文件中插入一个字节的话,需要对整个文件进行操作,非常烦琐并且效率低下。而复合文件则提供了非常方便的“增量访问”能力;
  5. 当频繁地删除文件,复制文件后,磁盘空间会变的很零碎,需要使用磁盘整理工具进行重新整合。和磁盘管理非常相似,复合文件也会产生这个问题,在适当的时候也需要整理,但比较简单,只要调用一个函数就可以完成了。

五、复合文件函数

  复合文件的函数和磁盘目录文件的操作非常类似。所有这些函数,被分为3种类型:WIN API 全局函数,存储 IStorage 接口函数,流 IStream 接口函数。什么是接口?什么是接口函数?以后的文章中再陆续介绍,这里大家只要把“接口”看成是完成一组相关操作功能的函数集合就可以了。
 


WIN API 函数


功能说明

StgCreateDocfile() 建立一个复合文件,得到根存储对象
StgOpenStorage() 打开一个复合文件,得到根存储对象
StgIsStorageFile() 判断一个文件是否是复合文件

 


IStorage 函数


功能说明

CreateStorage() 在当前存储中建立新存储,得到子存储对象
CreateStream() 在当前存储中建立新流,得到流对象
OpenStorage() 打开子存储,得到子存储对象
OpenStream() 打开流,得到流对象
CopyTo() 复制存储下的所有对象到目标存储中,该函数可以实现“整理文件,释放碎片空间”的功能
MoveElementTo() 移动对象到目标存储中
DestoryElement() 删除对象
RenameElement() 重命名对象
EnumElements() 枚举当前存储中所有的对象
SetElementTimes() 修改对象的时间
SetClass() 在当前存储中建立一个特殊的流对象,用来保存CLSID(注5)
Stat() 取得当前存储中的系统信息
Release() 关闭存储对象
 

IStream 函数


功能说明

Read() 从流中读取数据
Write() 向流中写入数据
Seek() 定位读写位置
SetSize() 设置流尺寸。如果预先知道大小,那么先调用这个函数,可以提高性能
CopyTo() 复制流数据到另一个流对象中
Stat() 取得当前流中的系统信息
Clone() 克隆一个流对象,方便程序中的不同模块操作同一个流对象
Release() 关闭流对象
 
WIN API 补充函数 功能说明
WriteClassStg() 写CLSID到存储中,同IStorage::SetClass()
ReadClassStg() 读出WriteClassStg()写入的CLSID,相当于简化调用IStorage::Stat()
WriteClassStm() 写CLSID到流的开始位置
ReadClassStm() 读出WriteClassStm()写入的CLSID
WriteFmtUserTypeStg() 写入用户指定的剪贴板格式和名称到存储中
ReadFmtUserTypeStg() 读出WriteFmtUserTypeStg()写入的信息。方便应用程序快速判断是否是它需要的格式数据。
CreateStreamOnHGlobal() 内存句柄 HGLOBAL 转换为流对象
GetHGlobalFromStream() 取得CreateStreamOnHGlobal()调用中使用的内存句柄

 

//利用IStream  和IStroage接口来读写复合文件 

// C++COMTEST.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include <objbase.h>     //Component Object Module的声明
#include <afxcom_.h>   //Microsoft Foundation Classes 的有关com的头文件 ASSERT就定义在这里 用于测试程序的正确性

class ComTest
{
public:
 void CreateCompoundFile(TCHAR *path,TCHAR *streamName)
 {  
       
  HRESULT hr ;//返回一个long的结果
  IStorage * strageRoot ;//为复合文档定义根存储
  IStorage * strageSubRoot;//为跟存储定义一个子存储
  IStream  * streamSubRoot;//为子存储定义一个流
  ::CoInitialize(NULL) ;//初始换Com Library 
  hr=::StgCreateDocfile(path,STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,&strageRoot)  ; //第一次创建一个复合文档  这个存储对象 是一个根对象      
        ASSERT(SUCCEEDED(hr))  ; //测试结果是否正确
        hr=strageRoot->CreateStorage(L"SubTag",STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,0,0,&strageSubRoot) ;//在跟存储的基础上创建子存储
        ASSERT(SUCCEEDED(hr))  ; //测试结果是否正确
  strageSubRoot->CreateStream(streamName,STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,0,&streamSubRoot) ;//在子存储的下面创建流对象
  hr=streamSubRoot->Write("hello",5,NULL); //写入到流指针
  ASSERT(SUCCEEDED(hr)) ;
  //因为COM是独立于语言之外的 不能用C++的delete  在堆上分配 或者 在栈中分配    COM有自己的内存分配方式 这意味着 我们在创建完COM对象的时候 用完就需要自己手动删除并释放COM Library
  strageRoot->Release() ;//释放内存
  streamSubRoot->Release() ;//必须释放
  strageSubRoot->Release() ;//释放子存储内存
  CoUninitialize();//释放COM库

 }
 BOOL  CheckIfProfoundFile(TCHAR* path)   //在COM中通常需要宽字节
 {

  HRESULT r=StgIsStorageFile(path);
  switch(r)
  {
  case STG_E_FILENOTFOUND:
   printf("文件没有发现");
   break;
  case  S_FALSE:
   printf("文件不是复合文件");
   break;
  case S_OK:
   printf("文件是复合文件");
   break ;

  }
 }

 
} ;
int _tmain(int argc, _TCHAR* argv[])
{
 
     ComTest  test ;
  TCHAR path[]=L"c:\\1.doc";
  TCHAR name[]=L"stream" ;
  test.CreateCompoundFile(path,name) ;

 return 0;
}

 

 

 

时间: 2024-10-28 07:20:30

COM组件设计与应用学习1-------复合文档的相关文章

上位机-一个物联网方面的问题,做一个基于WiFi的楼宇能耗监测系统,软件部分怎么设计比较好,类似这个文档里的

问题描述 一个物联网方面的问题,做一个基于WiFi的楼宇能耗监测系统,软件部分怎么设计比较好,类似这个文档里的 http://www.docin.com/p-1224986285.html 之前并不知道上位机,也没学过C#.只学了一点php和J2EE.还需要做Android端,想问下各位前辈们的想法,是做成上位机吗?对WiFi那边的硬件也不是很了解, 本来是想做成网页或者客户端的,这个上位机是怎么做的? 怎么设计软件部分比较快一点... 还有APP怎么设计比较好? 解决方案 既然基于WI-FI,

求高手给一套学习DOS的文档我

问题描述 求高手给一套学习DOS的文档我非常感谢!!邮箱:clie_d@foxmail.com 解决方案 解决方案二:首先搞清楚,你要学习控制台命令还是DOS命令还是DOS操作系统还是什么.控制台命令和DOS命令有很多类似之处,但是本质上是完全不同的东西.其实学习调用这些命令非常简单,我花1分钟就能给你说清楚,根本不需要什么文档.你记住,任何时候,需要帮助信息,如同GUI程序在最右边会有帮助菜单一样,命令行一般包含一个-h或者-help的参数.对于一些程序来说,当你不带参数调用,或者调用参数不正

关于COM中的复合文档

关于COM中的复合文档   关于COM中的复合文档 结构化的存储 在永久存储机制下,普通文件都是用字节组织的.每个文件都是没有次序的字节组成的. 而整个文件则是一块的形式存储在磁盘中的,并且每个块都是离散的.当你要读某个文件 的时候文件系统会管理它的指针并返回要读取的字节流. COM采用另一种更为合理的方法来存储文件和数据.这种方式就叫结构化存储.结构化存储 采用的办法是把文件预先加工后存储在一个文档结构中.这里要使用两个COM对象.storages 和streams.storage对象很类似与

转 MongoDB数据库关系表示和设计:(1)嵌套文档和引用链接

使用数据的时候,一个数据项常常和另外的一个或多个数据项产生关系,比如一个"人"对象,有一个名字,可能有多个电话号码,以及多个子女,等等.   在传统的SQL数据库中,关系被分为一个个表(table),在表中,每个数据项以主键(primary key)标识,而一个表的主键又作为另一个表的外键(reference key),在两个表之间引用.当遇上多对多关系的时候,还需要一个额外的关联表(reference table),将多对多关系转化成两个一对多关系.   而在MongoDB中,表示关

【ASM学习】ASM文档

在深入介绍ASM的复杂内容之前,首先需要感谢Oracle公司的Nitin Vengurlekar,他负责编写了本章中关于ASM的优秀补充内容.    在Oracle Database 10g Release 2中,使用自动存储管理(Automatic Storage Management,ASM)极大地简化了数据库的存储管理和配置.ASM提供了内置于Oracle数据库内核中的文件系统和卷管理器功能.通过这些功能,ASM简化了各种存储管理任务,例如创建/布置数据库和磁盘空间管理.ASM允许用户使用

Jquery 基础学习笔记之文档处理_jquery

主要包括以下几部分:(1)内部插入(2)外部插入(3)包裹(4)替换(5)删除(6)赋值.那我们就开始详细地看一下. 1.内部插入:向一些元素的内部插入内容 (1)append(content) :向每个匹配的元素内部追加内容,追加到元素内部的末尾,比如 描述: 向所有段落中追加一些HTML标记. HTML 代码: <p>I would like to say: </p> jQuery 代码: $("p").append("<b>Hello

如何写软件设计文档

软件设计的不同模型:瀑布式.快速原型法以及迭代式 自从1968年提出"软件工程"概念以来,软件开发领域对于借鉴传统工程的原则.方法,以提高质量.降低成本的探索就从未停止过.而在这个过程中,提出了许多不同的软件开发模型,典型的有:瀑布式,快速原型法,以及迭代式开发等. 瀑布式模型 是由W.W.Royce在1970年最初提出的软件开发模型,在瀑布模型中,开发被认为是按照需求分析,设计,实现,测试 (确认), 集成,和维护顺序的进行. 快速原型法 快速原型模型的第一步是建造一个快速原型,实现

文档驱动式代码设计器——代码是设计出来的!

  代码是敲出来的吗?是批量生成出来的吗?   No no no,代码是设计出来的!   如果说到代码生成器,大家可能会想到三层.动软代码生成器.数据库表等等.其一般的思路是,先有数据库然后根据库里的表自动生成一系列的代码,包括实体类.持久化.业务层(空函数).页面代码等,还可以生成数据库文档.这个确实很好很强大,可以免除程序员的机械式的敲代码的工作.   ("主要实现在对应数据库中表的基类代码的自动生成,包括生成属性.添加.修改.删除.查询.存在性.Model类构造等基础代码片断,支持不同3种

COM组件设计与应用(一)起源及复合文件

一.前言 公元一九九五年某个夜黑风高的晚上,我的一位老师跟我说:"小杨呀,以后写程序就和搭积木一样啦.你赶快学习一些OLE的技术吧......",当时我心里就寻思 :"开什么玩笑?搭积木方式写程序?再过100年吧......",但作为一名听话的好学生,我开始在书店里"踅摸"(注1)有关OLE的书籍(注2).功夫不负有心人,终于买到了我的第一本COM书<OLE2 高级编程技术>,这本800多页的大布头花费了我1/5的月工资呀......