Windows消息编程


简单理解Windows的消息


消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。

消息一般用一个32位的数来标识,这个数唯一地标识这个消息。这些消息的标识符一般在头文件winuser.h 中定义,如:

#define WM_PAINT 0x000F

#define WM_QUIT 0x0012 其实消息本身是一个MSG结构。

MSG结构定义如下: 

typedef struct tagMSG 

   HWND hwnd; //接受消息的窗口句柄 

   UINT message; //消息标识符 

   WPARAM wParam; //32位附加信息

  LPARAM lParam; //32位附加信息

  DWORD  time; //消息创建的时间 

   POINT pt; //消息创建时鼠标在屏幕坐标系中的位置 

} MSG;

也就是说,对于任何一个消息,都有一个MSG变量与之对应,该变量包含了消息的相关信息。而我们在一般情况下,只使用消息的消息标识符,该标识符也唯一地代表了这个消息。举个例子来说,当我们收到一个字符消息的时候,message成员变量的值就是WM_CHAR,但用户到底输入的是什么字符,那么就由wParam和lParam来说明。wParam、lParam表示的信息随消息的不同而不同.
Windows操作系统已经给我们定义了大量的消息,这些消息我们称为系统消息。除了系统消息,我们还可以自己定义消息,即自定义消息。值小于0x0400的消息都是系统消息,自定义消息一般都大于0x0400。系统消息取值一般有如下规律,

如表1: 范围 意义 

0x0001——0x0087 主要是窗口消息 

0x00A0——0x00A9 非客户区消息 

0x0100——0x0108 键盘消息 

0x0111——0x0126 菜单消息 

0x0132——0x0138 颜色控制消息

0x0200——0x020A 鼠标消息 

0x0211——0x0213 菜单循环消息 

0x0220——0x0230 多文档消息 

0x03E0——0x03E8 DDE消息 

0x0400 WM_USER 0x0400——0x7FFF 自定义消息 


GetMessage,TranslateMessage,DispatchMessage,PeekMessage,WaitMessage。 
 

1、GetMessage 函数原型: 

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); 

参数:   lpMsg:一个指向MSG结构的指针,该结构用于存放从消息队列里取出的消息。

    hWnd:窗口句柄。如果该参数是非零值,则GetMessage只检索该窗口(也包括其子窗口)消息,如果为零,则GetMessage检索整个进程内的消息。 

    wMsgFilterMin:指定被检索的最小消息值,也就是消息范围的下界限参数。

    wMsgFilterMax:上界限参数。如果wMsgFilterMin和wMsgFilterMax都为零,则不进行消息过滤,GetMessage检索所有有效的消息。

返回值 GetMessage检索到WM_QUIT消息,返回值是零;其它情况,返回非零值。  

函数功能: 这个API函数用来从消息队列中“摘取”一个消息,放到lpMsg所指的变量里。(注:如果所取窗口的消息队列中没有消息,则程序会暂停在GetMessage(…)函数里,不会返回。) 再通俗一点讲解GetMessage函数:当程序执行GetMessage()的时候,会检查消息队列,如果有消息在消息队列里,它取出该消息,将该消息填充到lpMsg所指的MSG结构,并返回TRUE值。如果此时消息队列里没有消息(消息队列为空),它会将线程阻塞,也就是将控制权交给系统,直到消息队列中有内容时,才唤醒线程继续执行。对于GetMessage()函数,还有一点需要说明,就是当从消息队列中取出的消息是WM_QUIT时,函数返回值是0。我们一般利用这一点退出消息循环,结束程序。

2 、PeekMessage 函数原型:  

BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);  

参数: lpMsg、hWnd、wMsgFilterMin、wMsgFilterMax这四个参数的意义和GetMessage对应参数的意义相同,在此不再赘述。

wRemoveMsg:这个参数决定读消息时是否删除消息,可选值有PM_NOREMOVE和PM_REMOVE。如果您选PM_NOREMOVE,执行该函数后消息仍然留在消息队列(我称为读消息);如果您选PM_REMOVE,执行该函数后将在消息队列中移除该消息(同GetMessage())。 

返回值: 消息队列中有消息,返回值为TRUE;消息队列中没有消息,返回值为FALSE。  

