理解消息循环

在创建窗口、显示窗口、更新窗口后,我们需要编写一个消息循环,不断地从消息队列中取出消息,并进行响应。要从消息队列中取出消息,我们需要调用GetMessage()函数,该函数的原型声明如下:
BOOL GetMessage(# {; S' j/ ~+ q. P9 D
        LPMSG lpMsg,            // address of structure with message7 e' v; P# |% M' |' t$ w( D
        HWND hWnd,               // handle of window4 O+ c7 Z- d1 q4 U
        UINT wMsgFilterMin,     // first message
       UINT wMsgFilterMax      // last message1 `  l+ H" h' M2 H. `, J4 T! }
);
)
参数lpMsg指向一个消息(MSG)结构体,GetMessage从线程的消息队列中取出的消息信息将保存在该结构体对象中。5 U2 p1 a' e8 D  a
参数hWnd指定接收属于哪一个窗口的消息。通常我们将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。+ a4 O8 p' r+ l! ]2 k$ l2 m
参数wMsgFilterMin指定要获取的消息的最小值,通常设置为0。! V* D+ y( U+ X9 Q
参数wMsgFilterMax指定要获取的消息的最大值。如果wMsgFilterMin和wMsgFilter Max都设置为0,则接收所有消息。+ R4 \- U0 @0 j) P& R7 f; L
GetMessage函数接收到除WM_QUIT外的消息均返回非零值。对于WM_QUIT消息,该函数返回零。如果出现了错误,该函数返回-1,例如,当参数hWnd是无效的窗口句柄或lpMsg是无效的指针时。
通常我们编写的消息循环代码如下:
O
MSG msg;" K( Z4 r. ^& i& g0 I
while(GetMessage(&msg,NULL,0,0))4 W  h& P4 b# X' w8 t& U0 B. L
{
    TranslateMessage(&msg);: I9 H! B- ?- c, c
    DispatchMessage(&msg);
}, X& |& \/ V- N: E' `& U
前面已经介绍了,GetMessage函数只有在接收到WM_QUIT消息时,才返回0。此时while语句判断的条件为假,循环退出,程序才有可能结束运行。在没有接收到WM_QUIT消息时,Windows应用程序就通过这个while循环来保证程序始终处于运行状态。! n7 t/ [& b. D* F' V, H, R) H7 K
TranslateMessage函数用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。当我们敲击键盘上的某个字符键时,系统将产生WM_KEYDOWN和WM_KEYUP消息。这两个消息的附加参数(wParam和lParam)包含的是虚拟键代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII码,TranslateMessage这个函数就可以将WM_KEYDOWN和WM_ KEYUP消息的组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII码),并将转换后的新消息投递到调用线程的消息队列中。注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。$ K0 o$ M* c; u0 A# `4 z
DispatchMessage函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。DispachMessage实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应)。  a% d) a- S. V2 ]
Windows应用程序的消息处理机制如图1.2所示。4 t; C) \6 y- ~% _8 E0 r0 @; ]; R

(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
(2)应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。
(3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage函数总能进行正确的传递。
(4)系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。4 z: L! m" ]( T! a$ H# T
以上就是Windows应用程序的消息处理过程。8 I7 j) g2 H3 @8 A+ }" W
+ Y) r0 ]1 s8 H" a! Y9 V  }
提示:$ E+ q/ \3 w5 s
(1)从消息队列中获取消息还可以调用PeekMessage函数,该函数的原型声明如下所示:
BOOL PeekMessage(6 w- O; z3 U* j1 p. Z) t6 r! u
  LPMSG lpMsg,           // message information
  HWND hWnd,              // handle to window
  UINT wMsgFilterMin, // first message
  UINT wMsgFilterMax,  // last message6 O& D/ d: k6 [; h; q1 E
  UINT wRemoveMsg    // removal options

);) A* I9 D& Z* b$ |
前4个参数和GetMessage函数的4个参数的作用相同。最后1个参数指定消息获取的方式,如果设为PM_NOREMOVE,那么消息将不会从消息队列中被移除;如果设为PM_REMOVE,那么消息将从消息队列中被移除(与GetMessage函数的行为一致)。关于PeekMessage函数的更多信息,请参见MSDN。+ G4 f/ K3 q9 o
(2)发送消息可以使用SendMessage和PostMessage函数。SendMessage将消息直接发送给窗口,并调用该窗口的窗口过程进行处理。在窗口过程对消息处理完毕后,该函数才返回(SendMessage发送的消息为不进队消息)。PostMessage函数将消息放入与创建窗口的线程相关联的消息队列后立即返回。除了这两个函数外,还有一个PostThreadMessage函数,用于向线程发送消息,对于线程消息,MSG结构体中的hwnd成员为NULL。
虽名为队列(queue)但是消息队列中的消息并非总是先进先出(First In First Out,FIFO),有一些特例:

(1 _只要消息队列中有WM_QUIT,就会先取出WM_QUIT,导致程序结束。

(2)只有在没有其它消息的时候,WM_PAINT 和WM_TIMER才会被取出。且多个WM_PAINT可能会被合并成一个,WM_TIMER也是如此。

(3) 利用TranslateMessage()来处理消息,可能会造成新消息的产生。例如:TranslateMessage()可以辨识出WM_KEYDOWN(按键按下)加上WM_KEYUP(按键放开)就产生WM_CHAR(字符输入)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值