分享
 
 
 

COM 组件设计与应用(十七)——持续性

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

COM 组件设计与应用(十七)——持续性

作者:杨老师

下载源代码

一、前言

我们写程序,经常需要实现这样的需求:

例一、程序运行产生一个窗口,用户关闭的时候需要记录窗口的位置,以便下次运行时保持位置不变;

例二、由于程序运行时间很长,今天执行一部分,明天继续执行。那么在下次运行前要恢复前次的状态;

... ... ... ...

智慧的老师:以上这些需求,如何实现呢?

懵懂的学生:这个简单,只要在程序退出前提取必要的信息保存到文件中,下次运行时再从文件中读出来,设置一下就OK了。

智慧的老师:恩,不错,这位同学的思想值得表扬。

懵懂的学生:不好意思,这都要感谢老师的栽培,我对您的景仰如滔滔江水......

智慧的老师:别臭P了,我话还没有说完那......如果你需要提取和保存的信息很多,结构很复杂......怎么办?

懵懂的学生:也好办,我设计一个结构来记录这些信息。

智慧的老师:恩......不错。但如果这些信息提供方是别人写的模块,并且随着版本的不同还经常变化,你怎么办?

懵懂的学生:... ...

智慧的老师:解决这些问题的方法是---持续性。

二、原理

持续性,也叫永久性。组件方提供

IPersistXXX 接口,调用者(容器)提供存储介质,比如文件啦、内存啦、注册表啦、流啦、文本啦......啦啦拉。需要保存的时候,调用者通过

IPersistXXX::Save()

接口函数让组件去自己存储属性信息,而调用者根本不用关心存储格式和存储内容;需要还原状态的时候,调用者打开存储介质,然后同样调用

IPersistXXX::Load() 接口函数让组件自己去读取属性信息并完成初始化的设置。

目前,微软定义了如下各种类型的持续性接口,足够满足你的需求了。我们只要在自己写的组件中实现其中一个或几个持续性接口,那么调用者就可以按照统一的方式和我们的组件协商完成属性信息的保存和状态还原了。

持续性接口

简要说明

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 中插入控件,<param name="属性名称" value="值" 这样的形式你应该见过吧?!

在下一回的文章中,我们介绍这个接口。因为在 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 接口函数。

手工在 h 头文件中增加函数声明: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;

elsereturn 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;

}

四、小结

下载示例程序后,结合本文仔细阅读代码,并试着运行看看效果。如果你理解了,那么你能自己实现

IPersistFile 接口吗?你能自己实现

IPersistStorage

接口吗?你实现的持续性接口越多,别人使用你的组件就越方便,也就是说你的组件就能大卖特卖啦,祝你为中国软件事业做贡献的同时多多赚钱:-)下回我们用

IPersistPropertyBag 接口实现持续性属性包功能,别忘了看呦......

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有