Windows 消息机制浅析--Delphi

1.消息是指什么?
     消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。

   Windows以record的形式发送消息给应用程序。记录中包括事件的类型以及附加的特定信息。Windows发送给应用程序的记录类型是TMsg,定义在windows.pas单元中,定义如下:

{ 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:32位windows句柄,指向消息要发往的窗口,这个窗口可以是几乎所有的屏幕对象,因为window对大多数可是对象都维护了一个窗口句柄。 

Message:代表某种消息的常量值,这些常量可以是在windows.pas中预定义的标准windows消息,也可以是用户自定义的消息。

Wparam:这个字段常常包含和消息关联的常量值,也可以包含一个窗口句柄或者消息关联的某个窗口或控件的ID值。

Lparam:这个字段经常容纳一个对内存数据的索引或指针,由于wparam和lparam都是32位大小,我们可以进行强制类型转换。

WM开头的通常是指WindowsMessage.

 

2.常用windows消息:

wm_active:窗口被激活

wm_char按下某个键发送wm_keydown和wm_keyup消息

wm_close窗口将要关闭

wm_keydown用户正在按下键盘上的一个键

wm_keyup:用户已经释放按下的键

wm_lbuttondown用户按下鼠标左键

wm_mousemove用户正在移动鼠标

wm_paint必须重绘窗口区域

wm_timer发生了一个计时器事件

wm_quit:发送终止程序请求

 

3.windows消息系统的工作方式

三个部分:消息队列、消息循环、窗口过程。

消息队列:Windows为每个应用程序维护一个消息队列,windows应用程序必须从这个队列中取得消息,并且把得到的消息分派到合适的窗口。

消息循环:windows程序从应用程序队列中取得一条消息,分派到适合的窗口中,然后再取得下一条消息,再分发到适合的窗口中,如此循环。这种消息机制就是消息循环。

窗口过程:应用程序中的每个窗口都有一个窗口过程,它接受消息循环中传入的每条消息。窗口过程的任务是接受各个窗口消息并且对此做相应的回应。窗口过程是所谓的回调函数,在处理完毕一条消息之后,窗口过程通常要给windows一个返回值。

 

4.发送自己的消息:

我们需要在应用程序窗口和控件之间发送消息。Delphi提供了以下几种方法:

Perform()方法:VCL为所有的TContol派生类提供了perform()方法,可以发送消息给任何已知对象实例的对象或控件对象。Perform有三个参数:消息,对应的lparam和wparam。如下:

function Tcontrol.perform(Msg :cardinal;wparam,lparam:longint):longint;

要发送一条消息给一个窗体或控件,使用下面的格式:

Retval:=controlname.perform(messageid,lparam,wparam); 

Perform是同步调用的,因此直到消息被处理完毕,才能得到返回值。Perform()方法把它的参数组合成一个TMessage记录,然后调用该对象的Dispatch方法去发送这条消息,绕开了windowsAPI消息系统。

Sendmessage()和postmessage()API函数:

Sendmessage和perform()类似,是同步调用的。消息直接发往目的窗口,该消息被处理完毕才返回;postmessage是异步调用的,发送消息给windows队列,然后立即返回。

通知消息:窗口的子控件发生事件,如果需要通知父窗口,就发送此消息。只发生在windows标准控件中如button、 listbox 、combox和通用控件如tree view、 list view等。

5.自定义消息号和消息结构

const
  WM_5001=WM_USER+5001;
  WM_5002=WM_USER+5002;
  WM_5003=WM_USER+5003;
  WM_6001=WM_USER+6001;

type

  //自定义消息结构
  PMsgRec = ^TMsgRec;
  TMsgRec = record
    msgNo   : Cardinal;
    msgText: string;
  end;

6.消息分发

向特定的对象分发消息:Dispatch函数

在TObject类中,有一个Dispatch()方法和一个DefaultHandler()方法,它们都是与消息分发机制相关的。

Dispatch()负责将特定的消息分发给合适的消息处理函数。首先它会在对象本身类型的类中寻找该消息的处理函数,如果找到,则调用它;如果没有找到而该类覆盖了TObject的DefaultHandler(),则调用该类的DefaultHandler();如果两者都不存在,则继续在其基类中寻找,直至寻找到TObject这一层,而TObject已经提供了默认的DefaultHandler()方法。  

消息接收类:TMsgAccepter

//自定义消息接收类
  TMsgAccepter=class
  private
    procedure Msg5001Accepter(var msg:TMsgRec); message WM_5001;

  public
    procedure defaultHandler(var message); override;

  end;

implementation
uses
  Main;

{ TMsgAccepter }
procedure TMsgAccepter.Msg5001Accepter(var msg: TMsgRec);
begin
    FrmMain.Memo1.Lines.Add('接收类已收到5001消息,并对其进行了处理!')
end;

procedure TMsgAccepter.defaultHandler(var message);
var
  theMsg:TMsgRec;
begin
  theMsg:=TMsgRec(message);
  FrmMain.Memo1.Lines.Add('接收类收到消息请求,但该消息号无指定处理函数!消息号为:'+IntToStr(theMsg.msgNo));
end;

a)向对象theMsgAccepter分发消息WM_5001(有对应处理函数)

