谁来给俺讲讲 消息机制! (100分)

  • 主题发起人 雪中漫步
  • 开始时间

雪中漫步

Unregistered / Unconfirmed
GUEST, unregistred user!
讲讲delphi的消息处理机制!
还有 PostMessage,sendMessage 等和消息有关的用法,举例说明,用在什么场合!谢谢
补充一下,还有perform()的 用法!!
 
俺也想学
 
来自delphi编程资料,原作者不知道是谁。


DELPHI中的消息处理机制

Delphi是Borland公司提供的一种全新的WINDOWS编程开发工具.由于它采用了具有弹性的和可重用的面向对象Pascal(object-oriented pascal)语言,并有强大的数据库引擎(BDE),快速的代码编译器,同时又提供了众多出色的构件.受到广大编程人员的青睐.在众多的编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖而出.
其中一个DELPHI强于其他编程语言(如VB4.0)的地方就是在DELPHI中可自定义消息,并可直接处理消息.这对于那些希望编写自己的构件(Component),或者希望截获.过滤消息的用户来说是必不可少的.因为编写构件一般要对相应的消息进行处理.下面就对Delphi中消息处理机制进行一下介绍。

一.DELPHIVCL中消息的传递
Delphi中每一个VCL(Visual Component Library)构件(如Tbutton,Tedit等)都有一内在的消息处理机制,其基本点就是构件类接收到某些消息并把它们发送给适当的处理方法,如果没有特定的处理方法,则调用缺省的消息处理句柄。

其中mainwndproc是定义在Twincontrol类中的一个静态方法,不能被重载(Override)。它不直接处理消息,而是交由wndproc方法处理,并为wndproc方法提供一个异常处理模块。Mainwndproc方法声明如下:
procedure MainWndProc(var Message: TMessage);

Wndproc是在Tcontrol类中定义的一个虚拟方法,由它调用dispatch方法来进行消息的分配,wndproc方法声明如下:

procedure WndProc(var Message: TMessage); virtual;

dispatch方法是在Tobject根类中定义的,其声明如下:

procedure Tobject.dispatch(var Message);传递给dispatch的消息参数必须是一个记录类型,且这个记录中第一个入点必须是一个cardinal类型的域(field),它包含了要分配的消息的消息号码.例如:

type
Tmessage=record
Msg:cardinal;
wparam:word;
lparam:longint; .
result:longint;
end;
而Dispatch方法会根据消息号码调用构件的最后代类中处理此消息的句柄方法.如果此构件和它的祖先类中都没有对应此消息的处理句柄,Dispatch方法便会调用Defaulthandler方法.Defaulthandler方法是定义于Tobject中的虚拟方法,其声明如下:
procedure Defaulthandler(var Message);virtual;

Tobject类中的Defaulthandler方法只是实现简单的返回而不对消息进行任何的处理.我们可以通过对此虚拟方法的重载,在子类中实现对消息的缺省处理.对于VCL中的构件而言,其Defaulthandler方法会启动windowsAPI函数Defwindowproc对消息进行处理.

二.DELPHI中的消息处理句柄
在DELPHI中用户可以自定义消息及消息处理句柄.消息处理句柄的定义有如下几个原则:
消息处理句柄方法必须是一个过程,且只能传递一个Tmessage型变量参数.
方法声明后要有一个message命令,后接一个在0到32767之间的消息标号(整型常数).
消息处理句柄方法不需要用override命令来显式指明重载祖先的一个消息处理句柄,另外它一般声明在构件的protected或private区.
在消息处理句柄中一般先是用户自己对消息的处理,最后用inherited命令调用祖先类中对应此消息的处理句柄(有些情况下可能正相反).由于可能对祖先类中对此消息的处理句柄的名字和参数类型不清楚,而调用命令inherited可以避免此麻烦,同样如果祖先类中没有对应此消息的处理句柄,inherited就会自动调用Defaulthandler方法.(当然如果要屏蔽掉此消息,就不用inherited命令了)。

