每一个有句柄的窗口控件(继承自TwinControl),在它被创建时,都要在Windwos中注册一个窗口过程(MainWndProc)。这个窗口过程并不是由应用程序自己调用,而是供windows调用的!!! 所以我们在这个过程中写的消息处理过程代码才能被自动调用、运行。
典型的窗口过程是会包含一个大的case分支,以处理不同的消息!比如像这样:
Case Message.Msg Of
WM_LBUTTONDOWN :
Dosomething1...
WM_CLOSE:
Dosomething2...
WM_PAINT:
Dosomething3...
WM_MOUSEWHEEL :
Dosomething4...
WM_SIZE:
DosomethingN...
end;
这样,当我们在相应的消息中写入代码后,当在这个控件上发生该消息时,就会执行相应的代码。(比如在上面的WM_LBUTTONDOWN消息中写入代码,那么当在这个控件上点击鼠标左键时,就会执行你在WM_LBUTTONDOWN中写的代码!)
======================================================================================================
上面讲的是最简单的说法。其实当你在一个控件上单击鼠标左键时,详细的过程是:
在一个控件上单击鼠标左键,Windows产生一个TMsg记录类型的WM_WM_LBUTTONDOWN消息,在TMsg记录中包含了消息接收者的句柄。然后Windows根据TMsg记录中的句柄,将消息发送到相应的应用程序。如果这个应用程序是由Delphi语言做的,VCL会将消息发送到TApplication.Run建立的消息循环队列中,然后这里调用Application.OnMessage事件,如果OnMessage事件中没有相应的消息处理方法,就调用TApplication.DispatchMessage根据TMsg记录中的消息接收者的句柄将消息发送到该句柄在Windows系统中注册的窗口过程(VCL中是MainWndProc)。到此为止这是消息在【Windows系统中的派遣】。
【Windows系统中的派遣】可以用如下这个流程图表示:
Windows产生相应消息(如单击鼠标等)→→TApplication.Run→→Application.HandleMessage→→调用 TApplication.ProcessMessages→→调用Application.OnMessage事件(如果有)→→TApplication.DispatchMessage→→调用TWinControl.MainWndProc进入VCL内部消息派遣!
下面是消息在【VCL中的消息派遣过程】:
TWinControl.MainWndProc对消息没做任何处理直接将其发送到Tcontrol.WindowProc属性,WindowProc属性是一个方法类型指针,所以它调用了TControl.WndProc虚方法。在WndProc虚方法中可处理以下几类消息:WM_KEYFIRST,WM_MOUSEFIRST,WM_LBUTTONDBLCLK,WM_RBUTTONDBLCLK,WM_MBUTTONDBLCLK,WM_MOUSEMOVE,WM_LBUTTONDOWN, WM_LBUTTONDBLCLK,WM_LBUTTONUP,CM_VISIBLECHANGED。这些消息,由TControl.WndProc处理,不会再往下传递了。由于WndProc是虚方法,在TControl类以后的子类中被多次覆盖(比如用户可以重新覆盖这个虚方法),这样做以后,大部分组件都通过继承获得了大多数的消息处理方法。其它的消息再向下传入Tobject.Dispatch中,Dispatch方法首先在本类中查找相应的消息处理方法,如果找到执行消息处理方法。否则,逐级向上父类,祖先类,直至找到对应的消息方法。如果找到Tobject这一层,仍然没找到相对应的消息处理方法,就调用Tobject.DefaultHandler方法。基本上消息到了这里,大部分没用的消息都将被抛弃。到此为止,消息处理过程全部结束。
【VCL中的消息派遣过程】可以用如下这个流程图表示:
TWinControl.MainWndProc→→Tcontrol.WindowProc属性(一个方法类型指针)→→TControl.WndProc(虚方法,可由用户override)→→Tobject.Dispatch(由子类开始向父类搜索相应的消息方法)