《Windows程序设计》读书笔记之三

窗口和消息

                                                                                    见钱眼开 于 2005-3-21

      Windows中,我们创建的应用程序都包含有“窗口”这一对象,它在屏幕上显示为一块矩形区域,含有显示程序名称的标题栏、菜单,甚至还有工具栏、滚动条等,用户可以通过鼠标和键盘直接与它进行交互操作。

下图描述了一个典型窗口的构成部件:

   我们可以拖动标题栏(Title Bar)在屏幕上移动窗口;可以单击最大化按钮(Maximize button)使整个窗口充满整个屏幕;可以最小化按钮(Minimize button)使整个窗口缩为一个图标;可以单击关闭按钮(Close button)关闭窗口甚至应用程序;可以标题栏最左边的系统菜单(System menu)中执行前面这些选项;可以按住边框(Sizing border)改变窗口大小;可以选择菜单栏(Menu bar)的菜单项执行各种预定义操作;可以拖动滚动栏(Scroll bar)浏览客户区不同区域内容。

       我们调用MessageBox函数创建的对话框其实是一个功能有限的窗口。不光如此,对话框表面的按钮和其他类型按钮如单选钮、复选框、列表框、滚动条、文本框其实都是窗口,只不过通常我们习惯称之为“子窗口”或“控件窗口”或“子窗口控件”。

       所有窗口都是在“窗口类”的基础上创建的。“窗口类”是一个抽象概念,用以定义某一类型窗口对象的基本外观和行为。使用窗口类可以创建多个窗口基于同一个窗口类,并且都使用同一个窗口过程,都有基本一致的窗口外观。

       Windows中,一般使用一种叫做“匈牙利表示法”的变量命名约定,变量名以一个或多个小写字母开始,这些字符表示变量的数据类型。了解这点,有助于我们更好理解Windows API函数的参数含义。

       调用RegisterClass注册一个窗口类,该函数只需一个参数,一个指向类型为WNDCLASS的结构指针。结构原型声明如下:

       typedef struct {

    UINT style;             //类风格

    WNDPROC lpfnWndProc;    //窗口过程

    int cbClsExtra;         //额外类空间

    int cbWndExtra;         //额外窗口空间

    HINSTANCE hInstance;    //实例句柄

    HICON hIcon;            //图标

    HCURSOR hCursor;        //光标

    HBRUSH hbrBackground;   //背景画刷

    LPCTSTR lpszMenuName;   //菜单 

    LPCTSTR lpszClassName;  //类名称

} WNDCLASS, *PWNDCLASS;

    WNDCLASS结构中最重要的两个参数是第二个和最后一个。第二个参数(lpfnWndProc)是所有基于该类创建窗口的窗口过程地址。最后一个参数(lpszClassName)是窗口类的文本名。

    窗口类定义窗口的一般特征,可以在已定义窗口类基础上调用CreateWindow创建具有不同特征的新窗口。CreateWindow函数原型声明如下:

