潘爱民 http://www.icst.pku.edu.cn/CompCourse COM:可连接对象 & 结构化存储 潘爱民 http://www.icst.pku.edu.cn/CompCourse.

Slides:



Advertisements
Similar presentations
主要内容 Java 的常用包 Java 的常用包 “ == ” 和 “ equals ” 的用法 “ == ” 和 “ equals ” 的用法 基本数据类型与引用类型 基本数据类型与引用类型 String 和 StringBuffer String 和 StringBuffer 对象的克隆( clone.
Advertisements

软件开发技术基础 第 3 章 操作系统及程序设计 讲授教师:卫颜俊. 主 要 内 容 主 要 内 容 操作系统及其功能 进程管理应用程序设计 内存管理应用程序设计 设备与文件管理应用程序设计 人机接口管理应用程序设计.
第16章 代理模式 Website:
Memory Pool ACM Yanqing Peng.
第二章 JAVA语言基础.
潘爱民 北京大学计算机科学技术研究所 组件技术——最后一讲 潘爱民 北京大学计算机科学技术研究所
AOP实践 演讲人:陈思荣.
潘爱民 COM开发 潘爱民
C# 程式設計 第一部分 第1-4章 C# 程式設計 - 南華大學資管系.
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
第二章 Visual Basic语言基础.
.NET 程式設計入門(使用 C#) 講師:鄧智鴻.
第二章 C# 基础知识.
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
第3章 變數、資料型別與運算子.
程式語言 -Visual Basic 變數、常數與資料型態.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第3章 語法入門 第一個Java程式 文字模式下與程式互動 資料、運算 流程控制.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
本單元介紹何謂變數,及說明變數的宣告方式。
潘爱民 COM对象的实现(续) 潘爱民
八. COM跨进程特性 进程外组件 列集 标准列集过程 总体结构 存根 代理 接口列集器 ORPC通道 标准列集的实现 自定义列集
JAVA程序设计 第5章 深入理解JAVA语言----补充.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第三章 C++中的C 面向对象程序设计(C++).
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
十四. 名字对象 概念 IMoniker接口 名字对象的创建 根据显示名创建名字对象 类名字对象的创建 其他名字对象的创建
第6章 基本的IDL到C++的映射 3.2 简介 从IDL到C++的映射必须具备下面的条件 (1) 映射应该很直观,并且很容易使用
MFC WinSock类的编程 为简化套接字网络编程,更方便地利用Windows的消息驱动机制,微软的基础类库(Microsoft Foundation Class Libary,简称MFC),提供了两个套接字类,在不同的层次上对Windows Socket API函数进行了封装,为编写Windows.
网络游戏开发语言基础 ——Windows程序设计
五 COM对象 接口及其接口方法的实现 注册表 类厂 COM组件与客户程序的交互过程 类厂的由来 类厂的定义与实现 类厂的创建
第3章 變數、資料型別與運算子 3-1 變數與資料型別的基礎 3-2 變數的命名與宣告 3-3 資料型別 3-4 運算式與運算子
新觀念的 VB6 教本 第 6 章 資料型別.
3.1 数据类型 3.2 标识符与关键字 3.3 常量 3.4 变量 3.5 运算符与表达式 3.6 一个编程实例
十二. 可连接对象 概念与模型 轮询 通知 出接口 连接点机制 IConnectionPointContainer接口
2019/1/17 Java语言程序设计-程序流程 教师:段鹏飞.
Java程序设计 第2章 基本数据类型及操作.
COM:moniker、UDT、control
C/C++/Java 哪些值不是头等程序对象
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
$10 可空类型.
C#程序设计基础 第二章 数据类型.
P2P聊天工具.
編譯程式設計 期末專題說明 V1.1 May 2004.
Chapter 5 Recursion.
第三章 C# 基础知识.
潘爱民 自动化(Automation) 潘爱民
透過YouTuBe API取得資料 建國科技大學 資管系 饒瑞佶 2018/1 V1.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
第三课 标识符、关键字、数据类型.
C#程序设计基础 $3 成员、变量和常量.
第二章 Java基本语法 讲师:复凡.
DEV342 Visual Basic 2005: 应用程序框架 和高级语言特性
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 Java语法基础.
回顾.
第二章 类型、对象、运算符和表达式.
Review 1~3.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第10章媒体控制接口 10.1 MCI设备类型 10.2 MCI编程步骤 10.3使用MCIWnd窗口类.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
Go 语言编程 —— 平台研发部 吴植民.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
第6單元 6-1 類別的繼承 (Class Inheritance) 6-2 抽象類別 (Abstract Class)
第2章 Java语言基础.
第二章 Java基础语法 北京传智播客教育
北师大版六年级数学下册 圆柱的体积.
Presentation transcript:

潘爱民 http://www.icst.pku.edu.cn/CompCourse COM:可连接对象 & 结构化存储 潘爱民 http://www.icst.pku.edu.cn/CompCourse

内容 复习:COM基础 可连接对象 结构化存储

复习:COM基础 COM客户 COM组件 COM库(SCM, RPCSS.EXE) COM库(OLE32.DLL) Apartment 进程B 机器A 机器B 安全通道 ORPC COM客户 COM组件 Apartment Apartment { IXxx *p; p->… } proxy 双接口 VB客户 COM库(SCM, RPCSS.EXE) COM库(OLE32.DLL) Registry

聚合模型的关键

可连接对象(connectable object) 内容: 可连接对象结构模型 实现可连接对象(源对象) 客户-源对象-接收器的协作过程 可连接对象的程序实现

双向通信机制 ——客户与可连接对象的关系

两个概念 入接口(incoming interface) 出接口(outgoing interface) 组件对象实现入接口,客户通过入接口调用对象提供的功能 客户和组件都需要知道接口的类型信息 出接口(outgoing interface) 客户端提供的COM对象实现出接口 组件端的对象通过出接口调用客户提供的功能 组件提供接口类型信息,客户实现该接口 类似于回调(callback),但是要复杂和灵活得多

出接口 类型信息由组件一方提供 客户提供出接口的实现,实现出接口的COM对象被称为接收器对象(sink) 也是一个COM接口,有IID sink没有CLSID,也不需要类厂 也是一个COM接口,有IID 每个成员函数代表了: 事件event 通知notification 请求request

源对象 or 可连接对象 Connectable object,source 普通的COM对象,支持一个或者多个出接口 提供出接口的类型信息 通过IProvideClassInfo[2]接口 通过typelib

客户与可连接对象之间的两种结构

可连接对象的基本结构

可连接对象 如何管理多个出接口 对于每个出接口,如何管理多个客户连接 每个出接口对应一个连接点对象 通过连接点枚举器管理 通过连接枚举器管理多个连接

实现可连接对象(源对象)(一) 枚举器 内部对象,不需要类厂和CLSID 其含义就如同指针——智能指针 枚举器接口模板 class IEnum<ELT_T> : public IUnknown { virtual HRESULT Next( ULONG celt, ELT_T *rgelt, ULONG *pceltFetched ) = 0; virtual HRESULT Skip( ULONG celt ) = 0; virtual HRESULT Reset( void ) = 0; virtual HRESULT Clone( IEnum<ELT_T>**ppenum ) = 0; };

枚举器的用法 class IStringManager : public IUnknown { virtual IEnumString* EnumStrings(void) = 0; }; void SomeFunc(IStringManager * pStringMan) { String psz; IEnumString * penum; penum=pStringMan->EnumStrings(); while (S_OK == penum->Next(1, &psz, NULL)) { … //Do something with the string in psz and free it } penum->Release(); return;

实现可连接对象(源对象)(二) IEnumConnectionPoints接口 IConnectionPointContainer接口 class IConnectionPointContainer : public IUnknown { virtual HRESULT EnumConnectionPoints(IEnumConnectionPoints **) = 0; virtual HRESULT FindConnectionPoint(const IID *, IConnectionPoint **) = 0; }; IEnumConnectionPoints接口 class IEnumConnectionPoints : public IUnknown virtual HRESULT Next( ULONG cConnections, IConnectionPoint **rgpcn, ULONG *pcFetched) = 0; virtual HRESULT Skip( ULONG cConnections) = 0; virtual HRESULT Reset(void) = 0; virtual HRESULT Clone( IEnumConnectionPoints **ppEnum) = 0;

实现可连接对象(源对象)(三) 连接点和IConnectionPoint接口 连接枚举器 —— 实现IEnumConnections接口 class IConnectionPoint : public IUnknown { virtual HRESULT GetConnectionInterface( IID *pIID) = 0; virtual HRESULT GetConnectionPointContainer( IConnectionPointContainer **ppCPC) = 0; virtual HRESULT Advise( IUnknown *pUnk, DWORD *pdwCookie) = 0; virtual HRESULT Unadvise( DWORD dwCookie) = 0; virtual HRESULT EnumConnections(IEnumConnections**ppEnum) = 0; }; 连接枚举器 —— 实现IEnumConnections接口 允许多个客户连接 每个连接用struct CONNECTDATA来描述

回顾:可连接对象的基本结构

客户与源对象建立连接过程 客户请求IConnectionPointContainer接口 客户调用IConnectionPointContainer::FindConnectionPoint找到连接点对象 客户调用IConnectionPoint::Advise建立与接收器的连接 最后,客户调用IConnectionPoint::Unadvise取消连接,并释放连接点对象

客户方基本结构 客户方实现接收器对象(sink) 建立连接 支持多个与可连接对象之间的连接 一般只实现专用的出接口(IUnknown除外) 不需要类厂、CLSID 与客户代码紧密连接起来 建立连接 1 通过IConnectionPointContainer接口找到连接点对象 2 通过连接点对象建立连接 连接点相当于连接管理器

接收器的实现 class CSomeEventSet : public ISomeEventSet { private: ULONG m_cRef; // Reference count ...... // other private data members public: DWORD m_dwCookie; // Connection key CSomeEventSet (); ~CSomeEventSet(void); //IUnknown members STDMETHODIMP QueryInterface(REFIID, PPVOID); STDMETHODIMP_(DWORD) AddRef(void); STDMETHODIMP_(DWORD) Release(void); STDMETHODIMP SomeEventFunction ( ... ); ...... };

接收器的用法 ISomeEventSet *gpSomeEventSet; ....... // Initialize CSomeEventSet *pSink = new CSomeEventSet; pSink->QueryInterface(IID_ISomeEventSet, pSomeEventSet ); // Reference count is 1 // connections the sink object to the connectable object we have hr=pConnectionPoint->Advise(pSomeEventSet , & pSomeEventSet->m_dwCookie); ....… // disconnections the sink object from the connectable object we have hr=pConnectionPoint->Unadvise( pSomeEventSet->m_dwCookie); // Uninitialize pSink->Release( ); // Reference count is 0

事件的激发和处理 BOOL CSourceObject::FireSomeEvent(IConnctionPoint *pConnectionPoint) { IEnumConnections *pEnum; CONNECTDATA connectionData; if (FAILED(pConnectionPoint->EnumConnections(&pEnum))) return FALSE; while (pEnum->Next(1, & connectionData, NULL) == NOERROR) ISomeEventSet *pSomeEventSet; if (SUCCEEDED(connectionData.pUnk->QueryInterface( IID_ISomeEventSet, (PPVOID)& pSomeEventSet))) pSomeEventSet->SomeEventFunction(); // Trigger event or request pSomeEventSet->Release(); } pEnum->Release(); return TRUE;

与出接口有关的类型信息 客户如何知道出接口?运行时刻?编译时刻? 动态构造接收器对象?动态构造vtable?支持部分成员? 类型信息的协商 通过IProvideClassInfo[2] 能否用标准的接口作为出接口?

用IDispatch接口作为出接口(一) class IDispatch : public IUnknown { public: virtual HRESULT GetTypeInfoCount( UINT *pctinfo) = 0; virtual HRESULT GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) = 0; virtual HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) = 0; virtual HRESULT Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) = 0; };

用IDispatch接口作为出接口(二)

IDispatch出接口的事件激发函数 void CMySourceObj::FireMyMethod (short nInt) { COleDispatchDriver driver; POSITION pos = m_xMyEventSet.GetStartPosition(); LPDISPATCH pDispatch; while (pos != NULL) { pDispatch = (LPDISPATCH) m_xMyEventSet.GetNextConnection(pos); ASSERT(pDispatch != NULL); driver.AttachDispatch(pDispatch, FALSE); TRY driver.InvokeHelper(eventidMyMethod, DISPATCH_METHOD, VT_EMPTY, NULL, (BYTE *) (VTS_I2), nInt); END_TRY driver.DetachDispatch(); }

用连接点机制实现回调的讨论 比传统的回调函数 Tightly coupled vs loosely coupled (COM+) 功能强大,灵活 可以跨进程、跨机器 Tightly coupled vs loosely coupled (COM+) 要求客户与组件同步 没有第三方的参与,所以双方必须保持共识

MFC对连接和事件的支持

用MFC实现源对象 创建工程——支持COM 定义出接口——编辑.odl文件 利用MFC宏加入连接点声明以及连接点对象的定义 在对象构造函数中调用EnableConnections(); 在接口映射表中加入接口IConnectionPointContainer的表项,再加入连接映射表 定义连接点类的虚函数(至少为GetIID) 加入事件激发函数

用MFC在客户程序中实现接收器 初始化 —— AfxOleInit 定义出接口成员类 实现出接口成员类 创建源对象 建立连接和取消连接 完成可触发事件的动作

用MFC实现的例子

ATL实现可连接对象 在IDL中 增加IConnectionPointContainer接口 定义一个用作出接口的automation接口 在coclass中加入出接口,含source属性 增加IConnectionPointContainer接口 在基类列表中增加 IConnectionPointConntainerImpl<CMyClass> 在COM MAP中加入 COM_INTERFACE_ENTRY(IConnectionPointConntainer)

模板类IConnectionPointImpl CMyClass继承IConnectionPointImpl一次或多次 IConnectionPointImpl实现了独立的引用计数 用法:在基类列表中增加 IConnectionPointImpl<CMyClass, &DIID__IEventSet> 加入connection point map,如下 BEGIN_CONNECTION_POINT_MAP(CMyClass) CONNECTION_POINT_ENTRY(DIID__IEventSet) END_CONNECTION_POINT_MAP()

激发事件辅助函数 手工激发事件 利用VC IDE提供的源码产生工具 IConnectionPointImpl包含一个m_vec成员,内含所有已经建立的接收器连接 遍历m_vec数组,逐一调用Invoke函数 利用VC IDE提供的源码产生工具 ATL连接点代理生成器,启动对话框Implement Connection Point 产生名为CProxy_<SinkInterfaceName>的模板类 例如CProxy_IEventSet,它从IConnectionPointImpl派生 对于每一个事件或者请求,都有一个对应的Fire_Xxx成员函数 用模板类代替IConnectionPointImpl基类

Implement Connection Point对话框 ClassView中,在对象类上右键点击选择此项功能

ATL实现连接点:最后的工作 在需要激发事件的地方 增加对IProvideClassInfo2接口的支持 调用CProxy_<Xxxx>提供的辅助函数 增加对IProvideClassInfo2接口的支持 需要typelib的支持 加入基类IProvideClassInfo2Impl 在COM MAP中加入: COM_INTERFACE_ENTRY(IProvideClassInfo2) COM_INTERFACE_ENTRY(IProvideClassInfo)

ATL实现接收器sink IDispEventSimpleImpl IDispEventImpl Event Sink Map 轻量,不需要typelib的支持 IDispEventImpl 需要typelib的支持 Event Sink Map BEGIN_SINK_MAP(CMyCLass) SINK_ENTRY_EX(...) // 适合用于non-UI object SINK_ENTRY(...) // 适合用于UI object END_SINK_MAP

ATL:建立sink和source之间的连接 IDispEventSimpleImpl成员 DispEventAdvise DispEventUnadvise AtlAdviseSinkMap 建立sink与source缺省源接口的连接

VB中使用出接口 使用浏览器控件的事件函数使两个窗口同步

结构化存储(structured storage) 内容: 结构化存储模型 复合文档 永久对象

问题的由来 文件系统的诞生 进展到结构化存储 多个应用程序共享同一个存储设备 文件服务功能的抽象 多个组件共享同一个文件 组件软件存储功能的基本要求 OLE的需求 组件共享句柄方案,如何定位?避免冲突?

文件系统结构

结构化存储

多个组件程序共享一个复合文件

复合文件 文件内部的文件系统 只有两种对象:存储对象和流对象 实现了部分访问和增量访问的功能

流对象 COM库提供实现,实现了IStream接口 class IStream : public IUnknown { public : virtual HRESULT Read (void *pv, unsigned long cb, unsigned long *pcbRead) = 0; virtual HRESULT Write (void *pv, unsigned long cb, unsigned long *pcbWritten) = 0; virtual HRESULT Seek (LARGE_INTEGER dlibMove, unsigned long dwOrigin, ULARGE_INTEGER *plibNewPosition) = 0; virtual HRESULT SetSize (ULARGE_INTEGER libNewSize) = 0; virtual HRESULT CopyTo (LPSTREAM pStm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) = 0; virtual HRESULT Commit (unsigned long dwCommitFlags) = 0; virtual HRESULT Revert ()= 0; virtual HRESULT LockRegion (ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, unsigned long dwLockType) = 0; virtual HRESULT UnlockRegion (ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, virtual HRESULT Stat (STATSTG *pStatStg, unsigned long grfStatFlag) = 0; virtual HRESULT Clone(LPSTREAM * ppStm) = 0; };

存储对象 COM库提供实现,实现了IStorage接口 class IStorage : public IUnknown { virtual HRESULT CreateStream (const WCHAR * , unsigned long , LPSTREAM * ) = 0; virtual HRESULT OpenStream (const WCHAR * , unsigned long , LPSTREAM * ) = 0; virtual HRESULT CreateStorage (const WCHAR * , unsigned long ,LPSTORAGE * ) = 0; virtual HRESULT OpenStorage (const WCHAR* , LPSTORAGE *, unsigned long , SNB , unsigned long , LPSTORAGE * ) = 0; virtual HRESULT CopyTo(unsigned long , IID const *, SNB snbExclude, LPSTORAGE * pStgDest) = 0; virtual HRESULT MoveElementTo(const WCHAR * , LPSTORAGE *,char const * , unsigned long ) = 0; virtual HRESULT Commit (unsigned long ) = 0; virtual HRESULT Revert ()= 0; virtual HRESULT EnumElements (unsigned long , void *,unsigned long , LPENUMSTATSTG * ) = 0; virtual HRESULT DestroyElement (const WCHAR * pwcsName) = 0; virtual HRESULT RenameElement (const WCHAR * pwcsOldName, const WCHAR * pwcsNewName) = 0; virtual HRESULT SetElementTimes(const WCHAR *,FILETIME const *,FILETIME const*, FILETIME const *) = 0; virtual HRESULT SetClass (REFCLSID rclsid) = 0; virtual HRESULT SetStateBits (unsigned long grfStateBits, unsigned long grfMask) = 0; virtual HRESULT Stat (STATSTG *pStatStg, unsigned long grfStatFlag) = 0; };

客户如何获取存储对象和流对象 如何得到指向根存储对象的接口指针? CreateStorage和OpenStorage成员函数得到一个子存储对象,是唯一的途径 CreateStream和OpenStream成员函数得到一个流对象,也是唯一的途径

用结构化存储设计应用(一) 用普通文件组织的文档结构

用结构化存储设计应用(二) 复合文件格式的文档结构

结构化存储特性——访问模式 STGM_CREATE STGM_CONVERT STGM_FAILIFTHERE STGM_DELETEONRELEASE STGM_DIRECT STGM_TRANSACTED STGM_PRIORITY STGM_READ STGM_WRITE STGM_READWRITE STGM_SHARE_DENY_READ STGM_SHARE_DENY_WRITE STGM_SHARE_EXCLUSIVE STGM_SHARE_DENY_NONE

结构化存储特性——事务机制 数据一致性和完整性 操作:Commit、Revert 事务嵌套:以STGM_TRANSACTED标志为基础 事务机制需要消耗较多系统资源 Commit参数: STGC_DEFAULT STGC_OVERWRITE STGC_ONLYIFCURRENT STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE

结构化存储特性——命名规则 根存储对象的名字遵守文件系统的命名约定 长度不超过32个字符 首字符使用大于32的字符,小于32的字符作为首字符有特殊意义 不能使用字符“\”、“/”、“:”和“!” 名字“.”和“..”被保留 名字保留大小写,但比较操作大小写无关

结构化存储特性——增量访问 减少保存和打开文件的时间 降低了应用程序对系统资源的要求 问题: 通过根存储逐层找到目标对象 空间回收

复合文档 结构化存储的具体实现 底层机制:LockBytes对象 把存储介质描述成一般化的字节序列 复合文档API函数 零内存保存特性

复合文档模型 root LockBytes Disk Memory 其他

LockBytes对象 ILockBytes接口 class ILockBytes : public IUnknown { public : virtual HRESULT ReadAt (ULARGE_INTEGER , VOID *pv, unsigned long , unsigned long *) = 0; virtual HRESULT WriteAt (ULARGE_INTEGER , VOID *pv, unsigned long , virtual HRESULT Flush ()= 0; virtual HRESULT SetSize (ULARGE_INTEGER cb) = 0; virtual HRESULT LockRegion (ULARGE_INTEGER , ULARGE_INTEGER , unsigned long ) = 0; virtual HRESULT UnlockRegion (ULARGE_INTEGER , ULARGE_INTEGER , virtual HRESULT Stat (STATSTG *, unsigned long ) = 0; };

复合文档API函数 创建复合文档的API函数 打开复合文档的API函数 与内存句柄有关的一组操作函数 其他 StgCreateDocfile、StgCreateDocfileOnILockBytes 打开复合文档的API函数 StgOpenStorage、StgOpenStorageOnILockBytes 与内存句柄有关的一组操作函数 CreateILockBytesOnHGlobal、GetHGlobalFromILockBytes CreateStreamOnHGlobal、GetHGlobalFromStream 其他

零内存保存特性 意义:资源耗尽之后,保留修改信息 资源预留,对于所有的流对象和存储对象 “Save”操作,只要调用Commit函数即可 “Save As”操作,利用根存储对象上的IRootStorage接口,调用SwitchToFile成员函数,再调用Commit函数即可。

与CLSID的联系 IStorage::SetClass函数把存储对象与CLSID联系起来 GetClassFile函数,从文件到CLSID: 复合文件,直接得到根存储的CLSID 非复合文件: (1) 文件扩展名-〉ProgID-〉CLSID (2) HKEY_CLASSES_ROOT\FileType键提供了匹配规则: HKEY_CLASSES_ROOT FileType {<clsid >} <type id> = <offset>,<cb>,<mask>,<value>

复合文档与COM的关系 复合文档技术以COM为基础 应用程序在处理复合文档时 ->永久对象 把storage或stream直接交给COM组件来处理 COM组件接受storage或stream作为数据存储 多个组件协同处理同一个文件 ->永久对象

永久对象 永久对象 永久接口: 永久接口的成员函数: 永久对象可以实现多个永久接口,但使用时要保持一致性 实现了IPersistXXX接口的COM对象 永久接口: class IPersist : public IUnknown class IPersistStream : public IPersist class IPersistStreamInit : public IPersist class IPersistFile : public IPersist class IPersistStorage : public Ipersist 永久接口的成员函数: GetClassID、IsDirty、Load和Save,…... 永久对象可以实现多个永久接口,但使用时要保持一致性

永久对象用法 永久对象与结构化存储模型结合 永久对象例子 用MFC实现的COM对象 功能:永久状态为一段文本,使用永久接口对文本维护 实现了IPersistStream和一个自动化接口

复合文档例子

复合文档查看工具