2002-10-17
呵呵,居然快过了一年了。从去年12月份重新上岗到现在,真是风风雨雨的一年。先是去做宽带计费,每天和radius打交道;后来又去做网管软件,每天同ucd-snmp和mib斗争。去年写的那个property manager在我的那个网管软件的节点管理模块里起了至关重要的作用。不仅节省了很多开发时间(省去了数十个dialog),而且大大方便了用户操作,节省了培训时间。当然,项目作完了,我一如既往的得到了应有的下场:即不让我接新的项目,也不让我开发该网管的Linux版,甚至不让我上班。于是我又成了SOHO,只不过这次有人给我发薪水。好像每年的这个时候,我都清闲的很。既然有时间就继续Looking吧。
在这里我想表达一下我对VCHELP的谢意和敬意。首先,我想表达一下我对VCHELP这样一个有专业水准的技术网站的敬意。我在编写我的那个网管软件的节点管理模块时,在VCHELP上下载并阅读了大量的技术文档和源代码,这些资料对我完成工作的帮助是无可估量的。其次,我想表达一下对VCHELP的谢意。因为有了个人专栏,我的这些无聊帖子才又有了安身之处。
在以前的帖子里,我曾经谈到过RTTI的实现问题。DELPHI使用编译器导出对象的RTTI;VC对于基本数据类型使用编译器导出,对于CObject子类使用宏在描述。在过去的一年中,为了写一个Linux下的网络节点管理模块,我曾经学习了一下KDE/QT编程。我发现QT在RTTI的处理上又有其独特的解决方案。QT在标准C++编译器的基础上,增加了一个预编译器。使用预编译器的关键字来定义属性。这看上去像是DELPHI和VC的折中,不过在开发效率上要比VC好的多,在独立性上要比Delphi好的多。
在以前的帖子中,探讨了属性编辑器的大多数实现方式。但,自定义属性编辑方式好像好像还没有提到过。在property manager中,proerty editor并不知道如何编辑属性。property editor只是提供一个EDIT窗体、一个ComboBox窗体和一个Button窗体。property editor甚至不知道这三个窗体的具体控制方式。property editor只是控制他们的position和visible。控制这3个窗体行为的是自定义属性编辑方式组件。自定义属性编辑方式组件是一个COM对象。每种数据类型对应这一个自定义属性编辑方式组件。自定义属性编辑方式COM组件的接口的描述如下:
interface IPropertyEditor: IUnknown
{
// 获取属性编辑方式的属性,它是由枚举LEPROPERTYEDITORATTR中的元素以“或”的形式返回
HRESULT _stdcall GetAttributes([out, retval] long * Result );
// 当选择了多个对象的时候,该函数判断这些对象的属性值是否相等。
HRESULT _stdcall AllEqual([in] IEnumVARIANT * pValue, [out, retval] VARIANT_BOOL * Result );
// 当具有PEA_VALUELIST属性时,该函数决定是否根据List中的内容补全用户的输入。这有点像IE的地址输入窗体。
HRESULT _stdcall AutoFill([out, retval] VARIANT_BOOL * Result );
// 编辑
HRESULT _stdcall Edit([in] VARIANT * pValue );
// 获取property editor中edit窗体的最大输入长度
HRESULT _stdcall GetEditLimit([out, retval] long * Result );
// 格式化并返回属性的字符表示。例如IDispatch的子interface的字符串表示是接口的名字。
HRESULT _stdcall GetValue([in] VARIANT * pValue, [out, retval] LPSTR * Result );
// 释放分配的字符串
HRESULT _stdcall ReleaseValue([in] LPSTR pValue );
// 获取combobox的list内容
HRESULT _stdcall GetValues([in, out] LPSTR ** pValues, [out, retval] long * Result );
// 初始化
HRESULT _stdcall Initialize([in] IUnknown * pTypeInfo, [in] long Parent );
// 赋值
HRESULT _stdcall SetValue([in] VARIANT * pValue, [in] LPSTR pText );
// 在owner draw属性时,绘制item。
HRESULT _stdcall ListDrawValue([in] LPSTR pValue, [in] long DC, [in] long Index, [in] long x, [in] long y, [in] long cx, [in] long cy, [in] long Selected );
// 在owner draw属性时,获取item的高
HRESULT _stdcall ListMeasureHeight([in] LPSTR pValue, [in] long DC, [out, retval] long * Result );
// 在owner draw属性时,获取item的宽
HRESULT _stdcall ListMeasureWidth([in] LPSTR pValue, [in] long DC, [out, retval] long * Result );
};
[
uuid(8B68443D-56BD-49CE-A898-72CBD3CC0590),
version(1.0)
]
typedef enum tagLEPROPERTYEDITORATTR
{
PEA_VALUELIST = 1, // 是否有Combox选择?
PEA_SORTLIST = 2, // 对于Combox中的列表内容是否排序?
PEA_DIALOG = 4, // 是否有编辑对话框?
PEA_MULTISELECT = 8, // 是否支持多选?
PEA_READONLY = 16, // 是否只读?
PEA_OWNERDRAWLIST = 32 // 是否Combox支持Owner Draw?
} LEPROPERTYEDITORATTR;
从IPropertyEditor的interface内容可以看到,它控制了属性编辑的方方面面。不过我得承认,这东西可不是我自己想出来的。我只是把Delphi的TPropertyEditor原样照抄并封装在COM接口里而已。剩下的工作就只是每种数据类型写一个COM组件,一共要写十几个COM组件。那种感觉真是够烦的。在上面提到的网络节点管理模块中,甚至还有一个编辑mib节点的组件。到这里我可以说,它基本满足了我的系统规划。
尽管看上却这个property manager很烦琐,但实际它对外界只有一个接口函数BOOL SetObjects(IEnumVARIANT *pObjects);。
写到这里看来得同property manager说bye-bye了。下面的任务就要进行实体建模了。这恐怕又是一大堆的COM组件。