很近没有更新了,闲话少说,直奔主题。
微软做任何技术的思路:在实现一个标准的时候,往往预留出一个通用的扩展机制。呃,貌似很多大公司都是如此,通过扩展把开发者跟自己捆绑。举例:微软的ie可以嵌入ActiveX控件、可以用BHO扩展;richedit中支持OLE扩展。这种扩展机制主要是基于其OLE框架,这也是微软操作系统框架的基石。开发层面目前的趋势是,淡化OLE强调.NET,有一种无奈叫骑虎难下,有一种错误叫脱离群众,在开发平台、技术百花齐放,开发资源极大丰富的今天,开发者对微软的依赖已经不那么强烈,以至于现在的Windows开发者有点小苦逼,尤其是C++开发者有一种被抛弃的感觉,扯远了。
谈谈OLE/COM/ACTIVEX的关系。很模糊,有一些说不清,有历史原因,也有普及程度关系。侯捷说玩模板有三境界:会用标准库,读懂标准库源码,会用模板做设计。我觉得COM相关的也是如此,使用COM组件往往是容易。我的理解:OLE是技术规范,COM是语言规范,而ACTIVEX则是用这2东东来实做的可服用组件的称谓。对于OLE的支撑主要在MFC库中,而ATL库则是更纯粹的OLE/COM框架。MFC跟OFFICE有一衣带水的关系,OFFICE的应用框架促使着MFC的发展(早期如此,但UI方面早已分道扬镳),OFFICE的应用模型也就是MFC的应用开发模型。
之所以提及MFC又OFFICE,只是想说通用的扩展机制没有那么多条条框框,即便是ACTIVEX框架这种东西。对于OLE实践,也就是微软最热衷,其中Windows操作系统和OFFICE系列软件要最典型,其它则很牵强。OLE技术标准接口只有极少数是必须实现,而大部分则是可选实现或者部分实现,其中richedit更是如此。OLE对服务器和客户端都做了行为规范,如果一方(一般是服务器)自行决定如何实施,则另一方也只需对应实现。
呃,我说了这么多,只是为了阐述我的险恶用心,或许没人明白。ATL框架定义了四个标准导出函数用于规范注册、反注册、加载、卸载,这些跟实际的OLE功能无关,尤其是在richedit扩展中。或许你在网上诸多示例中看到用ATL模板创建一个控件然后如何简单的插入位图就以为掌握了核心科技,那么我就要泼冷水了,这些东西无关大局。既然Windows能用OLE搭建框架,既然MFC可以实践OLE,那么我们也可以用纯正的C++代码玩OLE,我的意思无非就是没必要遵循ATL,也没必要一定去注册一个东西,问题的核心不是这些东西,目前我们仅仅是为了插入一个动画。
Richedit是一个不完全的OLE实践,前面提到能完全实践OLE的框架不多。因为richedit实现了图文混排,所以在IM领域很受欢迎,尤其是早期(现在基于chromium的扩展或许可以改变现状)。Richedit是一个容器,可以容纳OLE控件进入,典型的扩展就是动画控件。基于ATL框架开发,你可以实现一个标准的控件,但当你面对一个非标准的容器时,那些条条框框显得不是那么重要,这也是为什么能做好动画控件不容易的原因。
根据我的调查(呃,通过实践,通过QueryInterface观察),我发现实现一个richedit中的动画控件只需要实现二个接口:IOleObject、IViewObject2,前者为了融入到richedit环境中,后者为了渲染显示。由于richedit默认只喜好无窗口模式,所以针对IOleInPlaceSiteWindowless之类的,你去实现意义也不大,因为人家容器不认你,当然还有IPersist系列接口,对于标准的环境有用(比如IDE),但这里并不是很需要,所以认清核心问题能减少很多困惑。更显然的是我的控件没有用ATL框架,因为此控件脱离了richedit环境生存的意义也不大,更有甚者我没必要让使其成为标准(也没可能),仅仅是为了在一个系统中的richedit中更好地展示。实现的接口越少,引入的麻烦越少,这样才能使精力集中在主要问题上。
综上所述,我的控件是一个C++类,只实现了两个接口:
X_INTERFACE_PART(IMRichPicture, IID_IOleObject, OleObject)
X_INTERFACE_PART(IMRichPicture, IID_IViewObject, ViewObject)
X_INTERFACE_PART(IMRichPicture, IID_IViewObject2, ViewObject)
X_END_INTERFACE_MAP()
其中大部分接口都可以无视,因为我们只需要这个控件在richedit中能够占位(长宽),能够展示(效率关键),至于其他的可编程、在位激活、对象识别都不重要。我观察了QQ的动画控件,呃,比现在网上流行的要改变很多(网上内容没有与时俱进)。现在的`QQ的动画控件很简单(后面会讲述如何找到这个控件),看起来只是作为一个占位工具,如何触发动画则是由host控制。观其接口:
};
interface IRichPicObj : IDispatch {
[id(0x00000001), propput, helpstring("property src")]
HRESULT src([in] BSTR rhs);
[id(0x00000002), propput, helpstring("property static")]
HRESULT na([in] long rhs);
[id(0x00000003), propput, helpstring("property autoHeight")]
HRESULT autoHeight([in] long rhs);
[id(0x00000004), propput, helpstring("property autoWidth")]
HRESULT autoWidth([in] long rhs);
[id(0x00000005), propput, helpstring("property maxAutoWidth")]
HRESULT maxAutoWidth([in] int rhs);
[id(0x00000006), propput, helpstring("property onerror")]
HRESULT onerror([in] BSTR rhs);
[id(0x00000007), propput, helpstring("property objid")]
HRESULT objid([in] BSTR rhs);
};
IrichFrameObj的作用我不是很理解,居然一个接口都没有,而后者模糊的能够理解一些:src大概就是动画图片路径,auto系列是为了动态缩放。现在的QQ只允许一个自定义动画,依据老衲猜测,因为自定义动画往往是截图,比较大,在一行容易引起点击时视图跳跃。再有其他的属性是为了识别所用,无法推测具体行为。
呃,事情看起来没有那么复杂,的确,我只实现了2个接口,而其中大部分也都是返回E_NOTIMPL,因为richedit确实没有那么标准,你实现的再标准也无济于事。当然richeit也在更新,win8的sdk对其改动最大,但win7的sdk也暴露了一些更早的功能,这或许是目前实现的最大亮点(技术含量,高风险高回报,一般人难以置信)。
对于动画控件阐述到此为止,或许很多人会很失望,但也仅仅如此,因为它本身什么都没有,尤其是在你真正明白之后,所以这里或许你会很失望,但是真正的内容就这么多,我也不知怎么添油加醋。
最后一个建议,希望尊重所有玩技术的:
1、中国官本位思想很严重,技术搞得不错(或许是运气)立马转管理
2、文人相轻
3、资本运作,体制运作,技术作用不明显
4、吃饱肚皮,难以维系理想
大道无形,大音希声,牛叉的技术很多都是巧工,而真正的产业才是大无畏的研发,我们只不过在投机取巧。