【MFC】PeekMessage() 与GetMessage() 消息函数

01、目录

02、简言

想必了解过MFC(windows程序)的都知道,windows的程序全是由消息驱动的。不敢干什么,都是通过各种各样的消息来做一些事。

消息队列(MQ)这一词,可能一眼看去有点很高端的感觉,其实也就是跟队列一样的性质,一个一个消息排队而存。链接了一篇简书上介绍消息队列概念的文章,感兴趣可以看看。其实程序中,我们一般用不上,有个认知就行了。

而今天,我们要聊的两个函数,就是针对这个消息队列而存在的。

  1. PeekMessage()
  2. GetMessage()

见明知其意:都有得到消息或者说看消息的意思。
下面我们就来好好聊聊这两个消息函数,结合程序了解如何使用,什么时候用。

03、PeekMessage() Function

微软文档解释:分发传入的发送消息,检查线程消息队列中的已发布消息,并检索消息(如果存在的话)。

3.1 语法(Syntax)

函数原型,C++程序:

BOOL PeekMessageA(
  LPMSG lpMsg,
  HWND  hWnd,
  UINT  wMsgFilterMin,
  UINT  wMsgFilterMax,
  UINT  wRemoveMsg
);
3.2 参数(Parameters)

参数解释:

  • lpMsg:A pointer to an MSG structure that receives message information.(指向接收消息信息的MSG结构的指针。)
  • hWnd:A handle to the window whose messages are to be retrieved. The window must belong to the current thread.(要检索消息的窗口的句柄。窗口必须属于当前线程。)

如果HWND是零, PeekMessage检索属于当前线程的任何窗口的消息,以及当前线程消息队列上的任何消息Hwnd价值是零(见味精结构)。因此,如果hwnd是零,则同时处理窗口消息和线程消息。

如果HWND是-1,PeekMessage仅检索当前线程的消息队列上的消息Hwnd价值是零的线程消息。邮政讯息(当HWND参数是零)或后线程消息.

  • wMsgFilterMin:The value of the first message in the range of messages to be examined. Use WM_KEYFIRST (0x0100) to specify the first keyboard message or WM_MOUSEFIRST (0x0200) to specify the first mouse message.(要检查的消息范围中第一条消息的值。使用Wm_KEYFIRST(0x0100)指定第一条键盘消息或Wm_MOUSEFIRST(0x0200)指定第一个鼠标消息。)

如果WMsgFilterMin和WMsgFilterMax都是零,PeekMessage返回所有可用消息(即不执行范围筛选)。

  • wMsgFilterMax:The value of the last message in the range of messages to be examined. Use WM_KEYLAST to specify the last keyboard message or WM_MOUSELAST to specify the last mouse message.(要检查的邮件范围中最后一条消息的值。使用WM_KEYLAST指定最后一条键盘消息,或使用WM_MOUSELAST指定最后一条鼠标消息。)

如果WMsgFilterMin和WMsgFilterMax都是零,PeekMessage返回所有可用消息(即不执行范围筛选)。

  • wRemoveMsg:Specifies how messages are to be handled. This parameter can be one or more of the following values.(指定如何处理消息。此参数可以是下列一个或多个值。)
valueMeaning
PM_NOREMOVE处理后,消息不会从队列中移除。PeekMessage.
PM_REMOVE处理后,消息将从队列中移除。PeekMessage.
PM_NOYIELD防止系统释放等待调用方空闲的线程(请参阅WaitForInputIdle).将此值与PM NOREMOVE或PM去除.

默认情况下,将处理所有消息类型。若要指定只应处理某些消息,请指定下列一个或多个值。

valueMeaning
PM_QS_INPUT处理鼠标和键盘消息。
PM_QS_PAINT处理绘制消息。
PM_QS_POSTMESSAGE处理所有已发布的消息,包括计时器和热键。
PM_QS_SENDMESSAGE处理所有发送的消息。
3.3 返回值(Return value)

如果消息可用,则返回值为非零。
如果没有可用的消息,则返回值为零。

04、GetMessage() Function

微软文档解释:从调用线程的消息队列中检索消息。该函数将发送传入的发送消息,直到发布的消息可供检索为止。

4.1 语法(Syntax)

函数原型,C++程序:

BOOL GetMessage(
  LPMSG lpMsg,
  HWND  hWnd,
  UINT  wMsgFilterMin,
  UINT  wMsgFilterMax
);
4.2 参数(Parameters)
  • lpMsg:A pointer to an MSG structure that receives message information.(指向接收消息信息的MSG结构的指针。)
  • hWnd:A handle to the window whose messages are to be retrieved. The window must belong to the current thread.(要检索消息的窗口的句柄。窗口必须属于当前线程。)

如果hWnd为NULL,GetMessage将检索属于当前线程的任何窗口的消息,以及当前线程消息队列中hwnd值为空的任何消息(请参见MSG结构)。因此,如果hWnd为NULL,则同时处理窗口消息和线程消息。
如果hWnd为-1,GetMessage只检索当前线程的消息队列上的消息,该队列的hwnd值为null,即由PostMessage(当hWnd参数为NULL)或PostThreadMessage发送的线程消息。

  • wMsgFilterMin:The value of the first message in the range of messages to be examined. Use WM_KEYFIRST (0x0100) to specify the first keyboard message or WM_MOUSEFIRST (0x0200) to specify the first mouse message.(要检查的消息范围中第一条消息的值。使用Wm_KEYFIRST(0x0100)指定第一条键盘消息或Wm_MOUSEFIRST(0x0200)指定第一个鼠标消息。)

  • wMsgFilterMax:The value of the last message in the range of messages to be examined. Use WM_KEYLAST to specify the last keyboard message or WM_MOUSELAST to specify the last mouse message.(要检查的邮件范围中最后一条消息的值。使用WM_KEYLAST指定最后一条键盘消息,或使用WM_MOUSELAST指定最后一条鼠标消息。)

