Windows UI 消息循环

Windows 程序是以消息为基础,以事件驱动的 (messagebased, event driven) ,用户的操作如移动鼠标,敲击键盘等动作可看成是事件 Event, 这些事件会产生相应的消息,这些消息为硬件设备产生的消息,会被放在 System queue 中。除此之外, Windows 系统或其它 Windows 程序也有可能传送消息到Application queue 中,当然也有message, 没有经过队列,直接发送到对应的窗口处理函数中。

 

 

每个窗口需要一个对应的窗口函数。这个窗口函数负责接收侦测回路Dispatch 的消息,并执行相应的动作,该窗口函数也就是程序员需要负责设计的一个回调函数,之所以称之为回调是因为它完全是由Windows 负责调用的。
那么为什么需要将窗口函数设计成回调函数呢?为什么不在 GetMessage 之后,直接调用窗口函数执行相关动作呢?这是因为除了你需要调用该函数,很多时候操作系统也需要调用,把窗口函数设计成Callback, 才能给操作系统提供一个接口。

/*------------------------------------------------------------

   HELLOWIN.C -- Displays"Hello, Windows 98!" in client area

                (c) Charles Petzold, 1998

 ------------------------------------------------------------*/

 

#include <windows.h>
 
LRESULT CALLBACK WndProc (HWND, UINT,WPARAM, LPARAM) ;
 
int WINAPI WinMain (HINSTANCEhInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
     static TCHARszAppName[] = TEXT ("HelloWin") ;
     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,                  // window class name
                         TEXT ("The Hello Program"),          //window caption
                         WS_OVERLAPPEDWINDOW,        // window style
                         CW_USEDEFAULT,             // initial x position
                         CW_USEDEFAULT,             // initial y position
                         CW_USEDEFAULT,             // initial x size
                         CW_USEDEFAULT,             // initial y size
                         NULL,                      // parent window handle
                         NULL,                      // window menu handle
                          hInstance,                // program instancehandle
                         NULL);                    // creation parameters
    
     ShowWindow(hwnd, iCmdShow) ;
     UpdateWindow(hwnd) ;
    
     while(GetMessage (&msg, NULL, 0, 0))
     {
         TranslateMessage (&msg) ;
         DispatchMessage (&msg) ;
     }
     returnmsg.wParam ;
}
 
LRESULT CALLBACK WndProc (HWNDhwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC                  hdc ;
     PAINTSTRUCT ps ;
     RECT                rect ;
    
     switch(message)
     {
     caseWM_CREATE:
         PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
         return 0 ;
         
     caseWM_PAINT:
         hdc = BeginPaint (hwnd, &ps) ;
         
         GetClientRect (hwnd, &rect) ;
         
         DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
                   DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
         
         EndPaint (hwnd, &ps) ;
         return 0 ;
         
     caseWM_DESTROY:
         PostQuitMessage (0) ;
         return 0 ;
     }
     returnDefWindowProc (hwnd, message, wParam, lParam) ;
}
 

-----------------------------------------------------------------------------------------------

1> RegisterClass 注册窗口类

——从 C++ 的角度考虑,相当于定义一个类;说明需要创建什么样的窗口 

2> CreateWindow   生成一个窗口

——类定义完成后,相当于定义一个对象,即按照注册的窗口类型,实例化了一个窗口,CreateWindow 运行后,会产生一个 WM_CREATE 消息,不经过队列,直接给窗口函数,一般我们可以在窗口响应该消息时,做一些初始化的动作。 

3> ShowWindow 显示生成的窗口

The first time an applicationcalls ShowWindow , it should use the WinMain function's nCmdShow nCmdShow parameter. Subsequent calls to ShowWindow must use one of thevalues in the given list, instead of the one specified by the WinMain function's nCmdShow parameter. 

parameter as its:

CreateWindow 的参数dwStyle中包含 WS_VISIBLE 时,不需要调用 ShowWindow   函数,程序运行后也可以显示窗口

CreateWindow的参数dwStyle中不包含WS_VISIBLE 时,需要调用 ShowWindow   函数,且调用时包含SW_SHOW,才可以显示窗口 

可以在上述代码中做如下实验:

屏蔽ShowWindow ,在CreateWindow   中添加 WS_VISIBLE ,程序运行后正常显示

屏蔽ShowWindow ,在CreateWindow   未包含 WS_VISIBLE ,程序运行后在任务管理器中可以看到运行的进程,但无法显示。

 

4> UpdateWindow 发送 WM_PAINT 给窗口函数 

The UpdateWindow functionupdates the client area of the specified window by sending a WM_PAINT messageto the window if the window's update region is not empty. The function sends aWM_PAINT message directly to the window procedure of the specifiedwindow, bypassing the application queue. If the update region is empty, nomessage is sent.

 

 MSDN 上关于消息循环和 WM_PAINT 的描述中看, WM_PAINT 的优先级其实很低,在消息队列中先响应完其它消息后,再响应 WM_PAINT 消息。

UpdateWindow 发送的 WM_PAINT 消息,不经过消息队列,会直接发送到对应的窗口函数。因此,如果需要马上更新窗口时,就可以考虑调用UpdateWindow函数

 

5> 程序运行过程中,消息回路不断 GetMessage,如果该消息为 WM_QUIT,  GetMessage会返回,退出循环,结束整个程序 

6> DispatchMessage 分派消息到窗口函数,窗口函数执行相关动作;

 

为什么点关闭的时候不直接发送 WM_QUIT ?而要绕一个大圈呢( WM_CLOSE -> WM_DESTORY -> WM_QUIT )这是因为操作系统与应该程序两者各施其职,彼此使用消息进行通信 :

          当用户关闭程序时,系统就会送出 WM_CLOSE 消息,通常程序的窗口函数不拦截此消息,由DefWindowProc 去处理它

          DefWindowProc 收到消息 WM_CLOSE 后,调用 DestoryWindow 把窗口清除,并送出WM_DESTROY  —— 此时界面没了

          WM_DESTROY 的标准反应是调用 PostQuitMessage ;

          PostQuitMessage 发出在 WM_QUIT ,当该消息被 GetMessage取得时,也就跳出了循环,程序结束——此时整个应用程序进程结束

          如果在处理 WM_DESTORY 时,不让程序调用 PostQuitMessage ,则会出现程序界面关闭,但应用程序进程没关闭的现象(主程序没退出消息循环)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值