| shiwei's profileI have a dream...BlogLists | Help |
|
I have a dream...不要把要做的事情留到最后期限 November 12 My blog was migrated...Because of blockage, the readers from china were unable to read these posts. So I decide to migrate my blog, which is written in chinse, to CSDN.net. Here is the homepage of my new blog:
http://blog.csdn.net/xushiweizh. And late I will write something in english to http://codeproject.com
. 转移战场...由于blogspot被盾,不得不考虑转移战场了... 原先winxcn.blogspot.com主要访问来源有二:搜索引擎和一些一直关注WINX的朋友。目前只剩下几个骨灰级的朋友,和从sourceforge的链接(http://winxcn.com)无意中过来一些老外。 决定中文blog主要在CSDN上维护,网址: http://blog.csdn.net/xushiweizh。 下阶段可能会增加英文文档,目前考虑转战CodeProject。 November 07 WINX的消息分派机制(终结篇)你已经了解了WINX的消息分派,这里我们总结一下,并交代一些前文为了思路紧凑而略过的一些细节,内容包括:
开发WINX的时候,尽管我决定尽量重用WTL,以便这个界面库不至于和Sourceforge上其他众多的界面库一样,最后只是一个实验品(它们无法流行的原因多数在于体系封闭而个人精力有限而无法提供与当前流行的界面库同等的功能),只是拥有少量的忠实拥护者,但是最终无法成为工业级的产品(在我看来,C++界面库比较成功的有MFC、QT、wxWidgets,而WTL只能算半个)。但是,消息分派机制上我决定不沿袭WTL,而是自己提供全新的实现。 WTL的消息机制是极其灵活的,通过它你很容易将功能划分为一个个独立而且含义完整的功能切片。但是问题在于,这种灵活给用户带来了困惑:深奥的模板技术、复杂的消息机制、晦涩而丑陋的代码,用户望而却步了。 WINX的消息机制给用户最简洁的界面,并尽量与MFC的消息兼容。另一方面,WINX的消息分派的智能带来了另一个好处:WINX不断可以增加新的消息,只要派生类没用响应它,就没有任何额外开销。进而,我们可以为WINX加入任何新特性,只要用户没用使用该特性,那么就没用额外代价。——这很有趣。你马上可以想到,WTL的一个个功能切片,WINX也可以提供,并且可以以更为简洁的方式提供。 WINX的消息分派是高效的。一部分原因你已经了解到了:WINX它可以智能的了解派生类并做出优化。还有一个细节,同样与性能有关:WINX的消息响应次序在DispatchMessage中已经精心安排好了。你可以想象,把WM_PAINT消息放到switch..case的最后,还是放在最前,这对消息函数的执行效率产生了怎样的影响。而WTL中是由你自己去安排消息响应的次序,这对使用者是一个额外的负担。——这不只是因为性能的因素,WTL中某些功能切片的安放是有次序要求的,交换次序后会有细节上的行为差异。 WINX的消息机制最大的问题在于,由于我们随时可能会为了实现一个作用于所有窗口的新特性而添加新消息,故此,为了方便你未来升级WINX到更高的版本,你派生的窗口类需要小心定义其成员函数名。我的一个建议是,请尽量不要自定义一些以On开头的函数名。如果确实需要,建议引入类似命名空间的法则:例如,所有响应命令的函数统一以OnCmdXXX命名之。 MFC在你响应消息中希望执行默认处理时,除了调用基类的同名方法外,更为常见的方法是调用Default()函数。同样,WINX也提供了该函数,并且实现方式类似,大体代码如下: __declspec(thread) PackedMessage _g_currMsg; 应用程序框架设计(1):SW系统简介《应用程序框架设计》是我大学毕业时(2000年)写的毕业论文。在我给公司内部作"应用程序架构"方面的讲座时,曾经作为入门级的参考资料附上。后来不知如何就流传到Internet上,不过是不完整的版本(可尝试
在Google中搜索"应用程序框架设计:SW系统")。回头看这篇文字,最大的感受觉得自己的文字功底是越来越退步了:-) 由于与界面库有关,大家不妨看看。
二、应用程序框架设计的基本内容
November 06 WINX的消息分派机制(续2)我们继续Inside WINX's Message Dispatch。现在开始我们进入了最为关键的部分——WINX是怎么进行消息分派的。 从原理上来讲,WINX的消息分派函数(DispatchMessage)其实与上一篇:《WINX的消息分派机制(续) 》中的并无多大的不同,只不过更加智能而已。其中最为关键的是,WINX引入了一种技巧,它可以在编译期判断一个函数是否被重载。简单来说,WINX的消息分派伪代码如下: template <class T> class WindowMessage { ... BOOL DispatchMessage( HWND hWnd, UINT message, WPARAM wParam , LPARAM lParam, LRESULT& lResult) { T* pThis = static_cast<T*>(this); if (派生类重载了OnPaint && message == WM_PAINT) pThis->OnPaint(hWnd); else if (派生类重载了OnKeyDown && message == WM_KEYDOWN) pThis->OnKeyDown(hWnd, wParam, lParam); else if (...) ... else return FALSE; return TRUE; } }; 简单看一个实际的例子,这样做的好处就很明了了。设想WindowMessage的派生类只重载了OnPaint,那么WindowMessage类看起来是这样的: template <class T> class WindowMessage { ... BOOL DispatchMessage( HWND hWnd, UINT message, WPARAM wParam , LPARAM lParam, LRESULT& lResult) { T* pThis = static_cast<T*>(this); if (true && message == WM_PAINT) pThis->OnPaint(hWnd); else if (false && message == WM_KEYDOWN) pThis->OnKeyDown(hWnd, wParam, lParam); else if (...) ... else return FALSE; return TRUE; } }; 并最终被编译器优化为: template <class T> class WindowMessage { ... BOOL DispatchMessage( HWND hWnd, UINT message, WPARAM wParam , LPARAM lParam, LRESULT& lResult) { T* pThis = static_cast<T*>(this); if (message == WM_PAINT) pThis->OnPaint(hWnd); else return FALSE; return TRUE; } }; 特别地,如果WindowMessage派生类没有响应任何消息,则优化后DispatchMessage为一个空函数,如下: template <class T> class WindowMessage { ... BOOL DispatchMessage( HWND hWnd, UINT message, WPARAM wParam , LPARAM lParam, LRESULT& lResult) { return FALSE; } }; 这就是WINX的消息分派机制为何比MFC、WTL以及其他任何界面库高效(无论是编译后的代码尺寸上,还是执行效率上)的原因。 好了,现在该是解释WINX如何做到这一点——检测派生类是否重载某个函数的时候了。我们假设,基类(名为Base)中有一个成员函数Func(假设有两个参数),现在有另一个成员函数Caller希望根据派生类是否重载Func来做事情。如下: template <class T> class Base { RetType Func(ArgType1 arg1, ArgType2 arg2) { ... } void Caller() { if (派生类重载了Func) { ... } else { ... } } }; 一个办法是,略微修改一下基类中的Func原型,加上一个无用参数int unused: RetType Func(ArgType1 arg1, ArgType2 arg2, int unused = 0); 或者直接改为可变参数: RetType Func(ArgType1 arg1, ArgType2 arg2, ...); 当然,派生类重载Func原型还是需要按我们预期的: RetType Func(ArgType1 arg1, ArgType2 arg2); 如此,判断"派生类是否重载了Func"就变成了判断函数原型是否为 RetType Func(ArgType1 arg1, ArgType2 arg2); 而这正是编译器的拿手好戏。 最后提醒一下,阅读WINX源代码时,你可以发现这个技巧有不少变种(消息分派的实现就与此有细节上的不同),但是其中的道理是完全一致的。 November 03 细节修改由于msn space的bug,我无法编辑发送的帖子。故此只能发新的post说明了。
在很多时候,模板(template)与虚拟(virtual)是相同的。
请把它理解为:
在很多时候,模板(template)与虚拟(virtual)是相通的。
WINX的消息分派机制(续)和MFC、WTL等界面库不太一样的是,WINX认为消息分派是一个可独立于窗口存在的基础服务。所以WINX中负责消息分派的不是winx::Window<T>类,而是
winx::WindowMessage<T>类。winx::Window<T>只是从
winx::WindowMessage<T>继承。
上一篇我故意买了个关子。如果有读者在看了《WINX的消息分派机制》一文后去亲自看winx的头文件了解实地了解一下的话,我将觉得很安慰。这一篇我们继续这个话题。
WindowMessage<T>的基本规格是这样的:
template <class T>
class WindowMessage
{
void OnDestroy(HWND hWnd);
void OnPaint(HWND hWnd);
void OnKeyDown(HWND hWnd, UINT uVKChar, UINT uKeyData);
...
LRESULT InternalDefault(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT ProcessMessage(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};
按WindowMessage<T>的契约,其客户必须将发送给窗口的所有消息全部转发给ProcessMessage函数进行处理。涉及的几个关键函数功能如下:
显然,这里面最为关键的是
DispatchMessage。如果不考虑优化,它看起来并不复杂。我们这里实作一个基于虚函数(virtual)机制的版本:
class WindowMessage
{
virtual void OnPaint(HWND hWnd) { Default(); }
virtual void OnKeyDown(HWND hWnd, UINT uVKChar, UINT uKeyData) { Default(); }
...
LRESULT Default();
{
switch (message)
{
case WM_PAINT: OnPaint(hWnd); break;
case WM_KEYDOWN: OnKeyDown(hWnd, wParam, lParam); break;
...
default: return FALSE;
}
return TRUE;
} };
你一定对此不屑一顾:除了Default函数看起来有点意思(容后介绍它的实现)外,用一个超庞大switch..case来实现DispatchMessage,有"创意",但实在是有些乏味。
有人马上提建议说,改用模板(template)吧——性能高些。于是,就有了基于template的版本:
template <class T>
class WindowMessage
{
void OnPaint(HWND hWnd) { Default(); }
void OnKeyDown(HWND hWnd, UINT uVKChar, UINT uKeyData) { Default(); }
...
LRESULT Default();
{
T* pThis = static_cast<T*>(this);
switch (message)
{
case WM_PAINT: pThis->OnPaint(hWnd); break;
case WM_KEYDOWN: pThis->OnKeyDown(hWnd, wParam, lParam); break;
...
default: return FALSE;
}
return TRUE;
} };
在很多时候,模板(template)与虚拟(virtual)是相同的。这两个版本的WindowMessage并无本质的不同,事实上只是作了机械的代码变换而已。了解这个变换的等价性是很有必要的。不可否认,经此一变,性能提高了不少。前文《ATL界面类——兼谈多态与泛型》我们对此作了细述。
这个基于模板的WindowMessage类同样乏味。尽管没有了虚函数调用的开销,但这个DispatchMessage函数无疑仍然是个庞然大物,与WTL的精巧相去甚远。
那么,我们还有甚么办法可想吗?
to be continued...
WINX的消息分派机制WINX的消息分派是卓越的。我们先简单回顾一下WINX的SDI风格的Hello程序与MFC/WTL/SDK的对比 (我们关注的是窗口类中的消息处理相关):
MFC和WTL有着类似MessageMap(尽管内部机制大不一样),是通过宏实现消息分派的。也许你已经习惯了响应消息时提供MessageMap,但在WINX中这不需要响应任何消息你均只需要直接覆盖消息处理函数即可。示意如下: class MyWindow : public winx::Window<MyWindow> 你可能担忧WINX的消息分派的便利,是牺牲性能为代价的。——可是我郑重告诉你,这种担忧是多余的。随着本文对winx消息机制的一步步剖解,你将发现,事实恰恰相反,WINX在消息分派的性能上考虑甚多,其消息分派的代码的无论是编译后的执行代码尺寸,还是效率,均优于MFC、WTL。 to be continued ... November 02 终于来了——Gtalk已支持发送离线信息!![]() Surprise?!对,Gtalk终于正式支持了发送离线信息!当然,与QQ相比,这的确是迟来的功能。废话不多说,下面是实现发送离线功能的条件: 1.对方不在线( 废话) 2.对方必须是使用Gmail帐号登录Gtalk的 3.对方开启了保存聊天记录 这样一来,你就可以给他/她/它发送离线信息了: ![]() 使用之前的 Gtalk 1.0.0.100即可实现这个功能。如果你还没安装,请点击这里下载。 |
|||
|
|