《COM原理及应用》学习笔记之第七章
2010-03-07 10:01
267 查看
第七章
结构化存储
1
、结构化存储
COM
的结构化存储(
structured storage
)机制,也称为永久存储(
persistent storage
)机制。结构化存储可以说是软件存储技术的一个重要进展,
COM
针对组件软件的需要,在文件系统的基础上,提出了结构化存储的概念。利用结构化存储,组件程序之间可很好地协同工作,一个组件程序可以与另一个组件程序共享同一个文件,就如同一个应用程序与另一个应用程序共享同一个磁盘文件系统一样。
COM
定义了结构化存储的规范,包括一组接口和实现这些接口成员函数的一些规则;同时
COM
也提供了结构化存储的实现,即复合文档技术。复合文档技术是
OLE
的基础。
OLE
最初的目标是在文档中嵌入或链接对象,当然现在
OLE
的发展已经超出了这个范围,但复合文档仍然是
OLE
的基础存储技术。
2
、结构化存储的引入
组
件化程序设计方法把应用系统设计成多个组件程序,如何在这些组件程序之间以共享方式访问同一个文件是组件化程序设计必须要解决的问题。而多个组件通过文件
句柄访问共享文件难以实现,结构化存储技术“借用”文件系统的概念,在文件内部构造了一个类似于文件系统的树状层次结构,解决了这一问题。
结构化存储的层次结构的节点可以是两种对象:存储对象和流对象,每个存储对象或者流对象都是一个可独立进行读写操作的对象,组件程序只对它拥有的节点对象进行操作。从应用系统整体上看,这些组件程序在共享访问同一个文件。
3
、文件系统
操
作系统的诞生把应用程序与底层存储设备隔离开来,操作系统为应用程序的运行提供了基本的抽象环境,它可以处理所有与存储设备有关的基本操作。同时,操作系
统引入了文件系统的概念,允许多个应用程序共享同一个存储设备。而且,操作系统为应用程序提供了一种抽象的流式存储结构,应用程序仍然独立地访问它自己的
存储空间,不同应用程序相互之间不受干扰。
4
、文件系统与结构化存储的框图
5
、存储对象和流对象
对于一个完整的存储操作来说,它被分为两个层次:应用程序调用
API
函数;操作系统提供
API
函数的实现。
COM
库提供了结构化存储的实现,它提供了一组接口和
API
函数供组件程序调用来完成实际的存储操作。因此,结构化存储定义的存储对象和流对象由
COM
库实现,应用程序或者组件程序并不需要实现这两个对象,就好比应用程序不需要实现文件句柄或者目录对象一样。
流对象非常类似于单独的磁盘文件,它也是进行数据读写操作的基本对象,利用流对象可以保存各种类型的数据,它有自身的访问权限和一个独立的搜索指针。流对象也用一个字符串作为其名称,就好像文件名一样。流对象是一个由
COM
实现的组件对象,它实现了基本的
COM
接口
IStream
,应用程序通过
IStream
接口访问流对象,进行各种数据访问操作。
存储对象类似于目录对象,它也有一个字符串名称,但它本身并没有存储数据信息,它作为其子存储对象和子流对象的容器,只记录了这些子对象的信息。存储对象暴露
IStorage
接口,客户程序通过
IStorage
接口对存储对象进行操作。
应用程序或者组件程序可以通过结构化存储机制共享同一个复合文件,存储对象和流对象也可以在这些程序之间被共享访问,即使这些程序运行在不同的进程中。
6
、结构化存储特性――访问模式
存储对象和流对象支持两种基本的访问模式:直接访问和事务访问模式。在直接访问模式下,程序对存储对象或者流对象的修改操作马上生效;而在事务模式下,程序对存储对象或者流对象所作的修改被缓存起来,只有当提交(
Commit
)时才真正有效,如果调用
Revert
成员函数,则可以恢复到上次提交或刚打开时的状态。
7
、结构化存储特性――事务机制
在结构化存储的树状层次结构中,事务特性可以适用于所有层次上的对象,因此,事务特性可以嵌套使用。
事务机制需要消耗较多系统资源。
8
、结构化存储特性――命名规则
在结构化存储的树状层次结构中,每个存储对象和流对象都有一个字符串名字。
根
存储对象的名字实际上就是复合文档的文件名,所以根存储对象的命名规则受文件系统影响,它遵守文件系统的命名约定。与创建和打开根存储对象有关的函数中,
直接使用文件名即可。所有非根存储对象和流对象都由它们的父对象管理,由于它们存在于文件内部,因此它们的命名规则遵守
COM
给出的约定。
9
、结构化存储特性――增量访问
增量访问的意义在于减少了保存和打开文件时的操作时间和降低了应用程序对系统资源的要求。
对于大的复合文件的编辑操作,内存往往是个瓶颈,结构化存储本身也需要消耗一些系统内存资源,但它实现的增量访问反而降低了应用程序对内存的要求,同时也提高了系统的性能。
结构化存储也带来了另一个问题,那就是空间回收的管理。频繁地对同一个复合文件进行修改、保存,则文件的尺寸总是在增长,原因在于删除对象时,
COM
只是把这些所占用的磁盘空间标记为“未用”,而没有释放这些磁盘空间。当然
COM
以后可能会重用这些空间,但在重用之前,这些空间仍保留在文件中。解决这个问题的方法是:首先创建一个新的复合文件,然后调用原先根存储对象的
CopyTo
函数,把以前的树结构复制到新的根存储中,则新的复合文件没有碎片空间。
Microsoft Access
或
Word
产生的文件就会出现这种情况。
10
、结构化存储实现:复合文档
从结构化存储的对象结构可以看出,要在特定的系统平台上实现结构化存储,关键在两方面:一是如何把根存储与底层存储介质结合起来,二是实现存储对象和流对象。
复合文档通过一个被称为“
LockBytes
”的对象,把根存储与底层的存储介质联系起来,其他的子对象则通过根存储与底层存储介质进行数据通信,从而实现了整个结构化存储体系结构。底层介质不仅可以是磁盘文件,复合文档也允许是内存空间,甚至是用户自定义的虚拟空间。
LockBytes
对象实际上是所有存储介质的一种抽象表达方式,它把存储介质描述成一般化的字节序列,不管是磁盘文件还是内存区域都可以按字节序列对待。
COM
库提供了缺省的基于文件句柄操作的
LockBytes
对象,我们可以利用此
LockBytes
对象建立复合文件。
COM
库还提供了基于内存的
LockBytes
对象,我们可以利用内存
LockBytes
对象建立内存中的复合文档。而且,
COM
还允许我们实现自己的
LockBytes
对象,并在自定义
LockBytes
对象的基础上建立复合文档。
11
、复合文档
API
函数
创建复合文档
API
函数:
StgCreateDocfile
和
StgCreateDocfileOnLockBytes
。
打开已经存在的复合文档
API
函数:
StgOpenStorage
和
StgOpenStorageOnLockBytes
。
在内存基础上创建
LockBytes
对象或者流对象的
API
函数:
CreateILockBytesOnHGlobal
、
GetHGlobalFromILockBytes
、
CreateStreamOnHGlobal
、
GetHGlobalFromStream
。
其他的
API
函数:
StgIsStorageFile
和
StgSetTimes
。
12
、零内存保存特性和
IRootStorage
接口
复合文档通过
LockBytes
对象把根存储对象与底层的文件操作隔离开来,所以我们在访问存储对象或者流对象时避开了文件句柄操作。当我们用事务方式打开复合文件时,
COM
实际上用到了三个文件句柄,一个是复合文件句柄,另一个是临时文件句柄,该临时文件记录了存储对象操作过程中的修改信息,还有一个句柄用作在零内存情况下保存文件时预分配的文件句柄。
13
、存储对象、流对象和文件的
CLSID
信息
通过
IStorage
接口的
SetClass
函数可以为一个存储对象赋一个
CLSID
标识符,并可通过
Stat
函数获取此
CLSID
值。实际上,存储对象通过此
CLSID
值把它与一段可执行代码联系起来,当客户程序希望执行与存储对象相联系的代码时,它利用
CLSID
值,并调用
CoCreateInstamce
函数创建一个
COM
对象,再把存储对象交给
COM
对象,由它处理存储对象。这样的
COM
对象称为永久对象(
persistent object
),它通常实现了
IPersist***
接口,比如
IPersistFile
、
IPersistStorage
、
IPersistStream
和
IPersistStreamInit
等,客户程序通过这些接口进行数据交换。
COM
也提供了几个
API
函数用于存储对象或者流对象执行与
CLSID
有关的一些常规操作:
(
1
)
WriteClassStg
和
ReadClassStg
函数封装了
IStorage::SetClass
和
IStorage::Stat
成员函数,可以完成存储对象的
CLSID
的设置和获取操作。存储对象只是个容器,它本身不包含数据信息,所以它的
CLSID
信息被写在其下面的一个子流对象中,其名字为“
/x01CompObj
”。
(
2
)
WriteClassStm
和
ReadClassStm
函数使用一致的格式在流对象的当前位置分别写或者读
CLSID
信息,通常情况下,我们在流的起始处放置
CLSID
信息。
(
3
)
GetClassFile
函数返回一个与给定文件相联系的
CLSID
。
Windows
桌面环境利用它把数据文件与应用程序联系起来,实现了桌面环境中的数据驱动机制。
对于非复合文档,
Windows
提供了两种方法可以建立这样的联系。
(
1
)
Windows
系统注册记录了文件扩展名与
ProgID
之间的联系,而
ProgID
又指定了
CLSID
,所以该文件扩展名与
CLSID
联系起来了。
(
2
)
Windows
系统注册表提供了一些文件匹配规则,在
HKEY_CLASSES_ROOT/FilType
键下记录了一些
CLSID
与它们的匹配规则。
14
、复合文档与结构化存储
结构化存储是
COM
规范的一部分,它描述了一个理想的存储机制,而复合文档作为它在
Windows
平台上的实现,它利用操作系统的特点增加了一些新的特性,但也不可避免地受到一些限制。复合文档是目前
Windows
平台上的基本存储机制。
15
、永久接口
客户程序通过永久接口维护永久对象的状态信息,而状态信息可以被存放在各种介质中,比如存储对象、流对象或者文件中,根据介质的不同,
COM
定义了四个常用的永久接口:
IPersistFile
、
IPersistStorage
、
IPersistStream
和
IPersistStreamInit
,它们都派生自基本的
IPersist
接口。客户程序可以向永久对象请求这些接口,然后通过接口读写对象的状态信息。
16
、永久对象的存储特性
永久对象通过三种介质保存状态数据,分别为流对象、存储对象和文件。永久对象在三种介质上保存状态数据的特性是不同的,操作过程也是不同的。
实际上,一个永久对象可以实现多个永久接口以便提供多种介质的存储支持。从程序实现来讲,这是一个多接口支持问题,从客户程序来讲,它只能使用一种接口,根据客户程序的特性,它可以优先选择一个永久接口。
结构化存储
1
、结构化存储
COM
的结构化存储(
structured storage
)机制,也称为永久存储(
persistent storage
)机制。结构化存储可以说是软件存储技术的一个重要进展,
COM
针对组件软件的需要,在文件系统的基础上,提出了结构化存储的概念。利用结构化存储,组件程序之间可很好地协同工作,一个组件程序可以与另一个组件程序共享同一个文件,就如同一个应用程序与另一个应用程序共享同一个磁盘文件系统一样。
COM
定义了结构化存储的规范,包括一组接口和实现这些接口成员函数的一些规则;同时
COM
也提供了结构化存储的实现,即复合文档技术。复合文档技术是
OLE
的基础。
OLE
最初的目标是在文档中嵌入或链接对象,当然现在
OLE
的发展已经超出了这个范围,但复合文档仍然是
OLE
的基础存储技术。
2
、结构化存储的引入
组
件化程序设计方法把应用系统设计成多个组件程序,如何在这些组件程序之间以共享方式访问同一个文件是组件化程序设计必须要解决的问题。而多个组件通过文件
句柄访问共享文件难以实现,结构化存储技术“借用”文件系统的概念,在文件内部构造了一个类似于文件系统的树状层次结构,解决了这一问题。
结构化存储的层次结构的节点可以是两种对象:存储对象和流对象,每个存储对象或者流对象都是一个可独立进行读写操作的对象,组件程序只对它拥有的节点对象进行操作。从应用系统整体上看,这些组件程序在共享访问同一个文件。
3
、文件系统
操
作系统的诞生把应用程序与底层存储设备隔离开来,操作系统为应用程序的运行提供了基本的抽象环境,它可以处理所有与存储设备有关的基本操作。同时,操作系
统引入了文件系统的概念,允许多个应用程序共享同一个存储设备。而且,操作系统为应用程序提供了一种抽象的流式存储结构,应用程序仍然独立地访问它自己的
存储空间,不同应用程序相互之间不受干扰。
4
、文件系统与结构化存储的框图
5
、存储对象和流对象
对于一个完整的存储操作来说,它被分为两个层次:应用程序调用
API
函数;操作系统提供
API
函数的实现。
COM
库提供了结构化存储的实现,它提供了一组接口和
API
函数供组件程序调用来完成实际的存储操作。因此,结构化存储定义的存储对象和流对象由
COM
库实现,应用程序或者组件程序并不需要实现这两个对象,就好比应用程序不需要实现文件句柄或者目录对象一样。
流对象非常类似于单独的磁盘文件,它也是进行数据读写操作的基本对象,利用流对象可以保存各种类型的数据,它有自身的访问权限和一个独立的搜索指针。流对象也用一个字符串作为其名称,就好像文件名一样。流对象是一个由
COM
实现的组件对象,它实现了基本的
COM
接口
IStream
,应用程序通过
IStream
接口访问流对象,进行各种数据访问操作。
存储对象类似于目录对象,它也有一个字符串名称,但它本身并没有存储数据信息,它作为其子存储对象和子流对象的容器,只记录了这些子对象的信息。存储对象暴露
IStorage
接口,客户程序通过
IStorage
接口对存储对象进行操作。
应用程序或者组件程序可以通过结构化存储机制共享同一个复合文件,存储对象和流对象也可以在这些程序之间被共享访问,即使这些程序运行在不同的进程中。
6
、结构化存储特性――访问模式
存储对象和流对象支持两种基本的访问模式:直接访问和事务访问模式。在直接访问模式下,程序对存储对象或者流对象的修改操作马上生效;而在事务模式下,程序对存储对象或者流对象所作的修改被缓存起来,只有当提交(
Commit
)时才真正有效,如果调用
Revert
成员函数,则可以恢复到上次提交或刚打开时的状态。
7
、结构化存储特性――事务机制
在结构化存储的树状层次结构中,事务特性可以适用于所有层次上的对象,因此,事务特性可以嵌套使用。
事务机制需要消耗较多系统资源。
8
、结构化存储特性――命名规则
在结构化存储的树状层次结构中,每个存储对象和流对象都有一个字符串名字。
根
存储对象的名字实际上就是复合文档的文件名,所以根存储对象的命名规则受文件系统影响,它遵守文件系统的命名约定。与创建和打开根存储对象有关的函数中,
直接使用文件名即可。所有非根存储对象和流对象都由它们的父对象管理,由于它们存在于文件内部,因此它们的命名规则遵守
COM
给出的约定。
9
、结构化存储特性――增量访问
增量访问的意义在于减少了保存和打开文件时的操作时间和降低了应用程序对系统资源的要求。
对于大的复合文件的编辑操作,内存往往是个瓶颈,结构化存储本身也需要消耗一些系统内存资源,但它实现的增量访问反而降低了应用程序对内存的要求,同时也提高了系统的性能。
结构化存储也带来了另一个问题,那就是空间回收的管理。频繁地对同一个复合文件进行修改、保存,则文件的尺寸总是在增长,原因在于删除对象时,
COM
只是把这些所占用的磁盘空间标记为“未用”,而没有释放这些磁盘空间。当然
COM
以后可能会重用这些空间,但在重用之前,这些空间仍保留在文件中。解决这个问题的方法是:首先创建一个新的复合文件,然后调用原先根存储对象的
CopyTo
函数,把以前的树结构复制到新的根存储中,则新的复合文件没有碎片空间。
Microsoft Access
或
Word
产生的文件就会出现这种情况。
10
、结构化存储实现:复合文档
从结构化存储的对象结构可以看出,要在特定的系统平台上实现结构化存储,关键在两方面:一是如何把根存储与底层存储介质结合起来,二是实现存储对象和流对象。
复合文档通过一个被称为“
LockBytes
”的对象,把根存储与底层的存储介质联系起来,其他的子对象则通过根存储与底层存储介质进行数据通信,从而实现了整个结构化存储体系结构。底层介质不仅可以是磁盘文件,复合文档也允许是内存空间,甚至是用户自定义的虚拟空间。
LockBytes
对象实际上是所有存储介质的一种抽象表达方式,它把存储介质描述成一般化的字节序列,不管是磁盘文件还是内存区域都可以按字节序列对待。
COM
库提供了缺省的基于文件句柄操作的
LockBytes
对象,我们可以利用此
LockBytes
对象建立复合文件。
COM
库还提供了基于内存的
LockBytes
对象,我们可以利用内存
LockBytes
对象建立内存中的复合文档。而且,
COM
还允许我们实现自己的
LockBytes
对象,并在自定义
LockBytes
对象的基础上建立复合文档。
11
、复合文档
API
函数
创建复合文档
API
函数:
StgCreateDocfile
和
StgCreateDocfileOnLockBytes
。
打开已经存在的复合文档
API
函数:
StgOpenStorage
和
StgOpenStorageOnLockBytes
。
在内存基础上创建
LockBytes
对象或者流对象的
API
函数:
CreateILockBytesOnHGlobal
、
GetHGlobalFromILockBytes
、
CreateStreamOnHGlobal
、
GetHGlobalFromStream
。
其他的
API
函数:
StgIsStorageFile
和
StgSetTimes
。
12
、零内存保存特性和
IRootStorage
接口
复合文档通过
LockBytes
对象把根存储对象与底层的文件操作隔离开来,所以我们在访问存储对象或者流对象时避开了文件句柄操作。当我们用事务方式打开复合文件时,
COM
实际上用到了三个文件句柄,一个是复合文件句柄,另一个是临时文件句柄,该临时文件记录了存储对象操作过程中的修改信息,还有一个句柄用作在零内存情况下保存文件时预分配的文件句柄。
13
、存储对象、流对象和文件的
CLSID
信息
通过
IStorage
接口的
SetClass
函数可以为一个存储对象赋一个
CLSID
标识符,并可通过
Stat
函数获取此
CLSID
值。实际上,存储对象通过此
CLSID
值把它与一段可执行代码联系起来,当客户程序希望执行与存储对象相联系的代码时,它利用
CLSID
值,并调用
CoCreateInstamce
函数创建一个
COM
对象,再把存储对象交给
COM
对象,由它处理存储对象。这样的
COM
对象称为永久对象(
persistent object
),它通常实现了
IPersist***
接口,比如
IPersistFile
、
IPersistStorage
、
IPersistStream
和
IPersistStreamInit
等,客户程序通过这些接口进行数据交换。
COM
也提供了几个
API
函数用于存储对象或者流对象执行与
CLSID
有关的一些常规操作:
(
1
)
WriteClassStg
和
ReadClassStg
函数封装了
IStorage::SetClass
和
IStorage::Stat
成员函数,可以完成存储对象的
CLSID
的设置和获取操作。存储对象只是个容器,它本身不包含数据信息,所以它的
CLSID
信息被写在其下面的一个子流对象中,其名字为“
/x01CompObj
”。
(
2
)
WriteClassStm
和
ReadClassStm
函数使用一致的格式在流对象的当前位置分别写或者读
CLSID
信息,通常情况下,我们在流的起始处放置
CLSID
信息。
(
3
)
GetClassFile
函数返回一个与给定文件相联系的
CLSID
。
Windows
桌面环境利用它把数据文件与应用程序联系起来,实现了桌面环境中的数据驱动机制。
对于非复合文档,
Windows
提供了两种方法可以建立这样的联系。
(
1
)
Windows
系统注册记录了文件扩展名与
ProgID
之间的联系,而
ProgID
又指定了
CLSID
,所以该文件扩展名与
CLSID
联系起来了。
(
2
)
Windows
系统注册表提供了一些文件匹配规则,在
HKEY_CLASSES_ROOT/FilType
键下记录了一些
CLSID
与它们的匹配规则。
14
、复合文档与结构化存储
结构化存储是
COM
规范的一部分,它描述了一个理想的存储机制,而复合文档作为它在
Windows
平台上的实现,它利用操作系统的特点增加了一些新的特性,但也不可避免地受到一些限制。复合文档是目前
Windows
平台上的基本存储机制。
15
、永久接口
客户程序通过永久接口维护永久对象的状态信息,而状态信息可以被存放在各种介质中,比如存储对象、流对象或者文件中,根据介质的不同,
COM
定义了四个常用的永久接口:
IPersistFile
、
IPersistStorage
、
IPersistStream
和
IPersistStreamInit
,它们都派生自基本的
IPersist
接口。客户程序可以向永久对象请求这些接口,然后通过接口读写对象的状态信息。
16
、永久对象的存储特性
永久对象通过三种介质保存状态数据,分别为流对象、存储对象和文件。永久对象在三种介质上保存状态数据的特性是不同的,操作过程也是不同的。
实际上,一个永久对象可以实现多个永久接口以便提供多种介质的存储支持。从程序实现来讲,这是一个多接口支持问题,从客户程序来讲,它只能使用一种接口,根据客户程序的特性,它可以优先选择一个永久接口。
相关文章推荐
- 《COM原理及应用》学习笔记之第九章
- 《COM原理及应用》学习笔记之第十三章
- 《COM原理及应用》学习笔记之第十三章
- 《COM原理及应用》学习笔记之第十一章
- 《COM原理及应用》学习笔记之第十一章
- 《COM 原理与应用》学习笔记 - 第一部分 COM原理
- 《COM原理及应用》学习笔记之第十三章
- 《COM原理及应用》学习笔记之第十三章
- C++学习笔记(第七章 自定义数据类型 结构体应用 之三)
- 《COM原理及应用》学习笔记之第十四章
- 《COM原理及应用》学习笔记之写在前面的话
- C++学习笔记(第七章 自定义数据类型 结构体应用 之二)
- 《COM原理及应用》学习笔记之第一章
- 《COM原理及应用》学习笔记之第一章
- Unix原理与应用学习笔记----第七章 vi编辑器
- 《COM原理及应用》学习笔记之写在后面的话
- 《COM原理及应用》学习笔记之第三章
- 《COM原理及应用》学习笔记之第二章
- 《COM原理及应用》学习笔记之第三章
- 《COM原理及应用》学习笔记之第十章