窗口类别定义了窗口消息处理程序和依据此类别建立的窗口的其它特征。在建立窗口时,要定义一些该窗口所独有的特征.
在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。
在WinMain中为WNDCLASS定义一个结构,通常像这样:
WNDCLASS wndclass ;
然后,你就可以初始化该结构的10个字段,并呼叫RegisterClass。
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) 是依据这个类别来建立的所有窗口所使用的窗口消息处理程序的地址。在HELLOWIN.C中,这个是WndProc函数。最后一个字段是窗口类别的文字名称。程序写作者可以随意定义其名称。在只建立一个窗口的程序中,窗口类别名称通常设定为程序名称。
建立窗口
传递给RegisterClass函数的信息会在一个数据结构中设定好,而传递给CreateWindow函数的信息会在函数单独的参数中设定好。
显示窗口
在CreateWindow呼叫传回之后,Windows内部已经建立了这个窗口。这就是说,Windows已经配置了一块内存,用来保存在CreateWindow呼叫中指定窗口的全部信息跟一些其它信息,而Windows稍后就是依据窗口句柄找到这些信息的。
然而,光是这样子,窗口并不会出现在视讯显示器上。您还需要两个函数呼叫,一个是:
ShowWindow (hwnd, iCmdShow) ;
第一个参数是刚刚用CreateWindow建立的窗口句柄。第二个参数是作为参数传给WinMain的iCmdShow。它确定最初如何在
屏幕上显示窗口,是一般大小、最小化还是最大化。在开始菜单中安装程序时,使用者可能做出最佳选择。如果窗口按一般大小
显示,那么WinMain接收到后传递给ShowWindow的就是SW_SHOWNORMAL﹔如果窗口是最大化显示的,则为
SW_SHOWMAXIMIZED。而如果窗口只显示在工作列上,则是SW_SHOWMINNOACTIVE。
ShowWindow函数在显示器上显示窗口。如果ShowWindow的第二个参数是SW_SHOWNORMAL,则窗口的显示区域就会被
窗口类别中定义的背景画刷所覆盖。函数呼叫
UpdateWindow (hwnd) ;
会重画显示区域。它经由发送给窗口消息处理程序(即HELLOWIN.C中的WndProc函数)一个WM_PAINT消息做到这一点。
消息循环
呼叫UpdateWindow之后,窗口就出现在视讯显示器上。程序现在必须准备读入使用者用键盘和鼠标输入的数据。Windows为当前执行的每个Windows程序维护一个「消息队列」。在发生输入事件之后,Windows将事件转换为一个「消息」并将消息放入程序的消息队列中。
程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; }
msg变量是型态为MSG的结构。
消息循环以GetMessage呼叫开始,它从消息队列中取出一个消息:
GetMessage (&msg, NULL, 0, 0)
这一呼叫传给Windows一个指标,指向名为msg的MSG结构。第二、第三和第四个参数设定为NULL或者0,表示程序接收它自己建立的所有窗口的所有消息。Windows用从消息队列中取出的下一个消息来填充消息结构的各个字段,结构的各个字段包括:
- hwnd 接收消息的窗口句柄。在HELLOWIN程序中,这一参数与CreateWindow传回的hwnd值相同,因为这是该程序拥有的唯一窗口。
- message 消息标识符。这是一个数值,用以标识消息。对于每个消息,均有一个对应的标识符,这些标识符定义于Windows表头文件(其中大多数在WINUSER.H中),以前缀WM(「window message」,窗口消息)开头。例如,使用者将鼠标光标放在HELLOWIN显示区域之内,并按下鼠标左按钮,Windows就在消息队列中放入一个消息,该消息的message字段等于WM_LBUTTONDOWN。这是一个常数,其值为0x0201。
- wParam 一个32位的「message parameter(消息参数)」,其含义和数值根据消息的不同而不同。
- lParam 一个32位的消息参数,其值与消息有关。
- time 消息放入消息队列中的时间。
- pt 消息放入消息队列时的鼠标坐标。
只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012),GetMessage就传回一个非零值。WM_QUIT消息将导致GetMessage传回0。
叙述
TranslateMessage (&msg) ;
将msg结构传给Windows,进行一些键盘转换。(关于这一点,我们将在第六章中深入讨论。)
叙述
DispatchMessage (&msg) ;
又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。在HELLOWIN中,这个窗口消息处理程序就是WndProe函数。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。
窗口消息处理程序
以上我们所讨论的都是必要的负担:注册窗口类别,建立窗口,然后在屏幕上显示窗口,程序进入消息循环,然后不断从消息队列中取出消息来处理。
实际的动作发生在窗口消息处理程序中。窗口消息处理程序确定了在窗口的显示区域中显示些什么以及窗口怎样响应使用者输入。
在HELLOWIN中,窗口消息处理程序是命名为WndProc的函数。窗口消息处理程序可任意命名(只要求不和其它名字发生冲突)。一个Windows程序可以包含多个窗口消息处理程序。一个窗口消息处理程序总是与呼叫RegisterClass注册的特定窗口类别相关联。CreateWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口。
窗口消息处理程序总是定义为如下形式:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
注意,窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。第一个参数hwnd是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。对于与HELLOWIN相似的程序(只建立一个窗口),这个参数是程序所知道的唯一窗口句柄。如果程序是依据同一窗口类别(同时也是同一窗口消息处理程序)建立多个窗口,则hwnd标识接收消息的特定窗口。
第二个参数与MSG结构中的message字段相同,它是标识消息的数值。最后两个参数都是32位的消息参数,提供关于消息的更多信息。这些参数包含每个消息型态的详细信息。有时消息参数是两个存放在一起的16位值,而有时消息参数又是一个指向字符串或数据结构的指针。
程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。
处理消息
窗口消息处理程序所接受的每个消息均是用一个数值来标识的,也就是传给窗口消息处理程序的message参数。Windows表头文件WINUSER.H为每个消息参数定义以「WM」(窗口消息)为前缀开头的标识符。
一般来说,Windows程序写作者使用switch和case结构来确定窗口消息处理程序接收的是什么消息,以及如何适当地处理它。窗口消息处理程序在处理消息时,必须传回0。窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数。从DefWindowProc传回的值必须由窗口消息处理程序传回。
对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始:
hdc = BeginPaint (hwnd, &ps) ;
而以一个EndPaint呼叫结束:
EndPaint (hwnd, &ps) ;
在这两个呼叫中,第一个参数都是程序的窗口句柄,第二个参数是指向型态为PAINTSTRUCT的结构指针。PAINTSTRUCT结构
中包含一些窗口消息处理程序,可以用来更新显示区域的内容。
呼叫完BeginPaint之后,WndProc接着呼叫GetClientRect:
GetClientRect (hwnd, &rect) ;第一个参数是程序窗口的句柄。第二个参数是一个指标,指向一个RECT型态的rectangle结构。该结构有四个LONG字段,分别
为left、top、right和bottom。GetClientRect将这四个字段设定为窗口显示区域的尺寸。left和top字段通常设定为0,right和
bottom字段设定为显示区域的宽度和高度(像素点数)。
WndProc除了将该RECT结构指针作为DrawText的第四个参数传递外,不再对它做其它处理:
DrawText ( hdc, TEXT ("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;DrawText可以输出文字(正如其名字所表明的一样)。由于该函数要输出文字,第一个参数是从BeginPaint传回的设备内容
句柄,第二个参数是要输出的文字,第三个参数是 -1,指示字符串是以字节0终结的。
DrawText最后一个参数是一系列位旗标,它们均在WINUSER.H中定义(虽然由于其显示输出的效果,使得DrawText像一个
GDI函数呼叫,但它确实因为相当高级的画图功能而成为User模块的一部分。此函数在
/Platform SDK/Graphics and Multimedia Services/GDI/Fonts and Text中说明)。旗标指示了文字必须显示在一行上,
水平方向和垂直方向都位于第四个参数指定的矩形中央。因此,这个函数呼叫将让字符串「Hello, Windows 98!」显示在显示
区域的中央。
一旦显示区域变得无效(正如在改变大小时所发生的情况一样),WndProc就接收到一个新的WM_PAINT消息。WndProc通过呼叫GetClientRect取得变化后的窗口大小,并在新窗口的中央显示文字。