使用RAD类型工具的人越来越多了,虽然我对于RAD类的工具向来不多作评议,但我还是常常使用的。所以我深深的知道这类工具虽然给我们带来了便利,使我们能不用将更多的精力放在界面上,但同时也将初学者紧紧的圈在了他所提供的控件和组件中。所以很多人并不能真正的了解windows的消息驱动原理以及windows的运作过程。本文中我们就一起来学习一下windows的运作过程,使我们对delphi这样一个优秀的编程工具有一个新的认识,并对windows下的程序编写有更深刻、透辟的了解和认识。
一、消息的定义
我们先从使用的角度看看windows的运作过程。我们都知道windows是一个多任务的平台,使用这个平台,我们可以一边工作,一边听歌曲,等等。所以对于这个操作平台可以想象到它除了一般操作系统所提供的对文件系统,内存系统等的管理之外,更重要的就是我们所熟知的消息驱动了,也就是说,要通过一定的方法和结构可以给每一个运行中的程序实例以及其中的每一个窗口传递其中所触发的事件。Windows中究竟是怎样做到的呢?让我们打开安装delphi的目录,在其中的source\rtl\Win\Windows.pas文件(或者在一个工程文件,找到uses,在其中找到Windows,然后按下Ctrl键,用鼠标点击单词),在其中的第18919行,我们可以看到这样一个结构的定义:
{ Message structure }
PMsg = ^TMsg ;
tagMSG = packed record
hwnd : HWND ;
message : UINT ;
wParam : WPARAM ;
lParam : LPARAM ;
time : DWORD ;
pt : TPoint ;
end ;
{ $ EXTERNALSYM tagMSG }
TMsg = tagMSG ;
MSG = tagMSG ;
{ $ EXTERNALSYM MSG }
其中hwnd字段表示触发了消息的窗口的ID,由此可以保证消息正确的发送到每一个窗口去。 Message 表示消息的类型,其中更细致的解释要通过wParam和lParam一起来进行,不同的消息,wParam和lParam的值也就不相同。time用来记录消息触发的时间。Pt则表示触发的位置(毕竟window中有了鼠标)。我们也可以用同样的方法打开Messages文件。其中定义了windows中的绝大部分消息和结构。下面是我们截取的其中一部分。
const
{ $ EXTERNALSYM WM_NULL }
WM_NULL
= $0000 ;
{ $ EXTERNALSYM WM_CREATE }
WM_CREATE
= $0001 ;
{ $ EXTERNALSYM WM_DESTROY }
WM_DESTROY
= $0002 ;
{ $ EXTERNALSYM WM_MOVE }
WM_MOVE
= $0003 ;
{ $ EXTERNALSYM WM_SIZE }
WM_SIZE
= $0005 ;
……
……
WM_APP = $8000 ;
{ NOTE : All Message Numbers below 0x0400 are RESERVED }
{ Private Window Messages Start Here }
{ $ EXTERNALSYM WM_USER }
WM_USER
= $0400 ;
……
……
{ Dialog messages }
{ $ EXTERNALSYM DM_GETDEFID }
DM_GETDEFID = ( WM_USER+0 ) ;
{ $ EXTERNALSYM DM_SETDEFID }
DM_SETDEFID = ( WM_USER+1 ) ;
{ $ EXTERNALSYM DM_REPOSITION }
DM_REPOSITION = ( WM_USER+2 ) ;
{ $ EXTERNALSYM PSM_PAGEINFO }
PSM_PAGEINFO = ( WM_USER+100 ) ;
{ $ EXTERNALSYM PSM_SHEETINFO }
PSM_SHEETINFO = ( WM_USER+101 ) ;
{ Button Notification Codes }
……
……
可以看到,windows中每一个消息都对应着一个唯一的数值。当然我们也可以定义自己的消息,它的数值只要定义在WM_USER之后,保证和其中的定义不想重复即可。
二、消息的接收
消息到是有了,但怎样才能让程序以及窗口接收到呢?还是从使用的角度考虑,可以想象到,对于一个程序或窗口可以接收到来自鼠标、键盘等输入设备的消息,也可以接收到来自程序传递的消息,因为有了前面的tagMSG结构,我们就可以被动的在程序中接收消息了。这个功能的实现就是由下面的程序实现的(同样的这段程序来自于delphi的元代码):
function Tapplication . ProcessMessage ( var Msg : TMsg ) : Boolean;
var
Handled : Boolean ;
begin
Result : = False ;
if PeekMessage ( Msg , 0 , 0 , 0 , PM_REMOVE ) then
begin
Result : = True ;
if Msg.Message WM_QUIT then
begin
Handled := False ;
if Assigned ( FonMessage ) then FonMessage ( Msg , Handled ) ;
if not IsHintMsg ( Msg ) and not Handled and not IsMDIMsg ( Msg ) and
not IsKeyMsg ( Msg ) and not IsDlgMsg ( Msg ) then
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
end ;
end ;
else
FTerminate : = True ;
end ;
end ;
其中主要的是这几句:
if PeekMessage ( Msg , 0 , 0 , 0 , PM_REMOVE ) then
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
end ;
但更常见的是:
while GetMessage ( Msg , 0 , 0 , 0 ) do
begin
TranslateMessage ( Msg ) ;
DispatchMessage ( Msg ) ;
End ;
PeekMessage和GetMessage都是从消息队列中得到发给程序的消息,只要有消息,就通过TranslateMessage ( Msg )和DispatchMessage ( Msg )两句将消息翻译为可处理的格式并分派给应用程序所注册的回调函数进行处理。