var
  theMsg:TMsgRec;
begin
  theMsg.msgNo:=WM_5001;
  theMsg.msgText:='';
  theMsgAccepter.Dispatch(theMsg);
end;

运行结果:

      

b)向对象theMsgAccepter分发消息WM_6001(无对应处理函数)

var
  theMsg:TMsgRec;
begin
  theMsg.msgNo:=WM_6001;
  theMsg.msgText:='';
  theMsgAccepter.Dispatch(theMsg);
end;

运行结果:

       

 

7.消息投递:向特定窗口投递消息

消息响应窗口:TFrmMsgProcess

TFrmMsgProcess = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure Msg5002Accepter(var msg:TMessage); message WM_5002;
    procedure Msg5003Accepter(var msg:TMessage); message WM_5003;
  public
    { Public declarations }
  end;

var
  FrmMsgProcess: TFrmMsgProcess;

implementation
uses
  Main;

{$R *.dfm}

{ TFrmMsgProcess }

procedure TFrmMsgProcess.Msg5002Accepter(var msg:TMessage);
var
  pTheMsg:PMsgRec;
begin
   Self.Memo1.Lines.Add('窗口已收到5002号消息,并开始进行处理。。。');

   Self.Memo1.Lines.Add('窗口收到的字符串参数信息为:'+String(msg.WParam));
   pTheMsg:=PMsgRec(msg.LParam);
   Self.Memo1.Lines.Add('窗口收到的结构体参数信息为:'+pTheMsg.msgText);
   
   Self.Memo1.Lines.Add('窗口对5002号消息的处理已完成!');
end;

procedure TFrmMsgProcess.FormCreate(Sender: TObject);
begin
   self.Memo1.Color:=clblack;
   self.Memo1.Font.Color:=clgreen;
end;

procedure TFrmMsgProcess.Msg5003Accepter(var msg: TMessage);
begin
   FrmMain.Memo1.Lines.Add('窗口已收到5003号消息,并开始进行处理。。。');
   Sleep(1000);
   FrmMain.Memo1.Lines.Add('窗口对5003号消息的处理已完成!');
end;

a)异步方式:Postmessage,发出后不等待,直接返回

begin
  Self.Memo1.Lines.Add('主线程向子窗口发送5003号消息。。。');

  PostMessage(FrmMsgProcess.Handle,WM_5003,0,0);

  Self.Memo1.Lines.Add('主线程消息发送函数已结束!');
end;

运行结果:

       

b)同步方式:SendMessage,等窗口响应后,再返回

begin
  Self.Memo1.Lines.Add('主线程向子窗口发送5003号消息。。。');

  SendMessage(FrmMsgProcess.Handle,WM_5003,0,0);

  Self.Memo1.Lines.Add('主线程消息发送函数已结束!');
end;

c)带参数信息的消息投递

    发送“字符串或结构体”时,尽可能用sendMessage,否则,可能内存中的内容已被更改,而窗口消息响应函数还未处理。

var
  msgStr:string;
  theMsg:TMsgRec;
begin
  msgStr:='WM_5002消息的字符串说明';
  theMsg.msgNo:=WM_5002;
  theMsg.msgText:='WM_5002消息的结构体说明';
  SendMessage(FrmMsgProcess.Handle,WM_5002,WParam(PChar(msgStr)),LParam(@theMsg));
end;

运行结果:

         .

PS:以上均是在同一个进程里投递消息,若在不同进程间投递消息,则必须使用“内存映射文件”存储字符串或结构体消息,而不是直接放在消息参数sparam和lparan中,那样会出现地址空间访问冲突的问题。

 

8.其他

a)wparam与lparam

         wParam: 原win16系统中,word类型的整数,16位;现在的win32系统中,扩展为32位整型。
         lParam:   原win16系统中,long类型的整数,32位;现在的win32系统中,32位整型。

         注:WPARAM常常代表一些控件的ID或者高位底位组合起来分别表示鼠标的位置,如果

         消息的发送者需要将某种结构的指针或者是某种类型的句柄时,习惯上用LPARAM来传递。

 b)常用消息处理函数

    Peekmessage()   从消息队列中读消息
      Getmessage()   可以有选择的从消息列表中得到消息,一般用在window的窗口过程函数里面
      DispatchMessage()   用来派送消息到窗口过程
      TranslateMessage()   用于将Virtul-Key消息翻译为字符消息
      Postthreadmessage()   用于向线程发送消息
      Postmessage()   用于将消息传送到窗口的消息队列
      SendMessage()不经过消息队列而直接发送给窗口的

c)窗口过程函数

        从消息队列中取消息
        重写窗口默认“窗口过程函数”

d)消息路由

            

 e)参考界面 线程安全  

         http://blog.csdn.net/danscort2000/article/details/2780418   ——原理

         http://topic.csdn.net/t/20040908/15/3352501.html   ——消息机制的使用

         https://www.cnblogs.com/edisonfeng/archive/2012/05/14/2499647.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值