上面是一张MFC的类图局部。
MFC将消息分为三大类:
① 命令消息WM_COMMAND:凡是由CCmdTarget派生的类,均可接收该消息。
② 标准消息WM_xxx:凡是由CWnd派生的类,均可接收该消息。
③ 控件通知消息WM_NOTIFY/WM_COMMAND:由控件产生,为的是向其父窗口发送通知。Windows9x及以上控件传送的是WM_NOTIFY,而老版本控件为了兼容,依然传送WM_COMMAND。
注意:WM_NOTIFY其实就是WM_COMMAND。WM_NOTIFY是为了将控件发送给父窗口的WM_COMMAND与其他WM_COMMAND区分,才使用了这样一个写法。但在实际中依然是使用WM_COMMAND来传送WM_NOTIFY。
由以上可知:
① CCmdTarget派生的类一定可以接收WM_COMMAND。
② CWnd派生的类一定可以接收WM_COMMAND与WM_xxx,当然也包括WM_NOTIFY。故而CWnd派生的类一定可以接收任何Windows消息。
③ 由CCmdTarget派生但不由CWnd派生的类(如CWinApp、CDoucument等),均不可以接收WM_xxx,只可以接收WM_COMMAND(以及WM_NOTIFY)。
会产生WM_COMMAND的,一定就是UI对象,如菜单和按钮等。对每一个WM_COMMAND,必须有一个对应的处理函数,将消息与处理函数绑定在一起,称之为Command Binding。
对于所对应的处理函数,若是标准的Windows消息,则不需要我们指定处理函数的名称,每个标准Windows消息都有自己的默认处理函数名称。如WM_LBUTTONDOWN所对应的处理函数名称为OnLButtonDown。
理论上来说,所有派生自的CCmdTarget类都应该具有DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP宏,这样才能使得该派生类具备接收消息并上溯的能力。但是,实际上,并非所有派生类都有这三个宏。CWinThread就是没有这三个宏的。
CWinThread没有这三个宏,就导致它无法接收消息,也无法上溯。那它的派生类该怎么办?岂不是无法上溯?实际上,这里使用了一个技巧。CCmdTarget->CWinThread->CWinApp。对于CWinApp,虽然它的父类CWinThread没有这三个宏,但这不妨碍CWinApp带有这三个宏。CWinApp带有这三个宏,那它就具备接收消息并上溯的能力。可是CWinApp是无法上溯到父类CWinThread,因为父类没有这三个宏。于是,在CWinApp的BEGIN_MESSAGE_MAP宏的参数里,是这样写的:
BEGIN_MESSAGE_MAP(CWinApp,CCmdTarget)
CWinApp的消息基类没有写成是父类CWinThread,而是写成了祖父类CCmdTarget。当有消息从CWinApp上溯时,会直接根据这里的参数直达祖父类CCmdTarget。
消息传递时,产生了跳跃。