函数功能: PeekMessage()也是从消息队列中取消息,但它是GetMessage()不同,主要在以下两点:

(一)、GetMessage()只能从消息队列中取走消息,也就是说,GetMessage()执行后,该消息将从消息队列中移除。 PeekMessage()可以从消息队列中取走消息。也可以读消息,让消息继续留在消息队列里。

(二)、当消息队列中没有消息时,GetMessage()将会阻塞线程,等待消息;而PeekMessage()与GetMessage()不同,它执行后会立刻返回,消息队列中有消息时,返回值为TRUE;消息队列中没有消息时,返回值为FALSE。  

 3 、WaitMessage 函数原型:  

BOOL WaitMessage(VOID);  

函数功能:这个函数的作用是当消息队列中没有消息时,将控制权交给其它线程。该函数将会使线程挂起,直到消息队列中又有新消息。这个函数专门和PeekMessage配合使用,当消息队列中没有消息时,挂起线程,等待消息队列中新消息的到来,这样可以减轻CPU的运算负担。  

4 、TranslateMessage  函数原型:

BOOL TranslateMessage(CONST MSG*lpMsg);  

参数:   IpMsg:指向MSG结构的指针,该结构是函数GetMessage或PeekMessage从消息队列里取得的消息。 

函数功能:该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里,当下一次线程调用函数GetMessage或PeekMessage时被读出。

什么是虚拟键码呢?Windows为了方便输入管理,减少程序对设备的依赖性,将键盘上所有的按键都用一个两位十六进制数对应,这些数称为虚拟键码。虚拟键码一般以VK_开头,如:Esc键对应的虚拟键码是VK_ESCAPE;空格键对应的虚拟键码是VK_SPACE;VK_LWIN与左边的Windows徽标键相对应。当一个按键被按下时,会触发WM_KEYDOWN消息, WM_KEYDOWN消息的wParam参数值就是虚拟键值。通过这个值就可以判断哪个键被按下了。为什么我们要把虚拟键码转换为字符码呢?比如我们按下了‘A’键,此时我们得到的字符可能是‘A’,也可能是小写的‘a’,这由当时的大写状态(Caps Lock)以及是否同时按下了Shift键有关。TranslateMessage()函数的作用就是不用我们考虑这些问题,而是根据这些情况,自动返回一个ASCII码值,以方便用户使用。并不是所有的虚拟键码值都会Translate成字符码。字母、数字键都有字符码相对应,而像方向箭头键、F1—F12功能键这些按键就没有字符码相对应。当虚拟键码需要转化成字符码时,TranslateMessage()函数就在消息队列里放一条WM_CHAR消息,WM_CHAR消息的wParam参数值就是转换后的ASCII码值。   

5、DispatchMessage  函数原型

LONG DispatchMessage(CONST MSG *lpmsg);

函数功能:它的作用很简单,就是分派消息到窗口的消息处理函数去执行。

PostMessage、SendMessage


 队列消息和非队列消息 Windows把消息分为两种:一种是需要立即处理的消息,另一种是不需要立即处理的消息。

对于需要立即处理的消息,Windows直接把它送给窗口的消息处理函数进行处理,这类消息我们叫做非队列消息;而对于不需要立即处理的消息,Windows会把它发送给应用程序的消息队列进行排队,由应用程序逐个进行处理,我们把这类消息叫做队列消息。为了更清楚地说明这个问题

1、Windows操作系统有一个消息队列,它存放操作系统收到的消息。如:当按键被按下,键盘会发送一个消息到操作系统的消息队列。

2、操作系统把系统消息队列中的消息分派到各个应用程序的消息队列。如果它是第1个应用程序的消息,操作系统把它发给第1个应用程序,把它放在第1个应用程序的消息队列;如果它是第2个应用程序的消息,发送给第2个程序的消息队列。

3、应用程序的消息循环从自己的消息队列中取消息,取出的消息调用窗口过程函数进行处理。

4、PostMessage是寄送消息,函数执行后立即返回。寄送的消息是队列消息,放在程序的消息队列中排队处理。

