600 likes | 837 Views
第 6 章 可连接对象. 1. 入接口和出接口 入接口:客户完成对象的创建和释放 出接口:对拥有出接口的对象称为可连接对象,也称为源对象 这些出接口并不是 由对象来实现,而是由客户来实现,客户实现这些接口,并把这些接口指针告诉对象。以后对象利用这些接口指针与客户进行通信。 接收器:在客户程序方,实现这些接口的对象被称为接收器 (sink) ,它本身就是一个 COM 对象,只用来监听并处理组件对象的通知或请求。. 6.1 可连接对象结构模型. 1. 客户与可连接对象之间的通信是双向的 (1) 客户调用对象所提供的服务,对象提供相应的服务给客户。
E N D
第6章 可连接对象 • 1. 入接口和出接口 入接口:客户完成对象的创建和释放 出接口:对拥有出接口的对象称为可连接对象,也称为源对象 这些出接口并不是 由对象来实现,而是由客户来实现,客户实现这些接口,并把这些接口指针告诉对象。以后对象利用这些接口指针与客户进行通信。 接收器:在客户程序方,实现这些接口的对象被称为接收器(sink),它本身就是一个COM对象,只用来监听并处理组件对象的通知或请求。
6.1 可连接对象结构模型 • 1. 客户与可连接对象之间的通信是双向的 (1) 客户调用对象所提供的服务,对象提供相应的服务给客户。 (2) 对象可以通过它的出接口向客户发出请求,或者 给客户发送通知或事件。 • 6.1.1. 客户与可连接对象的关系 客户程序把接收器的接口指针通过参数传递给连接对象,可连接对象记录下接收器的接口指针,以后便可通过此接口指针调用接收器的成员函数。 接收器也是一个COM对象,但是它在客户端实现,不需要COM来创建,不需要CLSID和类厂,单独的COM对象,自己的引用计数,以及接口查询。
客户与可连接对象的关系 • 客户与可连接对象之间的结构 (1) 因为可连接对象本身是一个COM对象,应用引用计数,因此一个接收器可以被多个可连接对象使用。 (2) 每个可连接对象也可以连接多个接收器。 可连接对象和接收器之间可以形成一对多和多对一的关系。
6.1.2 可连接对象的基本结构 • 在可连接对象中,通过接口IConnectionPointContainer管理所有的出接口。对应于每一个出接口,可连接对象又管理一个连接点(connection point)的对象,每一个连接点对象实现了IConnectionPoint接口,客户通过连接点对象建立接收器与可连接对象的连接, • 连接点既可以访问可连接对象的内部信息,也可以访问客户方的接收器,,而且连接点对象的引用计数可以包含在可连接对象的引用计数器内部,可以直接使用可连接对象的引用计数器。
基本结构 • 一个可连接对象可以支持多个出接口,在接口IConnectionPointContainer的成员函数中使用一个枚举器暴露出此对象所支持的所有出接口 • 对于每一个出接口的连接点对象,在接口IConnectionPoint中使用一个枚举器管理它所连接的接收器。
6.2 实现可连接对象 • 6.2.1 枚举器 利用枚举器可以很方便的访问一组数据单元。客户程序利用枚举器对COM对象中的数据单元进行枚举操作,枚举器把客户对数据单元的操作进行了标准化,因此,COM对象可以按照标准的方法把数据提供给客户。 对枚举的数据单元可以进行Next、Skip、Reset和Clone操作 枚举器所枚举的数据单元不确定,因此使用模板<ELT_T>,String—IEnumString,IUnknown-IEnumUnknown 枚举器对象是一个内部对象,只暴露枚举接口,不需要CLSID和类厂.
源对象 • 6.2.2 源对象 源对象通过IConnectionPointContainer接口暴露自己的出接口信息,只要COM对象支持出接口,则必须实现此接口。 此接口实现了两个函数Eunm和Find。Enum用来返回连接点枚举器,以便客户利用此枚举器访问COM对象的所有连接点。Find函数根据给定的IID,返回相应出接口的连接点。 其中Enum是一个典型的枚举器接口,枚举的数据成员为连接点对象,相应的数据类型为IConnextionPiont接口。
连接点 • 连接点实现了IConnectionPoint接口,此接口有五个函数 • GetConnectionInterface-返回对应出接口的IID • GetConnectionPointContainer-返回源对象的此接口指针 • Advise-建立接收器和源对象之间的连接 • Unadvise-取消连接 • EnumConnections-访问建立的连接,返回连接枚举器对象
实现可连接对象 • 6.2.4 建立连接过程 假设源对象支持出接口 ISome,客户方创建了一个接收器对象,它实现了接口ISome,接口指针为pSome。以下是客户如何从一个基本的IUnknown接口指针pUnk建立连接的过程步骤。 (1) 调用pUnk->QueryInterface(IID_ICPC, &pCPC),如果不成功,则不支持出接口。 (2) 调用pCPC->Find(IID_ISome,pCP),如果不成功,则表明不支持出接口ISome。调用pCPC->release()。 (3) 调用pCP->Advise(pSome,&dwCookie)建立与接收器的连接。客户保存连接标识dwCookie,以后断开连接时需要用到dwCookie标识连接。 (4) 当客户要取消连接时,调用pCP->Unadvise(dwCookie),并调用pCPC->release释放连接点对象。 连接点对象与原对象分开实现,可以保持各自的独立性,原对象的变化不影响连接点,连接点也不影响原对象,当然连接点对象要受到原对象的控制。
接收器实现 • 使用New创建 • 使用Release释放
事件的激发和处理 • 入函数(属性修改) • 用户操作(鼠标) • 其它对象和客户调用 • 调用封装函数完成
类型信息 • 动态实现接收器 • 使用IDispatch作为出接口
IDispatch接口作为出接口 • 用标准的IDispatch接口作为出接口:利用此接口中方法的分发功能实现事件控制函数。此接口是自动化对象的基本接口。 • 此接口的特点: (1) 此接口支持迟绑定。 (2) 自动化对象的这个接口的vtable是固定的(不支持指针的语言) (3) 用名字访问属性和方法非常简单容易。
第7章 结构化存储 • 结构化存储也称为永久化存储机制。COM针对组件软件的需要,在文件系统的基础上,提出了结构化存储。利用结构化存储,组件程序之间可以更好地协同工作,一个组件程序可以与另一个组件程序共享同一个文件。如同两个应用程序共享同一文件一样。 • COM调用结构化存储的规范,包括一组接口和实现这些接口成员函数地一些规则。 • 用复合文档技术来实现COM的结构化存储。此技术是OLE地基础,OLE最初地目的是在文档中嵌入和链接对象。
7.1 结构化存储基础 • 结构化存储“借用”文件系统地概念,在文件内部构造了一个类似于文件系统地树状层次结构,层次结构地节点可以是两种对象:存储对象和流对象,每个存储对象或流对象都是一个可以独立进行读写操作地对象,组件程序只对它拥有地节点对象进行操作。从应用系统整体上看,这些组件程序在共享同一文件。这个层次结构就构成了结构化存储的基本概念。 • 7.1.1 从文件系统发展到结构化存储 操作系统提供了流式存储结构,目录是文件的容器,文件是叶节点,保存数据对象,大文件可以不连续。
结构化存储基础 • 结构化存储的引入使多个组件程序共享访问同一个文件成为可能,而且每一个组件程序可以有自己独立的存储空间。否则客户程序就需要进行统一管理文件,当需要进行读写操作时,客户程序吧文件句柄传递给组件程序,由组件程序完成实际的文件操作,然而客户程序并没有能力保证一个组件程序不去干扰其他的存储空间。
结构化存储的核心思想: 在一个文件内部构造一个树状层次结构,有如文件系统的层次结构一样,在文件内部的树状结构中,每个节点可以是两种对象之一:存储对象和流对象,根结点为根存储(root storage),根存储下面可以有子存储对象或流对象,在子存储对象下面可以再有子存储和流对象,从而形成层次树状结构。存储对象本身不包含数据信息,它只是作为流对象或子存储的容器,而流对象可以包含任意长度的数据信息。经常称此存储结构为文件内部的文件系统,把这样的文件称为复合文档。
每个组件程序只访问属于自己的存储对象或流对象。每个组件程序只访问属于自己的存储对象或流对象。 客户程序负责根存储的访问操作,它可以把根存储操作封装到某个组件程序中。如图7.3所示: 利用结构化存储自动实现了“部分访问”和“增量访问”的特性。只需要访问必要的信息、只保存修改过的数据信息。
结构化存储基础 • 7.1.2 存储对象和流对象 对一个完整的存储操作来说,它被分为两个层次: 应用程序调用API函数 操作系统提供API函数的实现 在结构化存储上,COM库提供了结构化存储的实现,它提供了一组接口和API函数供组件程序调用,组件程序利用这些接口和函数完成实际的存储操作。因此,结构化存储中的存储对象和流对象由COM库来实现。
流对象:流对象非常类似于单独的磁盘文件,它也是进行读写操作的基本对象,利用流对象可以保存各种类型的数据,它有自身的访问权限和搜索指针。流对象提供了连续的字节流存储空间,虽然实际的存储位置在文件不一定连续,但COM封装了内部的数据结构,提供了连续的字节流抽象结构。流对象:流对象非常类似于单独的磁盘文件,它也是进行读写操作的基本对象,利用流对象可以保存各种类型的数据,它有自身的访问权限和搜索指针。流对象提供了连续的字节流存储空间,虽然实际的存储位置在文件不一定连续,但COM封装了内部的数据结构,提供了连续的字节流抽象结构。 流对象也用一个字符串作为其名称。同一个存储对象下的流对象不能重名。 流对象是一个由COM实现的组件对象,它实现了基本的COM接口IStream,应用程序通过IStream接口访问流对象,进行各种数据访问操作。
结构化存储基础 • 在实现组件程序和应用程序时,只通过Istream接口与流对象打交道。其中的函数的说明如表7.1 • Read,write,seek,copyto,commit,revert,lockregion,unlockregion,stat,clone • 其中的SetSize可用于程序性能的优化,允许客户程序预先分配好流对象的空间,有助于提前发现程序中的问题,以及提高程序的性能。
存储对象类似于目录对象,也有一个字符串名称,但不存储数据。存储对象暴露IStorage接口,客户通过IStorage接口对存储对象进行操作, IStorage接口的成员函数与平时进行的目录操作相似。比如:列出目录中所有的子目录和文件、移动、拷贝、删除、新建文件和目录等。 • 存储对象作为容器对象,提供了针对其子对象的各种操作,如CreateStream、CreateStorage、EnumElements、RenameElement等,也提供了用于事务处理的Commit、Revert成员函数以及其它一些操作。说明见表7.2
结构化存储基础 • 其中IStorage::CreateStream和IStorage::OpenStream成员函数是获取流对象的唯一途径。 • 在调用IStorage::Stat(用存储对象的一些有用信息填充STATSTG结构,包括名字和CLSID)函数时必须注意,如果flags参数为STATFLAG_DEFAULT值,那么COM为存储对象名字分配内存,因此必须用与此一致的方法来对其释放,在此可以调用CoTaskMemFree辅助函数释放字符串资源,如果只想要存储对象的大小和类型,而不是名字信息,那么可以把此参数设置为STATFLAG_NONAME值,这时没有分配内存,因此就不需要释放。
从IStorage的接口成员函数可以看出,对于存储对象,处理方式可以按照统一的方式进行从IStorage的接口成员函数可以看出,对于存储对象,处理方式可以按照统一的方式进行 • 在CopyTO和MoveElementTo这两个成员函数中,参数pStgDest指定的目标存储对象既可以位于同一树状结构,也可以在另一个复合文件中。 • 只能从CreateStorage和OpenStorage两个成员函数得到一个子存储对象。COM库提供相应的函数或者其它途径创建或打开根存储对象。
用结构化存储设计应用 • 用一个例子来进行说明: 一个有很多章组成的文档,其中每一章内又有很多节。文档很大,因此程序启动时,把整个内容都读入内存效率较低,只是希望能够插入和删除文档的一部分,这时可以设计一个复合文档,根存储包含一些子存储,子存储代表章,每一章的子存储又包含一些流,它们代表节,在节存储下有一些流对象和存储对象,分别用来保存文本信息和图片、表格信息。结构如图7.5。
这样的结构信息可以用一些交叉引用的指针进行管理,文档维护一组指向每一章的内存指针,在章结构中维护一组指向节的内存指针,节的结构中包含了实际的文字信息。如果文档包含图片或表格之类的格式化信息,则在节的结构信息中也要包含这些结构信息。在内存中,可以使用指针来构造文档结构,而且,当增加或删除章节信息时,只需要通过指针操作维护相应的结构既可。这样的结构信息可以用一些交叉引用的指针进行管理,文档维护一组指向每一章的内存指针,在章结构中维护一组指向节的内存指针,节的结构中包含了实际的文字信息。如果文档包含图片或表格之类的格式化信息,则在节的结构信息中也要包含这些结构信息。在内存中,可以使用指针来构造文档结构,而且,当增加或删除章节信息时,只需要通过指针操作维护相应的结构既可。 如果存储到普通的文件中,则指针就不适用了,一般采用偏移量以便正确定位信息结构,比如,文档信息结构保存了每一章的偏移量,章的结构信息保存了每一节的偏移量。如图7.4。
用结构化存储设计应用 • 用普通文件来组织很难管理,增加信息或修改信息,都要设计整个文档,因此既影响了程序的性能,也使应用程序实现起来更加复杂。 • 复合文档结构可以很好的解决部分信息的编辑和管理问题。如果增加或删除某些章节,不会影响其它的章节。同时,COM可以自动管理文件的空闲空间,而不需要应用程序管理磁盘空间的申请和回收。 • 优点:章节对象被放置在与主应用程序分离的组件程序中,组件程序可以直接把章节信息保存到磁盘中,而不需要通过主应用程序,而且组件程序只需要保存修改过的信息。
结构化存储特性 • 7.2.1 访问模式 分两种基本的访问模式:直接访问和事务访问 直接访问模式下:操作马上生效 事务访问模式下:所做的操作被缓存起来,只有当提交(Commit)时才真正有效,如果调用Revert函数,则可恢复到上一次提交或刚打开时的状态。在IStorage的前四个成员函数中,有一个gfMode参数指定了在存储对象或者流对象的访问模式,此参数可以由一些预定义的常量组合而成,按其功能分为以下几组:
(1) 创建存储对象或者流对象的标志常量: STGM_CREATE:------创建新的存储对象,若存在删除-创建新的 STGM_CONVERT:--允许创建操作保留原来的数据,并把这些数据保存在名为“CONTENTS”的流对象中,并且该流对象总是位于当前存储对象下。此标志只适用于存储对象的创建操作。 STGM_FAILIFRHERE:如果同名对象存在,则创建失败。适用于存储对象和流对象 • (2) 创建临时存储标志常量 STGM_DELETEONRELEASE:根存储对象释放时,删除此文件
访问模式 • (3) 直接模式和事务模式标志常量 STGM_DIRECT:直接 STGM_TRANSACTED:事务 (4) 优先标志常量 STGM_PRIORITY:打开存储对象之前先读到一些流信息 ,必须和STGM_DIRECT和STGM_READ一起指定。 (5) 读写操作标志常量 STGM_READ:指定读权限 STGM_WRITE:指定写权限 父对象的读写权限也限定了子对象的读写权限
(6) 共享权限标志常量 STGM_READWRITE:可读性权限 STGM_SHARE_DENY_READ:禁止再以读方式打开 STGM_SHARE_DENY_WRITE:禁止再以写方式打开 STGM_SHARE_EXCLUSIVE:是上面两个的组合 STGM_SHARE_DENY_NONE:不禁止 访问模式标志比较复杂,应尽量按照规则给出标志组合。
事务机制 • 7.2.2 事务机制 事务特性可以篏套使用:A包含B,B包含C,如都使用STGM_TRANSACTED标志,那么对对象C作了修改,在调用Commit函数后,只有B知道,A是不知道的,对B调用Commit函数后,A才知道C被修改了。 如果B用STGM_DIRECT标志,则A就知道C的修改。如果A用STGM_DIRECT标志,则对C的修改直接写到磁盘文件中。 如果是事务模式,则COM实际上在内存或临时文件中保存了一份拷贝,所作的操作就是在此拷贝中进行,只有调用Commit时,COM把此拷贝才提交。STGM_PRIORITY标志只能与STGM_DIRECT标志一起使用。
Commit成员函数中有一个参数grfCommitFlags用于控制提交的方式:Commit成员函数中有一个参数grfCommitFlags用于控制提交的方式: STGC_DEFAULT:新的代替旧的数据 STGC_OVERWRITE:新的代旧的,对内存要求小 STGC_ONLYIFCURRENT:共享情况下,阻止其它用户执行提交 STGC_DAN:提交并不马上写到磁盘文件中 Release释放之前没有提交,相当于Revert
命名规则 • 存储对象和流对象的命名规则与文件系统的命名规则不同,其如下: 首先,根存储对象的名字实际上就是复合文件的文件名。 其次,子对象的命名规则遵循COM给出的约定,其如下: (1) 长度不超过32个字符 (2) 不能使用字符\ / : ! (3) 名字.和..被保留
(4) 名字保留大小写,但比较文件名时大小写不敏感。 (5) 首字符使用大于32的字符,小于32的有特殊意义 小于32的名字有特殊意义,COM利用首字符值,把对象分成了多个名字空间,只有特殊的代码才能使用这些名字空间: (1) ‘0x01’和’0x02’的名字为COM以及OLE专用 (2)‘0x03’被管理父对象的客户代码使用,客户程序可保存一些说明信息,比如标志信息、摘要信息 (3)‘0x04’为结构化存储本身使用 (4)0x05~0x1f保留为将来使用
增量访问 • 增量访问有两个意义: (1) 保存和打开文件时减少操作时间。装入时,只要装入需要编辑的部分,或者产生时只装载简单的摘要信息,待编辑到某处时再装入相应的信息。 (2) 降低了应用程序对系统资源的要求,只需要能够装下需要编辑的某一部分就可以了。 从根存储出发,依次调用IStorage的前四个成员函数来进行定位,定位到流,进行编辑修改,时存储对象,可以创建、删除子对象等。
但有一个问题就是“文件碎片”:也就是空间的回管理问题。对复合文档进行修改和删除操作,复合文档的尺寸总是再增长。因为删除了对象时,COM只是把这些对象所占用的磁盘空间作了“未用”的标记,而不是释放这些磁盘空间,当然COM以后会重用这些空间,但在重用之前,这些空间仍保留在文件中。但有一个问题就是“文件碎片”:也就是空间的回管理问题。对复合文档进行修改和删除操作,复合文档的尺寸总是再增长。因为删除了对象时,COM只是把这些对象所占用的磁盘空间作了“未用”的标记,而不是释放这些磁盘空间,当然COM以后会重用这些空间,但在重用之前,这些空间仍保留在文件中。 解决办法:首先创建一个新的复合文档,然后调用原先根存储对象的CopyTo函数,把以前的树结构复制到新的根存储中,则新的复合文件没有碎片空间。
7.3结构化存储实现:复合文档 • LockBytes对象和ILockBytes接口 在特定的操作系统平台上实现结构化存储,关键在于两个方面: (1) 如何把根存储与底层存储介质结合起来 (2) 实现存储对象和流对象 复合文档通过一个被称为“LockBytes”的对象,解决了第一个问题。 LockBytes对象实际上是所有存储介质的一种抽象表达方式,它把存储介质描述成一般化的字节序列,不管是磁盘文件还是内存区域都可以按字节序列对待。它不关心内容,只提供读写操作。在复合文档中, LockBytes对象位于根存储与底层存储介质之间。
ReadAt和WriteAt参数中指定了读写数据在字节序列中的位置。ReadAt和WriteAt参数中指定了读写数据在字节序列中的位置。 SetSize设置字节序列的大小 Flush保证内部缓冲区中的数据被写道实际的存储介质中 Stat返回当前字节许类的一些状态信息 Lockregion加锁 UnlockRegion解锁 LockBytes对象可以不实现区域锁功能
LockBytes对象实现了ILockBytes接口。 COM库提供了缺省的基于文件句柄操作的LockBytes对象,建立复合文件,也可以利用它建立内存中的复合文档,也可以建立自定义的复合文档。 LockBytes对象有如磁盘驱动程序,文件可以通过磁盘驱动程序与底层的存储介质传输数据,存储对象通过LockBytes对象与底层存储空间交换数据。 在复合文档的层次结构中,根存储通过LockBytes对象与底层的存储介质进行数据通信,其它的子对象则通过根存储与底层存储介质进行数据通信,从而实现了整个结构化存储体系结构。
复合文档API函数介绍 • 复合文档作为结构化存储的实现,不仅建立了LockBytes对象的概念,把根存储与底层的存储介质隔离开来,也实现了基于文件句柄和内存句柄的LockBytes对象,并且,COM还提供了一组API函数是应用程序可以很方便的使用这些LockBytes对象以及存储对象和流对象。
(1) 用来创建复合文档分别为:StgCreateDocfile和StgCreateDocfileOnILockBytes。 函数StgCreateDocfile创建一个复合文档,它使用缺省的基于文件句柄的LockBytes对象作为底层的存储机制。pwcsName参数指定了文件名。 函数StgCreateDocfileOnILockBytes创建一个基于LockBytes对象的复合文档,要使用基于内存句柄或自定义作为底层的存储机制,调用此函数。
复合文档API函数介绍 • (2) 打开已经存在的复合文档,分别为StgOpenStorage和StgOpenStorageOnILockBytes。 这两个函数分别打开基于文件LockBytes对象或其它LockBytes对象的复合文档。 StgOpenStorage函数首先关闭pstgPriority存储对象,再跟据grfMode参数指定的模式打开新的存储对象,并保存在ppstgOpen输出参数中,因此函数返回后,就只能使用ppstgOpen 存储对象而不能再使用pstgPriority。参数snbExclude用于优化程序性能。
(3) 与内存句柄有关的一组操作函数 CreateILockBytesOnHGlobal函数可以创建建立在内存句柄基础上的LockBytes对象。在调用之前应首先调用GlobalAlloc分配一定的内存,如果LockBytes对象要跨越进程共享,那么必须使用共享内存,hGlobal为Null,那么分配一共享内存。 GetHGlobalFromILockBytes函数返回内存LockBytes对象的内存句柄,可能不一样,原因在于LockBytes可能重新分配。 CreateStreamOnHGlobal函数用来创建一个基于内存的流对象,通常永久对象(persistent object)只能作为流对象。 GetHGlobalFromStream返回流对象句柄。也可能不一样。
复合文档API函数介绍 • (4) 还有几个很有用API函数,程序中直接可以使用: StgIsStorageFile函数用来判断一个文件是否是复合文档 StgIsStorageILockBytes函数用来判断一个LockBytes对象是否包含一个存储对象。 StgSetTimes函数可以修改一个复合文档的创建、访问、修改时间值。
零内存保存特性和IRootStorage接口 • 复合文档提供LockBytes对象把根存储对象与底层的文件操作隔离开来,所以在访问存储对象或者流对象时避开了文件句柄操作。当用事务方式打开复合文档时,COM实际上用到了三个文件句柄: • 复合文档句柄;临时文件句柄(保存修该信息);用于零内存情况下保存文件时预分配的文件句柄。 • 保存实际上就是把内存中的修改记录写到磁盘文件中,在此Microsoft特别考虑了资源的消耗,尤其是内存消耗殆尽的情况下,保证修该信息被正确的保存到文件中。
对于save操作,调用Commit函数,COM可以在不利用额外的内存的情况下把修改的信息保存到文件中。对于save操作,调用Commit函数,COM可以在不利用额外的内存的情况下把修改的信息保存到文件中。 • 对于save as操作,事务模式下的存储对象被分成两部分,一部分是原始的状态信息,另一部分是存放在内存或临时文件中的修改信息,把这两者保存到另一个存储对象中,则必须执行IStorage::CopyTo函数,要用额外内存。因此COM提供了接口IRootStorage,此接口在根存储上实现,它与底层文件系统联系在一起,可以在根存储对象上调用IStorage::QueryInterface(IID_IRootStorage,…)得到接口指针,接口IRootStorage的成员函数SwitchToFile利用预留的第三个文件句柄实现了不需要额外内存情况下,把修改信息保存到新文件中。
存储对象、流对象和文件的CLSID信息 • 在Istorage接口中的成员函数SetClass用来为一个存储对象赋一个CLSID标识符,以后可以通过Stat函数来获取此CLSID值,实际上,存储对象通过此CLSID值把它与一段可执行代码联系起来,当客户程序希望执行与存储对象相联系的代码时,它可以利CLSID值,并调用CoCreateInstance函数创建一个COM对象,再把存储对象交给COM对象,由它处理存储对象。这样的COM对象称为永久对象。 • COM也提供API函数用于对存储对象或者流对象执行与CLSID有关的一些操作。
(1) WriteClassStg和ReadClassStg函数封装了SetClass和Stat函数,可以完成存储对象的CLSID的设置和获取操作。 (2) WriteClassStm和ReadClassStm函数使用一致的格式在流对象的当前位置分别写或读CLSID信息,通常情况下,在流的起始位置放CLSID信息。 (3) GetClassFile函数返回一个与给定文件联系的CLSID。 1.在Windows注册表中记录了文件扩展名与ProgID之间的联系,而ProgID有指定了CLSID,所以该文件扩展名与CLSID联系起来。 2. Windows注册表提供了一些文件匹配规则。
复合文档与结构化存储 • 结构化存储中的一些限制: (1) 状态标志位没实现,IStorage::SetStateBits是一个空函数 (2) EunmElements、MoveElementTo、RenameElement和DestoryElement成员函数没有优化。 (3)流对象不支持区域锁操作和事务模式 (4)结构化存储允许达到2的64次方,但复合文档只能位2的32次方,因为流的位置指针为32位值 (5) 流对象以512个字节为单位申请空间。 (6)流对象使用单向链表管理它所占用的非连续的存储空间,向前移动比向后快 (7)所有的存储对象和流对象的名字都使用Unicode字符保存。