消息处理句柄方法声明为:
procedure Mymsgmethod(var message:Tmessage); message Msgtype;

同样用户也可以定义自己的消息,用户自定义消息应从WM_USER开始.

自定义消息及消息处理句柄举例如下:

const my_paint=Wm_user+1;

type
Tmypaint=record
msgid:cardinal;
msize:word;
mcolor:longint;
msgresult:longint;
end;
type
Tmycontrol=class(TCustomControl)
protected
procedure change(var message:Tmypaint); message my_paint;
.....
end;
......
procedure Tmycontrol.change(var message:Tmypaint);
begin
size:=message.msize;{设置Tmybutton尺寸属性}
color:=message.mcolor;{设置Tmybutton颜色属性}
{do something else}
inherited; { 交 由Tcustomcontrol 处 理}
end;


三.过滤消息

过滤消息又称消息陷阱。在一定情况下,用户可能需要屏蔽某些消息.或者截获某些消息进行处理。由以上介绍可以看出过滤消息一般有三种途径:(1).重载构件继承的虚拟方法wndproc.(2).针对某
消息编写消息处理句柄.(3).重载构件继承的虚拟方法Defhandler,在其中对消息进行处理。其中常用的方法是方法(2),在上节中已介绍过了,方法(1)与方法(3)相似,这里只简单介绍一下方法(1)。

重 载 虚 拟 方 法wndproc 的 一 般 过 程 如 下:

procedure Tmyobject.wndproc(var message:Tmessage);
begin
{...判断此消息是否该处理..}
inheritedwndproc(message);
{未处理的消息交由父辈wndproc方法处理}
end;

由此可以看出在wndproc方法中处理消息的优势是可以过滤整个范围内的消息,而不必为每个消息指定一个处理句柄,事实上Tcontrol构件中就是利用它来过滤并处理所有的鼠标消息的(从WM_mousefirst到WM_mouselast,如下代码示).同样利用它也可以阻止某些消息被发送给处理句柄。

