window程序设计Word文档格式.docx
《window程序设计Word文档格式.docx》由会员分享,可在线阅读,更多相关《window程序设计Word文档格式.docx(38页珍藏版)》请在冰豆网上搜索。
由于应用程序能改变其显示的样子,所以它也「知道」窗口大小改变了。
应用程序是如何知道使用者改变了窗口的大小的呢?
由于程序写作者习惯了往常的文字模式程序,操作系统没有设置将此类消息通知给使用者的机制。
问题的关键在于理解Windows所使用的架构。
当使用者改变窗口的大小时,Window给程序发送一个消息指出新窗口的大小。
然后程序就可以调整窗口中的内容,以响应大小的变化。
「Windows给程序发送消息。
」我们希望读者不要对这句话视而不见。
它到底表达了什么意思呢?
我们在这里讨论的是程序代码,而不是一个电子邮件系统。
操作系统怎么给程序发送消息呢?
其实,所谓「Windows给程序发送消息」,是指Windows呼叫程序中的一个函数,该函数的参数描述了这个特定消息。
这种位于Windows程序中的函数称为「窗口消息处理程序」。
无疑,读者对程序呼叫操作系统的做法是很熟悉的。
例如,程序在打开磁盘文件时就要使用有关的系统呼叫。
读者所不习惯的,可能是操作系统呼叫程序,而这正是Windows对象导向架构的基础。
程序建立的每一个窗口都有相关的窗口消息处理程序。
这个窗口消息处理程序是一个函数,既可以在程序中,也可以在动态链接库中。
Windows通过呼叫窗口消息处理程序来给窗口发送消息。
窗口消息处理程序根据此消息进行处理,然后将控制传回给Windows。
更确切地说,窗口通常是在「窗口类别」的基础上建立的。
窗口类别标识了处理窗口消息的窗口消息处理程序。
使用窗口类别使多个窗口能够属于同一个窗口类别,并使用同一个窗口消息处理程序。
例如,所有Windows程序中的所有按钮均依据同一个窗口类别。
这个窗口类别与一个处理所有按钮消息的窗口消息处理程序(位于Windows的动态链接库中)联结。
在对象导向的程序设计中,对象是程序与数据的组合。
窗口是一种对象,其程序是窗口消息处理程序。
数据是窗口消息处理程序保存的信息和Windows为每个窗口以及系统中那个窗口类别保存的信息。
窗口消息处理程序处理给窗口发送消息。
这些消息经常是告知窗口,使用者正使用键盘或者鼠标进行输入。
这正是按键窗口知道它被「按下」的奥妙所在。
在窗口大小改变,或者窗口表面需要重画时,由其它消息通知窗口。
Windows程序开始执行后,Windows为该程序建立一个「消息队列」。
这个消息队列用来存放该程序可能建立的各种不同窗口的消息。
程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。
有些消息直接发送给窗口消息处理程序,不用放入消息队列中。
如果您对这段Windows架构过于简略的描述将信将疑,就让我们去看看在实际的程序中,窗口、窗口类别、窗口消息处理程序、消息队列、消息循环和窗口消息是如何相互配合的。
这或许会对您有些帮助。
HELLOWIN程序
建立一个窗口首先需要注册一个窗口类别,那需要一个窗口消息处理程序来处理窗口消息。
处理窗口消息对每个Windows程序都带来了些负担。
程序3-1所示的HELLOWIN程序中整个做的事情差不多就是料理这些事情。
程序3-1HELLOWIN
HELLOWIN.C
/*------------------------------------------------------------------------
HELLOWIN.C--Displays"
Hello,Windows98!
"
inclientarea
(c)CharlesPetzold,1998
-----------------------------------------------------------------------*/
#include<
windows.h>
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);
intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,
PSTRszCmdLine,intiCmdShow)
{
staticTCHARszAppName[]=TEXT("
HelloWin"
);
HWNDhwnd;
MSGmsg;
WNDCLASwndclass;
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.lpszMenuNam=NULL;
wndclass.lpszClassName=szAppName;
if(!
RegisterClass(&
wndclass))
{
MessageBox(NULL,TEXT("
ThisprogramrequiresWindowsNT!
),
szAppName,MB_ICONERROR);
return0;
}
hwnd=CreateWindow(szAppName,//windowclassname
TEXT("
TheHelloProgram"
),//windowcaption
WS_OVERLAPPEDWINDOW,//windowstyle
CW_USEDEFAULT,//initialxposition
CW_USEDEFAULT,//initialyposition
CW_USEDEFAULT,//initialxsize
CW_USEDEFAULT,//initialysize
NULL,//parentwindowhandle
NULL,//windowmenuhandle
hInstance,//programinstancehandle
NULL);
//creationparameters
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&
msg,NULL,0,0))
TranslateMessage(&
msg);
DispatchMessage(&
returnmsg.wParam;
}
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)
HDChdc;
PAINTSTRUCTps;
RECTrect;
switch(message)
caseWM_CREATE:
PlaySound(TEXT("
hellowin.wav"
),NULL,SND_FILENAME|SND_ASYNC);
caseWM_PAINT:
hdc=BeginPaint(hwnd,&
ps);
GetClientRect(hwnd,&
rect);
DrawText(hdc,TEXT("
),-1,&
rect,
DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hwnd,&
caseWM_DESTROY:
PostQuitMessage(0);
returnDefWindowProc(hwnd,message,wParam,lParam);
程序建立一个普通的应用程序窗口,如图3-1所示。
在窗口显示区域的中央显示「Hello,Windows98!
」。
如果安装了声卡,那么您还可以听到相应的朗读声音。
图3-1HELLOWIN窗口
提醒您注意:
如果您使用MicrosoftVisualC++为此程序建立新项目,那么您得加上连结程序所需的链接库文件。
从Project菜单选择Setting选项,然后选取Link页面标签。
从Category清单方块中选择General,然后在Object/LibraryModules文字方块添加WINMM.LIB(Windowsmultimedia-Windows多媒体)。
您这样做是因为HELLOWIN将使用多媒体功能呼叫,而内定的项目中又不包括多媒体链接库文件。
不然连结程序报告了错误信息,表明PlaySound函数不可用。
HELLOWIN将存取文件HELLOWIN.WAV,该文件在本书所附光盘的HELLOWIN目录中。
执行HELLOWIN.EXE时,内定的目录必须是HELLOWIN。
在VisualC++中执行此程序时,虽然执行文件会产生在HELLOWIN的RELEASE或DEBUG子目录中,但执行程序的目录还是必须在HELLOWIN中。
通盘考量
实际上,每一个Windows程序代码中都包括HELLOWIN.C程序的大部分。
没人能真正记住此程序的全部写法;
通常,Windows程序写作者在开始写一个新程序时总是会复制一个现有的程序,然后再做相应的修改。
您可以按此习惯自由使用本书附带光盘中的程序。
上面提到,HELLOWIN将在其窗口的中央显示字符串。
这种说法不是完全正确的。
文字实际显示在程序显示区域的中央,它在图3-1中是标题列和边界范围内的大片白色区域。
这区别对我们来说很重要;
显示区域就是程序自由绘图并且向使用者显示输出结果的窗口区域。
如果您认真思考一下,将会发现虽然只有80行程序代码,这个窗口却令人惊讶地具有许多功能。
您可以用鼠标按住标题列,在屏幕上移动窗口;
可以按住大小边框,改变窗口的大小。
在窗口大小改变时,程序自动地将「Hello,Windows98!
」字符串重新定位在显示区域的中央。
您可以按最大化按钮,放大HELLOWIN以充满整个屏幕;
也可以按最小化按钮,将程序缩小成一个图示。
您可以在系统菜单中执行所有选项(就是按下在标题列最左端的小图示);
也可以从系统菜单中选择Close选项,或者单击标题列最右端的关闭按钮,或者双击标题列最左端的图标,来关闭窗口以终止程序的执行。
我们将在本章的余下部分对此程序作一详细的检查。
当然,我们首先要从整体上看一下。
与前两章中的范例程序一样,HELLOWIN.C也有一个WinMain函数,但它还有另外一个函数,名为WndProc。
这就是窗口消息处理程序。
注意,在HELLOWIN.C中没有呼叫WndProc的程序代码。
当然,在WinMain中有对WndProc的参考,而这就是该函数要在程序开头附近声明的原因。
Windows函数呼叫
HELLOWIN至少呼叫了18个Windows函数。
下面以它们在HELLOWIN中出现的次序列出这些函数以及各自的简明描述:
∙LoadIcon加载图标供程序使用。
∙
∙
∙LoadCursor加载鼠标光标供程序使用。
∙GetStockObject取得一个图形对象(在这个例子中,是取得绘制窗口背景的画刷对象)。
∙RegisterClass为程序窗口注册窗口类别。
∙MessageBox显示消息框。
∙CreateWindow根据窗口类别建立一个窗口。
∙ShowWindow在屏幕上显示窗口。
∙UpdateWindow指示窗口自我更新。
∙GetMessage从消息队列中取得消息。
∙TranslateMessage转译某些键盘消息。
∙DispatchMessage将消息发送给窗口消息处理程序。
∙PlaySound播放一个声音文件。
∙BeginPaint开始绘制窗口。
∙GetClientRect取得窗口显示区域的大小。
∙DrawText显示字符串。
∙EndPaint结束绘制窗口。
∙PostQuitMessage在消息队列中插入一个「退出程序」消息。
∙DefWindowProc执行内定的消息处理。
这些函数均在PlatformSDK文件中说明,并在不同的表头文件中声明,其中绝大多数声明在WINUSER.H中。
大写字母标识符
读者可能注意到,HELLOWIN.C中有几个大写的标识符,这些标识符是在Windows表头文件中定义的。
有些标识符含有两个字母或者三个字母的前缀,这些前缀后头接着一个底线:
这些是简单的数值常数。
前缀指示该常数所属的类别,如表3-1所示。
表3-1
前缀
类别
CS
窗口类别样式
CW
建立窗口
DT
绘制文字
IDI
图示ID
IDC
游标ID
MB
消息框
SND
声音
WM
窗口消息
WS
窗口样式
奉劝程序写作者不要费力气去记忆Windows程序设计中的数值常数。
实际上,Windows中使用的每个数值常数在表头文件中均有相应的标识符定义。
新的数据型态
HELLOWIN.C中的其它标识符是新的数据型态,也在Windows表头文件中使用typedef叙述或者#define叙述加以定义了。
最初是为了便于将Windows程序从原来的16位系统上移植到未来的使用32位(或者其它)技术的操作系统上。
这种作法并不如当时每个人想象的那样顺利,但是这种概念基本上是正确的。
有时这些新的数据型态只是为了方便缩写。
例如,用于WndProc的第二个参数的UINT数据型态只是一个unsignedint(无正负号整数),在Windows98中,这是一个32位的值。
用于WinMain的第三个参数的PSTR数据型态是指向一个字符串的指针,即是一个char*。
其它数据型态的含义不太明显。
例如,WndProc的第三和第四个参数分别被定义为WPARAM和LPARAM,这些名字的来源有点历史背景:
当Windows还是16位系统时,WndProc的第三个参数被定义为一个WORD,这是一个16位的无正负号短(unsignedshort)整数,而第四个参数被定义为一个LONG,这是一个32位有正负号长整数,从而导致了文字「PARAM」前面加上了前置前缀「W」和「L」。
当然,在32位的Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG(这就是C中的long整数型态),因此窗口消息处理程序的这两个参数都是32位的值。
这也许有点奇怪,因为WORD数据型态在Windows98中仍然被定义为一种16位的无正负号整数,因此「PARAM」前的「W」就有点误用了。
WndProc函数传回一个型态为LRESULT的值,该值简单地被定义为一个LONG。
WinMain函数被指定了一个WINAPI型态(在表头文件中定义的所有Windows函数都被指定这种型态),而WndProc函数被指定一个CALLBACK型态。
这两个标识符都被定义为_stdcall,表示在Windows本身和使用者的应用程序之间发生的函数呼叫的呼叫参数传递方式。
HELLOWIN还使用了Windows表头文件中定义的四种数据结构(我们将在本章稍后加以讨论)。
这些数据结构如表3-2所示。
表3-2
结构
含义
MSG
消息结构
WNDCLASS
窗口类别结构
PAINTSTRUCT
绘图结构
RECT
矩形结构
前面两个数据结构在WinMain中使用,分别定义了两个名为msg和wndclass的结构,后面两个数据结构在WndProc中使用,分别定义了ps和rect结构。
句柄简介
最后,还有三个大写标识符(见表3-3),用于不同型态的「句柄」:
表3-3
标识符
HINSTANCE
执行实体(程序自身)句柄
HWND
窗口句柄
HDC
设备内容句柄
句柄在Windows中使用非常频繁。
在本章结束之前,我们将遇到HICON(图标句柄)、HCURSOR(鼠标光标句柄)和HBRUSH(画刷句柄)。
句柄是一个(通常为32位的)整数,它代表一个对象。
Windows中的句柄类似传统C或者MS-DOS程序设计中使用的文件句柄。
程序几乎总是通过呼叫Windows函数取得句柄。
程序在其它Windows函数中使用这个句柄,以使用它代表的对象。
代号的实际值对程序来说是无关紧要的。
但是,向您的程序提供代号的Windows模块知道如何利用它来使用相对应的对象。
匈牙利表示法
读者可能注意到,HELLOWIN.C中有一些变量的名字显得很古怪。
如szCmdLine,它是传递给WinMain的参数。
许多Windows程序写作者使用一种叫做「匈牙利表示法」的变量命名通则。
这是为了纪念传奇性的Microsoft程序写作者CharlesSimonyi。
非常简单,变量名以一个或者多个小写字母开始,这些字母表示变量的数据型态。
例如,szCmdLine中的sz代表「以0结尾的字符串」。
在hInstance和hPrevInstance中的h前缀表示「句柄」;
在iCmdShow中的i前缀表示「整数」。
WndProc的后两个参数也使用匈牙利表示法。
正如我在前面已经解释过的,尽管wParam应该更适当地被命名为uiParam(代表「无正负号整数」),但是因为这两个参数是使用数据型态WPARAM和LPARAM定义的,因此保留它们传统的名字。
在命名结构变量时,可以用结构名(或者结构名的一种缩写)的小写作为变量名的前缀,或者用作整个变量名。
例如,在HELLOWIN.C的WinMain函数中,msg变量是MSG型态的结构;
wndclass是WNDCLASSEX型态的一个结构。
在WndPmc函数中,ps是一个PAINTSTRUCT结构,rect是一个RECT结构。
匈牙利表示法能够帮助程序写作者及早发现并避免程序中的错误。
由于变量名既描述了变量的作用,又描述了其数据型态,就比较容易避免产生数据型态不合的错误。
表3-4列出了在本书中经常用到的变量前缀。
表3-4
数据型态
c
char或WCHAR或TCHAR
by
BYTE(无正负号字符)
n
short
i
int
x,y
int分别用作x坐标和y坐标
cx,cy
int分别用作x长度和y长度;
C代表「计数器」
b或f
BOOL(int);
f代表「旗标」
w
WORD(无正负号短整数)
l
LONG(长整数)
dw
DWORD(无正负号长整数)
fn
function(函数)
s
string(字符串)
sz
以字节值0结尾的字符串
h
句柄
p
指标
注册窗口类别
窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。
不同窗口可以依照同一