一般来说,新寄送的消息排在消息队列的末尾,这样可以保证窗口以先进先出的顺序处理消息。 PostMessage是发送消息,它发出的消息是非队列消息,直接调用窗口过程函数处理。SendMessage函数一直等消息处理完成后才返回。 我们有必要再专门学习一下SendMessage和PostMessage函数。   

SendMessage的函数原型:

LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

这个函数向窗口发送一条消息,一直等到消息被处理之后才返回。也就是说,接收消息的窗口的窗口函数立即被调用。函数的返回值由接收消息的窗口的窗口函数返回。   

PostMessage的函数原型:

  BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);

该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。从上面这两个函数,我们可以看出消息的发送方式和寄送方式的区别:被发送的消息会被立即处理,处理完毕后函数才返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,按次序等候处理,而且函数放置消息后立即返回。以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓冲处理,例如鼠标、键盘消息都是寄送消息。应用程序调用系统函数,系统一般会发送非队列消息。例如,当程序调用SetWindowPos,系统会发送WM_WINDOWPOSCHANGED消息。  

WM_COMMAND和WM_NOTIFY 控件通知消息


是指这样一种消息,一个窗口内的控件发生了一些事情,需要通知父窗口。当用户与控件窗口交互时,控件通知消息就会从控件窗口发送到它的主窗口,这种消息一般不是为了处理用户命令,而是为了让主窗口能够改变控件。 WM_COMMAND和WM_NOTIFY都是控件通知消息。 在最初的Windows3.x中,还没有WM_NOTIFY,只存在WM_COMMAND消息,wParam参数中包含一个通知码和控件ID,lParam中包含控件句柄。这样一来,wParam和lParam都被填充了,没有额外的空间来传递一些其它信息,如鼠标按下的位置和时间。为了解决这个问题, Windows3.x就提出了一个解决策略,那就是给一些消息添加一些附加消息,比如控件自画用到的DRAWITEMSTRUCT等,这样,不同的消息附加的内容不同,结果是非常混乱。在Win32中,微软又提出了一个更好的解决方案,引进了NMHDR结构。这个结构的引进把消息统一起来,利用它可以传递各种复杂的消息。  

NMHDR结构内容如下:

NMHDR

{

  HWND hWndFrom;//相当于原WM_COMMAND消息的

  lParam  UINT idFrom; //相当于原WM_COMMAND消息的

  wParam(LOWORD)  UINT code; //相当于原WM_COMMAND消息的

  wParam(HIWORD)通知码 

}

使用这个结构,WM_NOTIFY还可以附带更多的信息,您可以定义一个更大的结构,这个结构的第一个元素就是NMHDR结构,在该元素的后面您还可以放置其它附加信息。由于在这个大结构中,第一个成员是NMHDR,这样一来,我们就可以利用指向NMHDR的指针来指向这个结构,不论后面有没有其它内容。可见,WM_NOTIFY和WM_COMMAND相比,是一种更灵活的消息格式,lParam中放的是一个称为NMHDR结构的指针。在wParam中放的则是控件的ID。


消息反射机制


父窗口将控件发给它的通知消息,反射回控件进行处理(即让控件处理这个消息),这种通知消息让控件自己处理的机制叫做消息反射机制。通过前面的学习我们知道,一般情况下,控件向父窗口发送通知消息,由父窗口处理这些通知消息。这样,父窗口(通常是一个对话框)会对这些消息进行处理,换句话说,控件的这些消息处理必须在父窗口类体内,每当我们添加子控件的时候,就要在父窗口类中复制这些代码。很明显,这对代码的维护和移植带来了不便,而且,明显背离C++的对象编程原则。 具体地讲,对于反射消息,如果控件有该消息的处理函数,那么就由控件自己处理该消息,如果控件不处理该消息,则框架会把该消息继续送给父窗口,这样父窗口继续处理该消息。可见,新的消息反射机制并不破坏原来的通知消息处理机制。  消息反射机制为控件提供了处理通知消息的机会,这是很有用的。如果按传统的方法,由父窗口来处理这个消息,则加重了控件对象对父窗口的依赖程度,这显然违背了面向对象的原则。若由控件自己处理消息,则使得控件对象具有更大的独立性,大大方便了代码的维护和移植。

转载于:https://www.cnblogs.com/zxcode/archive/2011/03/03/1970245.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值