procedure TControl.WndProc(var Message: TMessage);
begin
if (Message.Msg>=WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then
if Dragging then{处理拖曳事件}
DragMouseMsg(TWMMouse(Message))
else
...{处理其他鼠标消息}
end;
Dispatch(Message);
{否则正常发送消息}
end;

下例为一简单的自定义构件例子:

Tmyedit类是从Tedit类派生出的一个新类,它的特点是在运行中不能获得焦点,不能由键盘输入(有点类似Tlabel构件).我们可在其wndproc方法中过滤出WM_setfocus,WM_mousemove消息并进行处理来达到上述
要求,源程序如下:

unit myedit;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics,Controls, Forms, Dialogs,

StdCtrls;

type

Tmyedit = class(TEdit)
private
{ Private declarations }
protected
{ Protected declarations }
{ other fields and methods}
procedure wndproc(var message:Tmessage);override;
public
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Samples', [Tmyedit]);
end;
procedure Tmyedit.wndproc(var message:tmessage);
begin
if message.msg=wm_mousemove then
begin
cursor:=crarrow;
{设置光标为crarrow,而不是缺省的crBeam光标}
exit;
end;
if message.msg=wm_SetFocus then
exit;
{屏蔽掉WM_setfocus消息,不让Tmyedit控件获得输入焦点}
inherited wndproc(message);
{其他消息交父辈wndproc处理}
end;
end.

您可以将Tmyedit加到Component Palette中检验其性能。
由以上介绍可以看出,只有清楚了Delphi VCL中的消息处理机制,掌握好处理各种消息的方法和时机(必要时要借助各种工具,如winsight32,spy等),并结合OOP语言的特点,我们才可能编出高质量的构件。这当然要靠读者在实践中不断摸索,积累经验。


PostMessage只负责将消息放到消息队列中,不确定何时及是否处理
SendMessage要等到受到消息处理的返回码(DWord类型)后才继续
PostMessage执行后马上返回
SendMessage必须等到消息被处理后才会返回。
 
不是来讲课的,这个东西也没有什么可说的亚。
比如对某一个窗口发送鼠标消息
SendMessage(Handle,WM_LBUTTONDBLCLK,0,0);
对系统发消息关闭程序
SendMessage(Handle, WM_CLOSE, 0, 0);
启动开始菜单
Sendmessage(Application.Handle,WM_SYSCOMMAND,SC_TASKLIST,0);
哈哈哈,慢慢来吧。
 
消息,就是指Wi n d o w s发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变
窗口尺寸、按下键盘上的一个键都会使Wi n d o w s发送一个消息给应用程序。
消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,
对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。
消息中有什么?
是否觉得一个消息记录中的信息像希腊语一样?如果是这样,那么看一看下面的解释:
hwnd 32位的窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可
视对象的句柄(窗口、对话框、按钮、编辑框等)。
message 用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也
可以是自定义的常量。
wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
lParam 通常是一个指向内存中数据的指针。由于W P a r a m、l P a r a m和P o i n t e r都是3 2位的,
因此,它们之间可以相互转换。
Wi n d o w s的消息系统是由3个部分组成的:
• 消息队列。Wi n d o w s能够为所有的应用程序维护一个消息队列。应用程序必须从消息队列中获取
消息,然后分派给某个窗口。
• 消息循环。通过这个循环机制应用程序从消息队列中检索消息,再把它分派给适当的窗口,然
后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。
• 窗口过程。每个窗口都有一个窗口过程来接收传递给窗口的消息,它的任务就是获取消息然后
响应它。窗口过程是一个回调函数;处理了一个消息后,它通常要返回一个值给Wi n d o w s。
注意回调函数是程序中的一种函数,它是由Windows或外部模块调用的。
一个消息从产生到被一个窗口响应,其中有5个步骤:
1) 系统中发生了某个事件。
2) Wi n d o w s把这个事件翻译为消息,然后把它放到消息队列中。
3) 应用程序从消息队列中接收到这个消息,把它存放在T M s g记录中。
4) 应用程序把消息传递给一个适当的窗口的窗口过程。
5) 窗口过程响应这个消息并进行处理。
步骤3和4构成了应用程序的消息循环。消息循环往往是Wi n d o w s应用程序的核心,因为消息循环
使一个应用程序能够响应外部的事件。消息循环的任务就是从消息队列中检索消息,然后把消息传递
给适当的窗口。如果消息队列中没有消息,Wi n d o w s就允许其他应用程序处理它们的消息。
有的时候,可能需要向一个窗口发送一个消息,而又不知道这个窗口的实例。例如,可能要给一
个非D e l p h i的窗口发送一个消息,而只有这个窗口的句柄。幸运的是, Wi n d o w s有两个A P I函数可以
实现这一点: S e n d M e s s a g e ( )和P o s t M e s s a g e ( )。这两个函数几乎是一样的,它们的区别是: S e n d M e -
s s a g e ( )直接把一个消息发送给窗口过程,等消息被处理后才返回。P o s t M e s s a g e ( )只是把消息发送到消
息队列,然后立即返回。
尽管S e n d M e s s a g e ( )和P o s t M e s s a g e ( )调用方式完全一样,但它们的返回值不一样。
S e n d M e s s a g e ( )返回此消息被处理的结果值,而P o s t M e s s a g e ( )返回一个布尔值,表示消息是否
已被放到消息队列中。
 
看几段程序就明白了,有邮箱我发过去。
 
to jinmen:
walkinsnow_st@sohu.com 谢谢
 
《Delphi5开发人员指南》不就也有?
 
windows 程序设计(上下册)----160RMB
很详细的!经典!
 
大哥们推荐一些好书,谢谢了 :)

有很多很基础的东西不是大家都清楚的。

我到现在连Objet Pascal都不是很好。

因为没学过C++。

API和消息也是用的时候找相关例子看看,照着写。

不知什么时候才能成为高手L(
 
windows 程序设计(上下册
 
多人接受答案了。
 

Similar threads

顶部