March 09
Windows的消息机制:
1)系统中发生某种事件
2)Windows把该事件解释成消息,再把消息放到应用程序消息队列中。
3)客户应用程序从消息队列中得到消息,并把消息填入到TMsg记录中。
4)客户应用程序把消息传递到该程序中合适的窗口过程中。
5)窗口过程执行相关的动作以相应此消息。
Windows为每个应用程序维护一个消息队列。
VCL的消息系统。
1)从消息循环中取出消息。
2)VCL调用Application.ProcessMessage().
3)调用Application.OnMessage(). 由于直接发送的消息不是放到消息队列中,在直接发送消息时,OnMessage方法不会被调用到。对于放入消息对列的消息,Delphi在内部调用DisPatchMessage() API 来把这条消息分发给StdWndProc() , 对于直接发送的消息,Win32会直接调用StdWndProc() 函数,StnWndProc()是一个汇编函数,他接受Windows发送的消息并把消息转发给消息目的的对象。接受消息的对象方法较MainWndProc(),从MainWndProc开始我们可以执行程序需要的特殊消息的处理。
4)执行完毕mainWndProc()方法后,消息被转发到对象WndProc()方法中,然后进入分发系统。分发系统存在于各个系统的Dispatch()方法中。
5)消息到达我们自定义的消息处理过程中。如果我们用了inherited,还需要执行继承的消息处理过程。
5)DefaultHandler()方法,执行最终的消息处理,然后把消息传递到Windows的DefWindowProc()函数和其他的缺省的窗口过程(例如:DefMDIProc),从而执行一些Windows缺省处理。
消息--------> SomeClass WndProc
|
|
|
SomeClass 分发
/ | \
/ | \
/ | \
SomeClass消息处理程序 祖先消息 Ancestor N 消息处理程序
\ 处理程序 /
\ | /
\ | /
\ | /
SomeClass默认处理程序
说明:除非我们确实需要阻止正常的消息处理,一般情况下都需要调用inherited方法。
所有未处理的消息都会送入到DefaultHandler()中,因此 DefaultHandle()是处理应用
程序之间消息传递的最佳场所,这里处理的消息值是通过Register Window Message()过程
来获得的。
VCL的perform()方法适用于Tcontrol类的所有派生对象,该方法可以向应用程序中的任何
一个窗口或控件发送消息,前提是要知道窗口或控件的实例名。调用perform()后,
它要等到消息得到处理后才返回。
sendmessage():该函数把消息直接发送到指定窗口的窗口过程,等消息处理后才返回。
速度快,但由于它直接发送到窗口过程,没有经过先进先出的消息队列,所以可能引起线程的阻塞。
function sendmessage(hwnd:HWND;msg:UNIT;wparam:WPARAM;lparam:LPARAM);lresult;stdcall;
其中参数hwnd是接收该消息的窗口的句柄。
postmessage():该函数把消息发送到指定窗口所在的线程的消息队列中,然后立即返回。
function postmessage(hwnd:HWND;msg:UNIT;wparam:WPARAM;lparam:LPARAM);boolean;stdcall;
sendmessage()返回该消息被处理的结果值。
postmessage()返回一个布尔值,以表明该消息是否已放到消息队列中。
SendMessage,PostMessage
这两个函数基本上相同的,只有一点区别:SendMessage()和Perform()类似,都是同步调用,
消息直接发往目的的窗口,该消息处理完毕后才返回;PostMessage()是异步调用的,发消息
给Windows消息队列,然后立即返回。
SendMessage()返回被处理消息的结果值,PostMessage()返回的只是一个Boolean,表示
是否该消息被放到目标窗口队列中。
消息的处理过程:
SendMessage:WndProc(),Message Procdure(),Default Handler();
PostMessage:Application.onMessage(),WndProc(),Message Procdure(),Default Handler();
注意:
1、在消息的处理过程中,我们可以只用inherited关键词来把消息送到继承的消息处理过程中,
但是在使用WndProc() 或 DefaultHandler()时却不能这样做,使用这两个过程时,我们必须在
Inherited后面加上过程或函数名,如:inherited WndProc(Msg);
2、DefaultHandler()过程有点特殊,它只接受一个untyped var 参数,
DefaultHandler()假设参数中的第一个Word是消息ID,不关心参数剩下的部分。
所以我们用TMessage强制转换访问消息参数。
一个实例:
private
procedure WMLBUTTONDOWN(var Msg : TWMLBUTTONDOWN); message WM_LBUTTONDOWN;
procedure WMRBUTTONDOWN(var Msg : TWMRBUTTONDOWN); message WM_RBUTTONDOWN;
var
implementation
procedure TFormGetMess.WMLBUTTONDOWN(var Msg: TWMLBUTTONDOWN);
begin
{*****************************************************************************
问题:1、不用inherited,还有什么善后的操作?
2、用了inherited,点击窗体的关闭按钮,仍然执行消息的事件。
*****************************************************************************}
Showmessage('Hello');
inherited;
//这一句不能少,它的意思是对该消息做完自己的处理后,
//还要把该消息传递给tform1的父类,让它的父类再做一些善后的操作。
//MessageBeep(0);
//inherited;
//inherited 的用法,当需要把消息传递到祖先类的对象处理过程中时,我们调用
//inherited,这里通过调用inherited的方法,我们把消息传递到TForm的WM_PAINT处理
//过程中。
end;
procedure TFormGetMess.WMRBUTTONDOWN(var Msg: TWMRBUTTONDOWN);
begin
//RightMouse Message.
//当右击鼠标时,利用这个消息处理函数转换为点击左键。
//以下是三种消息发送给自己的方法。
//perform(WM_LBUTTONDOWN,0,0);
//SendMessage(FormGetMess.Handle,WM_LBUTTONDOWN,0,0);
PostMessage(FormGetMess.Handle,WM_LBUTTONDOWN,0,0);
inherited;
//VCL的perform()方法适用于Tcontrol类的所有派生对象,该方法可以向应用程序中的任何
//一个窗口或控件发送消息,前提是要知道窗口或控件的实例名。调用perform()后,
//它要等到消息得到处理后才返回。
//sendmessage():该函数把消息直接发送到指定窗口的窗口过程,等消息处理后才返回。
//速度快,但由于它直接发送到窗口过程,没有经过先进先出的消息队列,所以可能引起线程的阻塞。
//function sendmessage(hwnd:HWND;msg:UNIT;wparam:WPARAM;lparam:LPARAM);lresult;stdcall;
//其中参数hwnd是接收该消息的窗口的句柄。
//postmessage():该函数把消息发送到指定窗口所在的线程的消息队列中,然后立即返回。
//function postmessage(hwnd:HWND;msg:UNIT;wparam:WPARAM;lparam:LPARAM);boolean;stdcall;
//sendmessage()返回该消息被处理的结果值。
//postmessage()返回一个布尔值,以表明该消息是否已放到消息队列中。
end;
消息的一些基本概念
消息是Windows对应用程序发送的有关“发生了某种事件”的通知。
Windows以Record的形式发送消息给应用程序。记录包含消息的类型以及一些附加信息。
由Windows发送给应用程序的消息类型是TMsg(Hwnd,Message,WParam,LParam)。
Delphi把Windows的TMsg记录中的信息映射到TMessage(程序员需要处理的信息,Result)中。
一些特定的消息记录:鼠标消息记录(TWMMouse)等。
消息的处理
某个过程要成为消息处理过程要满足3个条件:
1、该过程必须是某个对象的方法;
2、该过程必须有一个引用类型(var)的TMessage或其他特定消息记录类型参数;
3、该过程必须使用message指示符,指示符后面是希望处理的消息常量。
例:
procedure WMPaint(var Msg : TWMPaint);message WM_PAINT;
说明:当命名消息处理过程时,一般把它们取成和消息同名,使用大小写规则,并不要下划线。
该过程有message指示符,后面是处理的消息常量 WM_PAINT; 满足条件3
procedure TForm1.WMPaint(var Msg : TWMPaint);
begin
beep;
inherited;
end;
该过程是TForm1对象的一个方法,在implementation部分不要使用message指示符。满足条件1
有var Msg : TWMPaint参数,该参数为特定消息类型TWMPaint。满足条件2
当需要把消息传递给祖先类的对象处理过程中时,我们调用Inherited,这里调用Inherited的
方法,我们把消息传递给TForm的WM_PAINT处理过程中。
注:理解部分
我们写消息的过程是让应用程序完成我们期望的功能,而调用Inherited是让应用程序完成Windows期望的附加功能。
和其他继承方法不同,这里不必给出继承方法的名字,因为分发时方法的名字并不重要,通过判断类定义中的message指示符后面的消息值,Delphi知道调用哪个方法。
TApplication类型的OnMessage事件
我们可以指派一个过程给OnMessage,当应用程序从消息队列中取出一条消息,并准备处理该消息时,就调用这个过程。
如:Application.OnMessage := OnAppMessage;
OnAppMessage 为自己定义的过程。
Application.OnMessage 事件处理过程是TMessageEvent类型,定义形式:
Procedure SomeObject.AppMessageHandler(var Msg : TMsg;var Handled : Boolean);
注意参数是TMsg类型,Handled是boolean类型,说明我们是否处理了这个消息。
OnAppMessage的定义:
Procedure OnAppMessage(var Msg : TMsg;var Handled : Boolean);
OnMessage 唯一的限制就是他能处理的消息只能是从消息队列中取出的消息,而不能处理应用程序直接发送个Windows窗体过程的消息。
OnMessage过程中会接受到应用程序中的所有的Windows发送的消息,(一秒有上千条消息)
因此不要在OnMessage过程中做耗时的工作,否则应用程序会很慢。
用户定义的消息
WM_USER + 100 到 $7FFF范围的消息值
Const
SX_MYMESSAGE = WM_USER + 100;
注意:不要把从WM_USER 到$7FFF的消息值传递给其他的程序。除非程序员已经知道接受消息的应用程序已经定义了对这些消息的相应处理,因此,在不同的程序中传递这些消息可能会发生潜在的错误。
{
如何理解???实现??
}
应用程序间传递消息:
RegisterWindowMessage() API函数。
广播消息:TWinControl的派生类可以广播一条消息记录给它所拥有的控件。
如:
var
M : TMessage
begin
With M do
begin
Message := xiaoxi;
WParam := 0;
LParam := 0 ;
Result := 0;
end;
Panel1.Broadcast(M);
end;