4.3 返回值(Return value)

如果函数检索的消息不是Wm退出,返回值为非零。
如果函数检索Wm退出消息时,返回值为零。

如果出现错误,则返回值为-1。例如,如果hWnd是无效的窗口句柄或lpMsg是无效指针,则函数将失败。要获得扩展错误信息,请调用GetLastError。

由于返回值可以是非零、零或-1,所以请避免这样的代码:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

在hWnd是无效参数的情况下,返回值的可能性(例如引用已被销毁的窗口)意味着此类代码可能导致致命的应用程序错误。相反,使用如下代码:

BOOL bRet;

while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}

05、示例助教

// 普通的消息循环 
while( GetMessage(&msg,NULL,0,0) )
{
  TranslateMessage(&msg);
  DispatchMessage (&msg);
}
return msg.wParam;

// 等价于
while( TRUE )
{
  if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) )
  {
    if(msg.message == WM_QUIT)
    {
      break;
    }
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  else
  {
    // Other program lines to do some work
  }
}
return msg.wParam;

如果 PeekMessage 的返回值为 TRUE,则消息按通常方式进行处理。如果返回值为 FALSE,则在将控制返回给Windows(操作系统) 之前,还可以做一点工作(如显示另一个随机矩形)。

PeekMessage 不能从消息队列中删除 WM_PAINT 消息。
从队列中删除 WM_PAINT 消息的唯一方法是令窗口客户区的失效区域变得有效,这可以用 ValidateRect 和 ValidateRgn 或者 BeginPaint 和 EndPaint 对来完成。

不能使用如下所示的代码来清除消息队列中的所有消息:

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

这条语句从消息队列中删除 WM_PAINT 之外的所有消息。如果队列中有一个 WM_PAINT 消息,程序就会永远地陷在 while 循环中。

了解完了两个函数的基本概念知识,现在结合下面在网上Copy的例子,感受下吧。

#include <windows.h>
#include <stdlib.h>           // for the rand function

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
void DrawRectangle (HWND) ;

int cxClient, cyClient ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("RandRect") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;
     
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"),
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName, TEXT ("Random Rectangles"),
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     //用于替换的部分 
    while (TRUE)
     {
          if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
          {
               if (msg.message == WM_QUIT)
                    break ;
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
          }
          else
               DrawRectangle (hwnd) ;
     }
    //用于替换的部分 
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM

lParam)
{
     switch (iMsg)
     {
     case WM_SIZE:
          cxClient = LOWORD (lParam) ;
          cyClient = HIWORD (lParam) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}

void DrawRectangle (HWND hwnd)
{
     HBRUSH hBrush ;
     HDC    hdc ;
     RECT   rect ;
     
     if (cxClient == 0 || cyClient == 0)
          return ;
     
     SetRect (&rect, rand () % cxClient, rand () % cyClient,
                     rand () % cxClient, rand () % cyClient) ;
     
     hBrush = CreateSolidBrush (
                    RGB (rand () % 256, rand () % 256, rand () % 256)) ;
     hdc = GetDC (hwnd) ;
     
     FillRect (hdc, &rect, hBrush) ;
     ReleaseDC (hwnd, hdc) ;
     DeleteObject (hBrush) ;
} 

以上程序用PeekMessage函数实现,在应用程序消息队列没有消息时,随机生成的矩形

将坚持不懈地画下去。当有消息产生时,PeekMessage函数获取并派发消息出去,然后

继续画随机生成的矩形。

下面部分代码用于替换WinMain函数中“用于替换的部分”的代码。

while (TRUE)
     {
          if (GetMessage (&msg, NULL, 0, 0))
          {
               if (msg.message == WM_QUIT)
                    break ;
               TranslateMessage (&msg) ;
               DispatchMessage (&msg) ;
           DrawRectangle (hwnd) ;
          } 
          else
                break;
     }

替换后,应用程序产生一个消息(鼠标移动、改变窗体大小等),在窗体内画一个随

机生成的矩形,无消息产生时,窗体无变化。

06、总结

PeekMessage与GetMessage的对比:

  • 相同点:PeekMessage函数与GetMessage函数都用于查看应用程序消息队列,有消息时将队列中的消息派发出去。

  • 不同点:无论应用程序消息队列是否有消息,PeekMessage函数都立即返回,程序得以继续执行后面的语句(无消息则执行其它指令,有消息时一般要将消息派发出去,再执行其它指令)。
    GetMessage函数只有在消息对立中有消息时返回,队列中无消息就会一直等,直至下一个消息出现时才返回。在等的这段时间,应用程序不能执行任何指令。

从他们的不同点上来看,PeekMessage函数有点像“乞丐行乞”,有你就施舍点,没有也不强求。GetMessage函数有点像“强盗打劫”,有你得给,没有我就等你什么时候有了再给,这段时间我什么都不干,我就等你。

上述Code是借鉴网上的一个例子,概念是部分来源微软文档,外加自己组织了一点语言。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cain Xcy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值