分类: C/C++
2008-03-18 14:24:09
持续性接口 | 简要说明 | |
IPersist | 所有持续性接口的根,下面的接口大多从它派生出来。这个接口很简单,只有一个函数 GetClassID()它返回组件的 CLSID 号,以便调用者能保存这个号为将来 CoCreateInstance() 启动组件用。 实现这个函数也很简单,只要返回你组件中的 CLSID_XXX 即可,或者比较省事的方法是返回 GetObjectCLSID() 。 | |
IPersistStream |
派生自 IPersist,并增加了4个函数,从流(IStream)中读写组件属性信息。 | |
IsDirty() | 组件内部属性是否发生了变化。为调用者是否需要保存信息提供依据 | |
Load() | 从 IStream 中读入信息,初始化组件属性 | |
Save() | 把属性信息保存到 IStream 中 | |
GetSizeMax() | 返回信息尺寸,以便调用者事先开辟空间 | |
IPersistStreamInit | 派生自 IPersistStream,并再增加了一个函数 InitNew() 用来完成一个默认的组件属性初始化。 这个持续性接口是最常用的,本文示例中就实现了该接口。 | |
IPersistMemory | 和 IPersistStreamInit 类似,但使用的是内存块,而不是大小可变化的 IStream 流。 | |
IPersistStorage | 和 IPersistStream 类似,但保存属性信息使用的是存储 IStorage,一个 IStorage 中可以有多个 IStream。 | |
IPersistFile | 和 IPersistStream 类似,但存储介质为文件。 | |
IPersistPropertyBag | 使用属性包(属性名、属性值)的文本方式保存信息。在 IE 浏览器中,HTML 嵌入 ActiveX 控件通常使用这个方法。 在 HTML 中插入控件, 这样的形式你应该见过吧?! 在下一回的文章中,我们介绍这个接口。因为在 ActiveX 中,它太常用了。 | |
IPersistPropertyBag2 | 扩展了 IPersistPropertyBag 接口。提供了更丰富一些的属性管理用函数。 | |
IPersistMoniker | 用于命名(moniker)存储和读取状态的持续性接口。 | |
IPersistHistory | 运行于 IE 上,想在用户浏览 WEB 页面时存储和读取状态的持续性接口。 |
三、持续性接口组件的实现
示例程序分别在 vc6.0 和 vc.net 上实现了 IPersistStreamInit 接口的 COM 组件和调用举例。组件完成的功能是计算素数,你第一次运行的时候,会得到第一个素数2,然后是3,5,7,11......下班时间到了,今天就运行到这里。于是调用者开辟一个流来保存组件的属性信息。明天继续运行的时候,从流中原换组件状态,开始了新的计算 13,17,19,23......
这个示例应用完全是假设性的,其实没有什么实用价值,只是演示了 IPersistStreamInit 接口的实现方法。另外,关于建立流(IStream)的方法,请参阅《COM 组件设计与应用(一)》。
1、建立一个 ATL 工程项目。
2、增加 ATL 组件类,vc.net 使用者注意不要选择“属性化编程”方式,其它的设置全部使用默认方法。当然你愿意适当地改变选择也无所谓。
3、设计完成你的组件功能。
示例程序中,实现了一个接口函数 GetNext() 负责计算下一个素数。
4、添加IPersistStreamInit 接口。
class ATL_NO_VTABLE Cxxx : public CComObjectRootEx<...> , public CComCoClass<...>, ...... public IPersistStreamInit // 手工添加持续性接口 { ...... BEGIN_COM_MAP(Cxxx) ...... // 手工添加接口映射表入口 COM_INTERFACE_ENTRY(IPersistStreamInit) // 表示如果要取得 IPersistStream 指针,则返回 IPersistStreamInit 指针 COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStreamInit) // 表示如果要取得 IPersist 指针,则返回 IPersistStremInit 指针 COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersistStreamInit) END_COM_MAP()5、完成 IPersistStreamInit 接口函数。
public: // IPersist STDMETHOD(GetClassID)(/*[out]*/CLSID * pClassID); // IPersistStream STDMETHOD(IsDirty)(void); STDMETHOD(Load)(/*[in]*/IStream *pStm); STDMETHOD(Save)(/*[in]*/IStream *pStm,/*[in]*/BOOL fClearDirty); STDMETHOD(GetSizeMax)(/*[out]*/ULARGE_INTEGER *pcbSize); // IPersistStreamInit STDMETHOD(InitNew)(void);手工在 cpp 文件中增加函数实现:
// IPersist STDMETHODIMP Cxxx::GetClassID(/*[out]*/CLSID * pClassID) { *pClassID = GetObjectCLSID(); return S_OK; } // IPersistStream STDMETHODIMP Cxxx::IsDirty(void) { if( 数据已经改变,需要保存 ) return S_OK; else return S_FALSE; } STDMETHODIMP Cxxx::Load(/*[in]*/IStream *pStm) { return pStm->Read( 读到哪里, 读多长字节, NULL); } STDMETHODIMP Cxxx::Save(/*[in]*/IStream *pStm,/*[in]*/BOOL fClearDirty) { if( fClearDirty ) 清除内部表示数据变化的变量; return pStm->Write( 需要保存的数据指针, 写多长字节, NULL ); } STDMETHODIMP Cxxx::GetSizeMax(/*[out]*/ULARGE_INTEGER *pcbSize) { pcbSize->LowPart = 需要保存数据长度的低位; pcbSize->HighPart = 需要保存数据长度的高位;// 一般都是0,难道你的数据长度都超过了 4G? return S_OK; } // IPersistStreamInit STDMETHODIMP Cxxx::InitNew(void) { 内部属性数据默认初始化; 设置或清除内部表示数据变化的变量; return S_OK; }四、小结