HWND CreateWindow(     
    LPCTSTR lpClassName,    //
类名

    LPCTSTR lpWindowName,   //窗口名称

    DWORD dwStyle,          //窗口风格

    int x,                  //左上角x坐标

    int y,                  //左上角y坐标

    int nWidth,             //宽度

    int nHeight,            //高度

    HWND hWndParent,        //父窗口句柄

    HMENU hMenu,            //菜单句柄

    HINSTANCE hInstance,    //程序实例句柄 

    LPVOID lpParam          //WM_CREATE消息结构中的参数值

);

    创建一般的应用程序主窗口,指定dwStyle风格为WS_OVERLAPEDWINDOW即可。创建一个“顶级”窗口时,hWndParent参数设置为NULLCreateWindow调用返回一个窗口句柄,每个创建的窗口都对应一个句柄。

    句柄在Windows中使用非常频繁。它是一个32位整数,代表一个对象。程序通过Windows 函数获取句柄,在其他Window函数中使用句柄,以引用它代表的对象。一般无需关注句柄实际值。

    调用CreateWindow只是创建了一个窗口,要想使创建窗口在屏幕上显示,必须调用ShowWindowUpdateWindow函数。这两个函数都需要使用CreateWindow返回的窗口句柄,ShowWindow使窗口显示在屏幕上,UpdateWindow使窗口客户区被绘制。

    有一点很让人迷惑,无论用户进行何种操作,窗口都会及时作出反应,这一切是如何实现的呢?

    原来用户每次操作之后,Windows都给程序发送了一条消息。这条消息描述了特定的内容。程序接收该消息后,就调用目标窗口关联的窗口过程进行处理,最后返回结果。程序创建的每一个窗口都关联一个窗口过程。窗口过程其实就是一个函数,它有固定原型,声明如下:

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

    当一个Windows程序执行后,Windows为该程序创建一个“消息队列”,这个队列用来存放发送给窗口的各种消息。在调用CreateWindow函数创建完窗口后,开始进入消息处理循环,将消息队列中的消息分发给目标窗口关联窗口过程。

    程序通过执行一块被称之为“消息循环”的代码从消息队列中取出消息:

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

    {

        TranslateMessage(&Msg);

        DispatchMessage(&Msg);

    }

    消息循环以GetMessage调用开始,它从消息队列取出一个消息,存放在一个名为MsgMSG结构中。MSG结构原型如下:

    typedef struct tagMSG

    {

        HWND    hwnd;       //目标窗口句柄

        UINT        message; //消息ID

        WPARAM  wParam;     //消息辅助参数

        LPARAM lParam;     //消息辅助参数

        DWORD       time;   //消息放入消息队列时间

        POINT       pt;     //消息放入消息队列时的鼠标坐标

    } MSG,*PMSG;

    只要从消息队列中取出消息的message值不为WM_QUIT0x0012),GetMessage就返回一个非0值。WM_QUIT消息使GetMessage返回0,导致消息循环的结束,进而结束应用程序。TranslateMessage主要进行一些键盘转换。DispatchMessage负责将消息传送给窗口过程,并调用窗口过程。在窗口过程调用结束之后,进行下一个GetMessage调用。

    窗口过程一般由系统本身调用。程序一般不需要调用窗口过程。通过SendMessage函数,程序也可以直接调用窗口过程。窗口过程中忽略的消息由DefWindowProc提供缺省处理。

    WM_PAINT消息在Windows中是个很重要的消息。当窗口客户区的部分或全部变得“无效”,以至于必须刷新,系统将发送这个消息给程序。以下几种情况将发送WM_PAINT消息:

1.  窗口最初创建时;

2.  窗口移动后或大小改变后;

3.  窗口隐藏后重新显示或被其他窗口遮掩的部分重新可见;

4.  调用InvalidateRectInvalidateRgn函数;

5.  调用ScrollWindowScrollDC函数滚动客户区;

下列情况一般不发送WM_PAINT消息:

1.  光标穿越客户区;

2.  图标拖过客户区;

3.  显示对话框;

4.  下拉菜单后释放;

对于WM_PAINT的处理几乎总是从一个BeginPaint调用开始:

Hdc = BeginPaint(hwnd,&ps);

而以一个EndPaint调用结束:

EndPaint(hwnd,&ps);

    BeginPaint调用中,如果客户区背景未被清除,则由系统使用注册窗口类的WNDCLASS结构的hBackGround参数中指定的画刷负责清除。BeginPaint调用使客户区有效。无法使用从BeginPaint返回的设备描述表句柄在客户区之外绘图。EndPaint释放设备描述表句柄,使之不再有效。

    Windows程序所做的一切都是响应发送给窗口过程的消息。

    如果用户单击“关闭”按钮,DefWindowProc在处理这一鼠标输入后,它向窗口过程发送一个WM_SYSCOMMAND消息。窗口过程又将该消息传送给DefWindowProc处理,之后DefWindowProc又给窗口过程发送一个WM_CLOSE消息。窗口过程又将该消息传送给DefWindowProc处理,之后DefWindowProc又给窗口过程发送一个WM_Destroy消息。窗口过程接收到该消息后,调用PostQuitMessage发送一个WM_QUIT到消息队列。下次调用GetMessage后返回0。最后程序结束。

    消息可分为“进队消息”和“不进队消息”。进队消息是由Windows放入程序消息队列,在程序的消息循环中,重新返回并分配给窗口;不进队消息是Windows直接调用窗口过程。

    一般进队消息都是用户输入的结果。以击键(如WM_KEYDOWNWM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)、鼠标击键(WM_LBUTTONDOWN)的形式给出。进队消息还包括时钟消息(WM_TIMER)、刷新消息(WM_PAINT)、退出消息(WM_QUIT)。

    不进队消息许多情况都来自调用特定的Windows函数。调用CreateWindow后发送一个WM_CREATE消息;调用ShowWindow后发送WM_SIZEWM_SHOWWINDOW消息;调用UpdateWindow发送WM_PAINT消息。

    应用程序中的消息处理必须以一种有序的同步的方式进行,无法并发执行。在一个窗口过程中处理某个消息时,程序不会被其他消息突然中断。必须处理完一个消息后,才能处理另一个消息。

       窗口过程可重入,就是说窗口过程可嵌套调用。

 

 

 

 

             

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值