WINX的消息分派机制(续2)
2008-04-01 17:01
375 查看
我们继续Inside WINX's Message Dispatch。现在开始我们进入了最为关键的部分——WINX是怎么进行消息分派的。
从原理上来讲,WINX的消息分派函数(DispatchMessage)其实与上一篇:《WINX的消息分派机制(续)》中的并无多大的不同,只不过更加智能而已。其中最为关键的是,WINX引入了一种技巧,它可以在编译期判断一个函数是否被重载。简单来说,WINX的消息分派伪代码如下:
template
class WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast(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 WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast(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 WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast(this);
if (message == WM_PAINT)
pThis->OnPaint(hWnd);
else
return FALSE;
return TRUE;
}
};特别地,如果WindowMessage派生类没有响应任何消息,则优化后DispatchMessage为一个空函数,如下:
template
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 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源代码时,你可以发现这个技巧有不少变种(消息分派的实现就与此有细节上的不同),但是其中的道理是完全一致的。
下一篇:WINX的消息分派机制(终结篇)
WINX的消息分派机制源代码
从原理上来讲,WINX的消息分派函数(DispatchMessage)其实与上一篇:《WINX的消息分派机制(续)》中的并无多大的不同,只不过更加智能而已。其中最为关键的是,WINX引入了一种技巧,它可以在编译期判断一个函数是否被重载。简单来说,WINX的消息分派伪代码如下:
template
class WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast
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 WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast
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 WindowMessage
{
...
BOOL DispatchMessage(
HWND hWnd, UINT message,
WPARAM wParam , LPARAM lParam, LRESULT& lResult)
{
T* pThis = static_cast
if (message == WM_PAINT)
pThis->OnPaint(hWnd);
else
return FALSE;
return TRUE;
}
};特别地,如果WindowMessage派生类没有响应任何消息,则优化后DispatchMessage为一个空函数,如下:
template
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 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源代码时,你可以发现这个技巧有不少变种(消息分派的实现就与此有细节上的不同),但是其中的道理是完全一致的。
下一篇:WINX的消息分派机制(终结篇)
补充:
详细代码请参考WINX库。你也可以通过以下链接在线查看:WINX的消息分派机制源代码