com :可连接对象 & 结构化存储
DESCRIPTION
COM :可连接对象 & 结构化存储. 潘爱民 http://www.icst.pku.edu.cn/CompCourse. 内容. 复习: COM 基础 可连接对象 结构化存储. 进程 A. 进程 B. 机器 A. 机器 B. Apartment. Apartment. 安全通道. 双接口. proxy. VB 客户. ORPC. COM 库 ( OLE32.DLL ). COM 库 ( OLE32.DLL ). COM 库 (SCM, RPCSS.EXE ). Registry. 复习: COM 基础. COM 客户. COM 组件. - PowerPoint PPT PresentationTRANSCRIPT
![Page 1: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/1.jpg)
COMCOM :可连接对象 :可连接对象 & & 结构化存结构化存储储
潘爱民
http://www.icst.pku.edu.cn/CompCourse
![Page 2: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/2.jpg)
内容内容复习: COM 基础可连接对象结构化存储
![Page 3: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/3.jpg)
复习:复习: COMCOM 基础基础
Apartment
COM 组件COM 客户
{IXxx *p;
p->…
}
Apartment
proxy
COM 库 (SCM, RPCSS.EXE)
COM 库 (OLE32.DLL) COM 库 (OLE32.DLL)
Registry
进程 A 进程 B机器 A 机器 B
安全通道
ORPC
双接口VB 客户
![Page 4: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/4.jpg)
聚合模型的关键聚合模型的关键
对象 B
IOtherInterface
对象 A
ISomeInterface
客户程序 调用
传递
调用 数据如何传递?
![Page 5: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/5.jpg)
可连接对象可连接对象 (connectable object)(connectable object)
内容:– 可连接对象结构模型– 实现可连接对象 ( 源对象 )– 客户 - 源对象 - 接收器的协作过程– 可连接对象的程序实现
![Page 6: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/6.jpg)
双向通信机制双向通信机制 ——客户与可连接对象的关系——客户与可连接对象的关系
客户
接收器 可连接对象
客户把接收器的接
口指针传给对象
可连接对象调用接
收器的接口成员
![Page 7: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/7.jpg)
两个概念两个概念 入接口 (incoming interface)– 组件对象实现入接口,客户通过入接口调用对象提
供的功能– 客户和组件都需要知道接口的类型信息
出接口 (outgoing interface)– 客户端提供的 COM 对象实现出接口– 组件端的对象通过出接口调用客户提供的功能– 组件提供接口类型信息,客户实现该接口– 类似于回调 (callback) ,但是要复杂和灵活得多
![Page 8: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/8.jpg)
出接口出接口 类型信息由组件一方提供 客户提供出接口的实现,实现出接口的 COM
对象被称为接收器对象 (sink)– sink 没有 CLSID ,也不需要类厂
也是一个 COM 接口,有 IID 每个成员函数代表了:– 事件 event– 通知 notification– 请求 request
![Page 9: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/9.jpg)
源对象 源对象 or or 可连接对象可连接对象Connectable object , source普通的 COM 对象,支持一个或者多个
出接口提供出接口的类型信息– 通过 IProvideClassInfo[2] 接口– 通过 typelib
![Page 10: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/10.jpg)
客户与可连接对象之间的两种结构客户与可连接对象之间的两种结构
客户
接收器
可连接对象
可连接对象
可连接对象
客户
接收器
客户
接收器
客户
接收器
可连接对象
![Page 11: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/11.jpg)
可连接对象的基本结构可连接对象的基本结构
接收器
可连接对象IConnectionPointContainer
连接点对象
连接点对象
IConnectionPoint
IConnectionPoint
接收器
枚举器
枚举器
![Page 12: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/12.jpg)
可连接对象可连接对象如何管理多个出接口– 每个出接口对应一个连接点对象– 通过连接点枚举器管理
对于每个出接口,如何管理多个客户连接– 通过连接枚举器管理多个连接
![Page 13: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/13.jpg)
实现可连接对象实现可连接对象 (( 源对象源对象 )()( 一一 ))
枚举器– 内部对象,不需要类厂和 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;};
![Page 14: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/14.jpg)
枚举器的用法枚举器的用法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;}
![Page 15: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/15.jpg)
实现可连接对象实现可连接对象 (( 源对象源对象 )()( 二二 )) 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;
};
![Page 16: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/16.jpg)
实现可连接对象实现可连接对象 (( 源对象源对象 )()( 三三 ))
连接点和 IConnectionPoint 接口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 来描述
![Page 17: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/17.jpg)
回顾:可连接对象的基本结构回顾:可连接对象的基本结构
接收器
可连接对象IConnectionPointContainer
连接点对象
连接点对象
IConnectionPoint
IConnectionPoint
接收器
枚举器
枚举器
![Page 18: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/18.jpg)
客户与源对象建立连接过程客户与源对象建立连接过程 客户请求 IConnectionPointContainer 接口 客户调用 IConnectionPointContainer::Find
ConnectionPoint 找到连接点对象 客户调用 IConnectionPoint::Advise 建立与
接收器的连接 最后,客户调用 IConnectionPoint::Unadvis
e 取消连接,并释放连接点对象
![Page 19: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/19.jpg)
客户方基本结构客户方基本结构 客户方实现接收器对象 (sink)– 支持多个与可连接对象之间的连接– 一般只实现专用的出接口( IUnknown 除外)– 不需要类厂、 CLSID– 与客户代码紧密连接起来
建立连接– 1 通过 IConnectionPointContainer 接口找到连
接点对象– 2 通过连接点对象建立连接– 连接点相当于连接管理器
![Page 20: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/20.jpg)
接收器的实现接收器的实现class CSomeEventSet : public ISomeEventSet { private: ULONG m_cRef; // Reference count
...... // other private data members public: DWORD m_dwCookie; // Connection key public: CSomeEventSet (); ~CSomeEventSet(void);
//IUnknown members STDMETHODIMP QueryInterface(REFIID, PPVOID); STDMETHODIMP_(DWORD) AddRef(void); STDMETHODIMP_(DWORD) Release(void);
STDMETHODIMP SomeEventFunction ( ... ); ......};
![Page 21: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/21.jpg)
接收器的用法接收器的用法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
![Page 22: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/22.jpg)
事件的激发和处理事件的激发和处理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;}
![Page 23: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/23.jpg)
与出接口有关的类型信息与出接口有关的类型信息 客户如何知道出接口?运行时刻?编译时刻?
动态构造接收器对象?动态构造 vtable ?支持部分成员?
类型信息的协商– 通过 IProvideClassInfo[2]
能否用标准的接口作为出接口?
![Page 24: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/24.jpg)
用用 IDispatchIDispatch 接口作为出接口接口作为出接口 (( 一一 ))
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;
};
![Page 25: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/25.jpg)
用用 IDispatchIDispatch 接口作为出接口接口作为出接口 (( 二二 ))
客户把接收器接口指针传给对象
客户
接收器
源对象
客户获取出接口的类型信息
源对象调用Invoke成员函数
IDispatch
IProvideClassInfo
连接点对象
![Page 26: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/26.jpg)
IDispatchIDispatch 出接口的事件激发函数出接口的事件激发函数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_TRYdriver.DetachDispatch();
}}
![Page 27: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/27.jpg)
用连接点机制实现回调的讨论用连接点机制实现回调的讨论比传统的回调函数– 功能强大,灵活– 可以跨进程、跨机器
Tightly coupled vs loosely coupled (COM+)– 要求客户与组件同步– 没有第三方的参与,所以双方必须保持共识
![Page 28: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/28.jpg)
MFCMFC 对连接和事件的支持对连接和事件的支持
源对象(CCmdTarget派生类)
m_xConnPtContainer
出接口 1
IConnectionPointContainer
调用 EnableConnections进行初始化
连接映射表
GetExtraConnectionPoints函数指定
内置连接点枚举器
连接点对象 1
出接口 2
出接口 n
连接点对象 2
连接点对象 n
事件 1
事件 2
……
事件 n
事件激发函数激发事件或请求:在特定的连接点上,对所有的连接向接收器发送事件或请求
枚举连接点
发送事件或请求:调用 Invoke函数
![Page 29: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/29.jpg)
用用 MFCMFC 实现源对象实现源对象 创建工程——支持 COM 定义出接口——编辑 .odl 文件 利用 MFC 宏加入连接点声明以及连接点对象
的定义 在对象构造函数中调用 EnableConnections(); 在接口映射表中加入接口 IConnectionPointC
ontainer 的表项,再加入连接映射表 定义连接点类的虚函数 ( 至少为 GetIID) 加入事件激发函数
![Page 30: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/30.jpg)
用用 MFCMFC 在客户程序中实现接收器在客户程序中实现接收器
初始化 —— AfxOleInit定义出接口成员类实现出接口成员类创建源对象建立连接和取消连接完成可触发事件的动作
![Page 31: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/31.jpg)
用用 MFCMFC 实现的例子实现的例子
![Page 32: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/32.jpg)
ATLATL 实现可连接对象实现可连接对象 在 IDL 中– 定义一个用作出接口的 automation 接口– 在 coclass 中加入出接口,含 source 属性
增加 IConnectionPointContainer 接口– 在基类列表中增加– IConnectionPointConntainerImpl<CMyClass>– 在 COM MAP 中加入– COM_INTERFACE_ENTRY(IConnectionPointC
onntainer)
![Page 33: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/33.jpg)
模板类模板类 IConnectionPointImplIConnectionPointImpl
CMyClass 继承 IConnectionPointImpl 一次或多次– IConnectionPointImpl 实现了独立的引用计数– 用法:在基类列表中增加– IConnectionPointImpl<CMyClass, &DIID__IEventSet>
加入 connection point map ,如下BEGIN_CONNECTION_POINT_MAP(CMyClass)
CONNECTION_POINT_ENTRY(DIID__IEventSet)
END_CONNECTION_POINT_MAP()
![Page 34: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/34.jpg)
激发事件辅助函数激发事件辅助函数 手工激发事件
– IConnectionPointImpl 包含一个 m_vec 成员,内含所有已经建立的接收器连接
– 遍历 m_vec 数组,逐一调用 Invoke 函数 利用 VC IDE 提供的源码产生工具
– ATL 连接点代理生成器,启动对话框 Implement Connection Point
– 产生名为 CProxy_<SinkInterfaceName> 的模板类• 例如 CProxy_IEventSet ,它从 IConnectionPointImpl 派生• 对于每一个事件或者请求,都有一个对应的 Fire_Xxx 成员函数
– 用模板类代替 IConnectionPointImpl 基类
![Page 35: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/35.jpg)
Implement Connection PointImplement Connection Point 对话框对话框
创建对象时选择 Connection Point ClassView 中,在对象类上右键点击选择此项
功能
![Page 36: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/36.jpg)
ATLATL 实现连接点:最后的工作实现连接点:最后的工作 在需要激发事件的地方– 调用 CProxy_<Xxxx> 提供的辅助函数
增加对 IProvideClassInfo2 接口的支持– 需要 typelib 的支持– 加入基类 IProvideClassInfo2Impl– 在 COM MAP 中加入:– COM_INTERFACE_ENTRY(IProvideClassInfo2)– COM_INTERFACE_ENTRY(IProvideClassInfo)
![Page 37: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/37.jpg)
ATLATL 实现接收器实现接收器 sinksink
IDispEventSimpleImpl– 轻量,不需要 typelib 的支持
IDispEventImpl– 需要 typelib 的支持
Event Sink MapBEGIN_SINK_MAP(CMyCLass)
SINK_ENTRY_EX(...) // 适合用于 non-UI objectSINK_ENTRY(...) // 适合用于 UI object
END_SINK_MAP
![Page 38: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/38.jpg)
ATLATL :建立:建立 sinksink 和和 sourcesource 之间的之间的连接连接IDispEventSimpleImpl 成员– DispEventAdvise– DispEventUnadvise
AtlAdviseSinkMap– 建立 sink 与 source 缺省源接口的连接
![Page 39: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/39.jpg)
VBVB 中使用出接口中使用出接口使用浏览器控件的事件函数使两个窗口
同步
![Page 40: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/40.jpg)
结构化存储结构化存储 (structured storage)(structured storage)
内容:– 结构化存储模型– 复合文档–永久对象
![Page 41: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/41.jpg)
问题的由来问题的由来 文件系统的诞生– 多个应用程序共享同一个存储设备– 文件服务功能的抽象
进展到结构化存储– 多个组件共享同一个文件– 组件软件存储功能的基本要求– OLE 的需求– 组件共享句柄方案,如何定位?避免冲突?
![Page 42: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/42.jpg)
文件系统结构文件系统结构
根目录
子目录 1 子目录 2
子目录 11 子目录 21
文件
文件
...... ......
目录
文件
![Page 43: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/43.jpg)
结构化存储结构化存储
根存储
子存储 1 子存储 2
子存储 11 子存储 21
流
流
...... ......
存储
流
.整个文件
![Page 44: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/44.jpg)
多个组件程序共享一个复合文件多个组件程序共享一个复合文件
根存储
子存储 1 子存储 2
子存储 11 子存储 21
流
流
...... ......
.客户程序
组件程序 2
组件程序 3
组件程序 1
![Page 45: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/45.jpg)
复合文件复合文件文件内部的文件系统
只有两种对象:存储对象和流对象
实现了部分访问和增量访问的功能
![Page 46: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/46.jpg)
流对象流对象 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,
unsigned long dwLockType) = 0;
virtual HRESULT Stat (STATSTG *pStatStg, unsigned long grfStatFlag) = 0;
virtual HRESULT Clone(LPSTREAM * ppStm) = 0;
};
![Page 47: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/47.jpg)
存储对象存储对象 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;
};
![Page 48: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/48.jpg)
客户如何获取存储对象和流对象客户如何获取存储对象和流对象
如何得到指向根存储对象的接口指针?
CreateStorage 和 OpenStorage 成员函数得到一个子存储对象,是唯一的途径
CreateStream 和 OpenStream 成员函数得到一个流对象,也是唯一的途径
![Page 49: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/49.jpg)
用结构化存储设计应用用结构化存储设计应用 (( 一一 )) 用普通文件组织的文档结构
文件头
第一章偏移
第二章偏移
......
第 n章偏移
章信息头
第一节偏移
第二节偏移
章信息头
......
章信息头
第一节偏移
第二节偏移
......
节信息头
文本信息
图片信息
节信息头
......
节信息头
文本信息
表格信息
......
![Page 50: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/50.jpg)
用结构化存储设计应用用结构化存储设计应用 (( 二二 ))
根存储
第一章 第二章
第一节
文件头
章信息
............
第二节
节信息 图片
格式信息 位图数据
第二章
第一节 章信息第二节
节信息 表格
格式信息 表格数据
......
......
......
复合文件格式的文档结构
![Page 51: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/51.jpg)
结构化存储特性——结构化存储特性——访问模式访问模式 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
![Page 52: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/52.jpg)
结构化存储特性——事务机制结构化存储特性——事务机制 数据一致性和完整性 操作: Commit 、 Revert 事务嵌套:以 STGM_TRANSACTED 标志为基础 事务机制需要消耗较多系统资源 Commit 参数:
– STGC_DEFAULT– STGC_OVERWRITE– STGC_ONLYIFCURRENT– STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE
![Page 53: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/53.jpg)
结构化存储特性——命名规则结构化存储特性——命名规则根存储对象的名字遵守文件系统的命名约定长度不超过 32 个字符首字符使用大于 32 的字符,小于 32 的字符作
为首字符有特殊意义 不能使用字符“ \” 、“ /” 、“ :” 和“ !” 名字“ .” 和“ ..” 被保留 名字保留大小写,但比较操作大小写无关
![Page 54: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/54.jpg)
结构化存储特性——增量访问结构化存储特性——增量访问减少保存和打开文件的时间
降低了应用程序对系统资源的要求
问题:– 通过根存储逐层找到目标对象–空间回收
![Page 55: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/55.jpg)
复合文档复合文档结构化存储的具体实现
底层机制: LockBytes 对象–把存储介质描述成一般化的字节序列
复合文档 API 函数
零内存保存特性
![Page 56: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/56.jpg)
LockBytes
复合文档模型复合文档模型
root
Disk其他
Memory
![Page 57: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/57.jpg)
LockBytesLockBytes 对象对象 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 ,unsigned long *) = 0;
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 ,
unsigned long ) = 0;virtual HRESULT Stat (STATSTG *, unsigned long ) = 0;
};
![Page 58: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/58.jpg)
复合文档复合文档 APIAPI 函数函数 创建复合文档的 API 函数
– StgCreateDocfile 、 StgCreateDocfileOnILockBytes
打开复合文档的 API 函数– StgOpenStorage 、 StgOpenStorageOnILockBytes
与内存句柄有关的一组操作函数– CreateILockBytesOnHGlobal 、 GetHGlobalFromILockByt
es– CreateStreamOnHGlobal 、 GetHGlobalFromStream
其他
![Page 59: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/59.jpg)
零内存保存特性零内存保存特性意义:资源耗尽之后,保留修改信息
资源预留,对于所有的流对象和存储对象
“Save”操作,只要调用 Commit 函数即可
“Save As”操作,利用根存储对象上的 IRootStorage 接口,调用 SwitchToFile 成员函数,再调用 Commit 函数即可。
![Page 60: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/60.jpg)
与与 CLSIDCLSID 的联系的联系 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> <type id> = <offset>,<cb>,<mask>,<value>
![Page 61: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/61.jpg)
复合文档与复合文档与 COMCOM 的关系的关系 复合文档技术以 COM 为基础
应用程序在处理复合文档时– 把 storage 或 stream直接交给 COM 组件来处理– COM 组件接受 storage 或 stream 作为数据存储– 多个组件协同处理同一个文件
->永久对象
![Page 62: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/62.jpg)
永久对象永久对象 永久对象
– 实现了 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 ,… ...
永久对象可以实现多个永久接口,但使用时要保持一致性
![Page 63: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/63.jpg)
永久对象用法永久对象用法永久对象与结构化存储模型结合
永久对象例子– 用 MFC 实现的 COM 对象– 功能:永久状态为一段文本,使用永久接口
对文本维护– 实现了 IPersistStream 和一个自动化接口
![Page 64: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/64.jpg)
复合文档例子复合文档例子
![Page 65: COM :可连接对象 & 结构化存储](https://reader031.vdocuments.site/reader031/viewer/2022012303/5681445c550346895db0f526/html5/thumbnails/65.jpg)
复合文档查看工具复合文档查看工具