王朝网络
分享
 
 
 

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

王朝system·作者佚名  2006-01-31
宽屏版  字体: |||超大  

窗口和消息

见钱眼开 于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参数设置为NULL。CreateWindow调用返回一个窗口句柄,每个创建的窗口都对应一个句柄。

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

调用CreateWindow只是创建了一个窗口,要想使创建窗口在屏幕上显示,必须调用ShowWindow和UpdateWindow函数。这两个函数都需要使用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调用开始,它从消息队列取出一个消息,存放在一个名为Msg的MSG结构中。MSG结构原型如下:

typedef struct tagMSG

{

HWND hwnd; //目标窗口句柄

UINT message; //消息ID

WPARAM wParam; //消息辅助参数

LPARAM lParam; //消息辅助参数

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

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

} MSG,*PMSG;

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

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

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

1. 窗口最初创建时;

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

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

4. 调用InvalidateRect、InvalidateRgn函数;

5. 调用ScrollWindow或ScrollDC函数滚动客户区;

下列情况一般不发送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_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)、鼠标击键(WM_LBUTTONDOWN)的形式给出。进队消息还包括时钟消息(WM_TIMER)、刷新消息(WM_PAINT)、退出消息(WM_QUIT)。

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

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有