windows消息机制二

消息能够被分为队列化的和非队列化的。队列化的消息时有windows放入程序消息队列中的。在程序的消息循环中,重新传回并分配给窗口消息处理程序。非队列化的消息在windows调用窗口时直接接送给窗口消息处理程序。也就是说,队列化的消息被发送给消息队列,而非队列化的消息则发送给窗口消息处理程序,任何情况下,窗口消息处理程序都将获得窗口所有的消息----包括队列化的和非队列化的。窗口消息处理程序是窗口的消息中心。

  队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT).

  在许多情况下,非队列化消息来自调用特定的windows函数。例如,当WinMain调用CreateWindow时,windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。当WinMain调用ShowWindow时,windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。

  虽然windows程序可以多线程执行,但消息队列只为窗口消息处理程序在该执行中的窗口处理消息。换句话说,消息循环和窗口消息处理程序不是并发执行。当一个消息循环从其消息队列中接收一个消息,然后调用DispatchMessage将消息发送给窗口消息处理程序时,直到窗口消息吹了程序将控制传回给windows,DispatchMessage才能结束执行。

  当然,窗口消息处理程序能调用给窗口消息处理程序发送另一个消息的函数。这时,窗口消息处理程序必须在函数调用传回之前完成对第二个消息的处理。那时窗口消息处理程序将处理最初的消息。例如,当窗口过程调用UpdateWindow时,windows将调用窗口消息处理程序来处理WM_PAINT消息。窗口消息处理程序处理WM_PAINT消息结束以后,UpdateWindow调用将把控制传回给窗口消息处理程序。

  在工程中经常还会有这样的需求,即要求程序在空闲的时候执行某种额外的操作或运算。实际上在windows中有很多闲置时间,在这个时间内,所有消息队列空,Windows只停在一个小循环中等待键盘或者鼠标输入。能否在闲置时间内获得控制,从而做某种操作或运算,并且只在有消息加入程序的消息队列之后才释放控制呢?这就是PeekMessage函数的目的之一。下面是PeekMessage调用一个例子:

  PeekMessage(&msg,NULL,0,0,PM_PEMOVE);

  前面的4个参数(一个纸箱MSG结构的指针、一个窗口句柄、两个值只是消息范围)与GetMessage的参数相同。将第二、三、四个参数设定为NULL或0时,表明想让PeekMessage传回程序中所有窗口的所有消息。如果要将消息从消息队列中删除,则将PeekMessage的最后一个参数设定为PM_REMOVE.如果不希望删除消息,那么可以将这个参数设定为PM_NOREMOVE。

  GetMessage不将控制传回给程序,他一直处于阻塞状态,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现。当消息队列中有一个消息时,PeekMessage的传回值为TRUE,并且将按通常方式处理消息。当队列中没有消息时,PeekMessage传回FALSE。

  可以将如下所示的消息循环:

  while(GetMessage(&msg,NULL,0,0))

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

  替换为下面的循环:

  while(TRUE)

  {

    if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))

    {

      TreanslateMessage(&msg);

      DispatchMessage(&msg);

    }

    else

    {

      //完成其他额外的工作

    }

  }

  return msg.wParam;

  如果PeekMessage的传回值TRUE,则消息按通常方式进行处理。如果传回值为FALSE,则在将控制传回给Windows之前,还可以做一点工作。

  需要注意的是,不能用PeekMessage从消息队列中删除WM_PAINT消息,实际上GetMessage并不从消息队列中删除WM_PAINT消息。从队列中删除WM_PAINT消息的唯一方法是令窗口显示区域的实效区域变得有效,着可以用ValidateRect和ValidateRgn或者BeginPaint和EndPaint对来完成。如果在使用PeekMessage从队列中取出WM_PAINT消息后,同平常一样处理它,那么就不会有问题了。所不能做的是使用如下所示的程序代码来清除消息队列中的所有消息:

  while(PeekMessage(&msg,NULL,0,0,PM_REMOVE));

  这行代码从消息队列中删除WM_PAINT之外的所有消息。如果队列中有一个WM_PAINT消息程序就会永远的陷在while循环中。

原文地址:https://www.cnblogs.com/newlist/p/2679372.html