《windows程序设计(第五版)》——3 窗口和消息

    技术2024-09-28  66

    MessageBox函数建立一个窗口,但这只是一个功能有限的特殊窗口。消息窗口有一个带关闭按钮的标题列、一个选项图标、一行或多行文字,以及最多四个按钮。当然,必须选择Windows提供给您的图标与按钮。

    自己的窗口

    建立窗口很简单,只需呼叫CreateWindow函数即可。

     

    在对象导向的程序设计中,对象是程序与数据的组合。窗口是一种对象,其程序是窗口消息处理程序。数据是窗口消息处理程序保存的信息和Windows为每个窗口以及系统中那个窗口类别保存的信息。

    窗口消息处理程序处理给窗口发送消息。这些消息经常是告知窗口,使用者正使用键盘或者鼠标进行输入。这正是按键窗口知道它被「按下」的奥妙所在。在窗口大小改变,或者窗口表面需要重画时,由其它消息通知窗口。

    Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给窗口消息处理程序,不用放入消息队列中。

    如果您对这段Windows架构过于简略的描述将信将疑,就让我们去看看在实际的程序中,窗口、窗口类别、窗口消息处理程序、消息队列、消息循环和窗口消息是如何相互配合的。这或许会对您有些帮助。

     

    #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = 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 instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE: PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ; return 0 ; case WM_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 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }

     

     

    程序建立一个普通的应用程序窗口,如图3-1所示。在窗口显示区域的中央显示「Hello, Windows 98!」。如果安装了声卡,那么您还可以听到相应的朗读声音。

     

    图3-1 HELLOWIN窗口

    提醒您注意:WINMM.LIB(Windows multimedia-Windows多媒体)。您这样做是因为HELLOWIN将使用多媒体功能呼叫,而内定的项目中又不包括多媒体链接库文件。不然连结程序报告了错误信息,表明PlaySound函数不可用。

     


    通盘考量

    实际上,每一个Windows程序代码中都包括HELLOWIN.C程序的大部分。没人能真正记住此程序的全部写法;通常,Windows程序写作者在开始写一个新程序时总是会复制一个现有的程序,然后再做相应的修改。您可以按此习惯自由使用本书附带光盘中的程序。


    HELLOWIN.C也有一个WinMain函数,但它还有另外一个函数,名为WndProc。这就是窗口消息处理程序。注意,在HELLOWIN.C中没有呼叫WndProc的程序代码。当然,在WinMain中有对WndProc的参考,而这就是该函数要在程序开头附近声明的原因。

     

    Windows函数呼叫

    HELLOWIN至少呼叫了18个Windows函数。

    LoadIcon 加载图标供程序使用。  LoadCursor 加载鼠标光标供程序使用。  GetStockObject 取得一个图形对象(在这个例子中,是取得绘制窗口背景的画刷对象)。  RegisterClass 为程序窗口注册窗口类别。  MessageBox 显示消息框。  CreateWindow 根据窗口类别建立一个窗口。  ShowWindow 在屏幕上显示窗口。  UpdateWindow 指示窗口自我更新。  GetMessage 从消息队列中取得消息。  TranslateMessage 转译某些键盘消息。  DispatchMessage 将消息发送给窗口消息处理程序。  PlaySound 播放一个声音文件。  BeginPaint 开始绘制窗口。  GetClientRect 取得窗口显示区域的大小。  DrawText 显示字符串。  EndPaint 结束绘制窗口。  PostQuitMessage 在消息队列中插入一个「退出程序」消息。  DefWindowProc 执行内定的消息处理。  其中绝大多数声明在WINUSER.H中

    大写字母标识符

     

     

    这些是简单的数值常数。前缀指示该常数所属的类别,如表3-1所示。

    表3-1

     

    前缀

    类别

    CS

    窗口类别样式

    CW

    建立窗口

    DT

    绘制文字

    IDI

    图示ID

    IDC

    游标ID

    MB

    消息框

    SND

    声音

    WM

    窗口消息

    WS

    窗口样式

    奉劝程序写作者不要费力气去记忆Windows程序设计中的数值常数。实际上,Windows中使用的每个数值常数在表头文件中均有相应的标识符定义。

    新的数据型态

    HELLOWIN.C中的其它标识符是新的数据型态,也在Windows表头文件中使用typedef叙述或者#define叙述加以定义了

     

     

     

    1 用于WndProc的第二个参数的UINT数据型态只是一个unsigned int (无正负号整数),在Windows 98中,这是一个32位的值。

    2 用于WinMain的第三个参数的PSTR数据型态是指向一个字符串的指针,即是一个char

    3 WndProc的第三和第四个参数分别被定义为WPARAM和LPARAM,这些名字的来源有点历史背景:当Windows还是16位系统时,WndProc的第三个参数被定义为一个WORD,这是一个16位的 无正负号短(unsigned short)整数,

    4 而第四个参数被定义为一个LONG,这是一个32位有正负号长整数,从而导致了文字「PARAM」前面加上了前置前缀「W」和「L」。

    在32位的Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG(这就是C中的long整数型态),因此窗口消息处理程序的这两个参数都是32位的值。这也许有点奇怪,因为WORD数据型态在Windows98中仍然被定义为一种16位的 无正负号整数,因此「PARAM」前的「W」就有点误用了

         5  WndProc函数传回一个型态为LRESULT的值,该值简单地被定义为一个LONG。

         6  WinMain函数被指定了一个WINAPI型态(在表头文件中定义的所有Windows函数都被指定这种型态),而WndProc函数被指定一个CALLBACK型态。这两个标识符都被定义为_stdcall,表示在Windows本身和使用者的应用程序之间发生的函数呼叫的呼叫参数传递方式。

    7  HELLOWIN还使用了Windows表头文件中定义的四种数据结构(我们将在本章稍后加以讨论)。

     

    结构

    含义

    MSG

    消息结构

    WNDCLASS

    窗口类别结构

    PAINTSTRUCT

    绘图结构

    RECT

    矩形结构

    前面两个数据结构在WinMain中使用,分别定义了两个名为msg和wndclass的结构,后面两个数据结构在WndProc中使用,分别定义了ps和rect结构。

    8 句柄

    最后,还有三个大写标识符(见表3-3),用于不同型态的「句柄」:

    表3-3

     

    标识符

    含义

    HINSTANCE

    执行实体(程序自身)句柄

    HWND

    窗口句柄

    HDC

    设备内容句柄

    句柄在Windows中使用非常频繁。在本章结束之前,我们将遇到HICON(图标句柄)、HCURSOR(鼠标光标句柄)和HBRUSH(画刷句柄)。

    句柄是一个(通常为32位的)整数,它代表一个对象。Windows中的句柄类似传统C或者MS-DOS程序设计中使用的文件句柄。程序几乎总是通过呼叫Windows函数取得句柄。程序在其它Windows函数中使用这个句柄,以使用它代表的对象。代号的实际值对程序来说是无关紧要的。但是,向您的程序提供代号的Windows模块知道如何利用它来使用相对应的对象。

    匈牙利表示法

    表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

    指标

      szCmdLine中的sz代表「以0结尾的字符串」。在hInstance和hPrevInstance中的h前缀表示「句柄」;在iCmdShow中的i前缀表示「整数」。WndProc的后两个参数也使用匈牙利表示法。正如我在前面已经解释过的,尽管wParam应该更适当地被命名为uiParam(代表「无正负号整数」),但是因为这两个参数是使用数据型态WPARAM和LPARAM定义的,因此保留它们传统的名字   


     

     


    Windows数据类型

    。这些数据类型是Windows特有的。在SDK的相关头文件中有定义。

    在众多的Windows数据类型中,最常用的有DWORDHANDLELPTSTRWORDBYTE

    CHAR等。在Windows系统中,DWORD用于表示无符号整型的数据,意为double word32位。

    在一般情况下BYTE8位的,而WORD16位,DWORD就是32位的。

    Windows系统的应用程序中还具有一个特有的数据类型-HANDLE,通常HANDLE类型的变量用于唯一标识一个对象,如窗口、控件、文件等,Windows平台中存在众多这样的对象,对象是程序操作的目标。HANDLE也是一个32位的数据类型。

     

    ,分别是LPSTRCHARDWORDINT

    LPSTR类型的数据是字符串,也就是字符指针,CHAR是字符,DWORD32位的无符号

    整数,INT32位有符号整数

    (1)       BYTE8位的      0xFF

    (2)       WORD16       0xFFFF    

    (3)       DWORD就是32位的  0xFFFF FFFF   0x1(4294967295>1) 无符号的

    (4)INT32位有符号整数  0xFFFF FFFF 0x1(-4294967295<-1)

     

    (5) /定义字符串

    LPSTR szString = "Windows data type, string.";

    (typedef CHAR *LPSTR, *PSTR;)

    指针类型的命令方式一般是在其指向的数据类型前加LPP,比如指向DWORD的指针类型为LPDWORDPDWORD

     

    (6) //定义字符数组

        CHAR lpString[120];//要大于szString的长度

    typedef char CHAR;

    (7) 各种句柄类型的命令方式一般都是在对象名前加HWindows系统中有很多对象,所有表示一个对象的数据类型都是句柄,每一种对象都对应着一种句柄类型,比如与位图( BITMAP)对应的句柄类型为HBITMAP,与菜单(MENU)对应的句柄类型为HMENU,与窗口(WINDOW)对应的句柄类型为HWND

    HANDLE文件句柄类型

    typedef void *HANDLE;

    HWND 窗口句柄类型

    MENU

     

    无符号类型一般是以U开头,比如INT是符号类型,UINT是无符号类型,LONG是符号类型ULONG是无符号类型等

     

    (8) LPVOID是无类型指针,相当于void *

    (9)LPDWORD是指向DOWRD类型数据的指针类型。

     

     

     

     

     

     

    Windows中的数据结构

     

    Windows系统中使用全大写来命名结构体、共用体,并使用_来分隔单词,在结构名加LPP表示指向数 据结构的指针


     


     

     

    注册窗口类别

    窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。

    不同窗口可以依照同一种窗口类别建立。例如,Windows中的所有按钮窗口-包括按键、复选框,以及单选按钮-都是依据同一种窗口类别建立的。窗口类别定义了窗口消息处理程序和依据此类别建立的窗口的其它特征。在建立窗口时,要定义一些该窗口所独有的特征。

    然后,你就可以初始化该结构的10个字段,并呼叫RegisterClass

    在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。此结构包括两个指向字符串的字段,因此结构在WINUSER.H表头文件中定义了两种不同的方式,第一个是ASCII版的WNDCLASSA:

     

    typedef struct tagWNDCLASSA { UINT style ; WNDPROC lpfnWndProc ; int cbClsExtra ; int cbWndExtra ; HINSTANCE hInstance ; HICON hIcon ; HCURSOR hCursor ; HBRUSH hbrBackground ; LPCSTR lpszMenuName ; LPCSTR lpszClassName ; } WNDCLASSA, * PWNDCLASSA, NEAR * NPWNDCLASSA, FAR * LPWNDCLASSA ;

    在这里提示一下数据型态和匈牙利表示法:其中的lpfn前缀代表「指向函数的长指标」。(在Win32 API中,长指标和短指标(或者近程指标)没有区别。这只是16位Windows的遗物。)cb前缀代表「字节数」而且通常作为一个常数来表示一个字节的大小。h前缀是一个句柄,而hbr前缀代表「一个画刷的代号」。lpsz前缀代表「指向以0结尾字符串的指针」。

    Unicode版的结构定义如下:

    typedef struct tagWNDCLASSW { UINT style ; WNDPROC lpfnWndProc ; int cbClsExtra ; int cbWndExtra ; HINSTANCE hInstance ; HICON hIcon ; HCURSOR hCursor ; HBRUSH hbrBackground ; LPCWSTR lpszMenuName ; LPCWSTR lpszClassName ; } WNDCLASSW, * PWNDCLASSW, NEAR * NPWNDCLASSW, FAR * LPWNDCLASSW ;

    与前者唯一的区别在于最后两个字段定义为指向宽字符串常数,而不是指向ASCII字符串常数。

     

     UNICODE

    1   WINMM.LIB

                                

     #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { static TCHAR szAppName[]=TEXT("双"); HWND hwnd; MSG msg; WNDCLASSW wndclass; wndclass.style=CS_HREDRAW|CS_VREDRAW; wndclass.lpfnWndProc=WndProc; wndclass.cbWndExtra=0; wndclass.cbClsExtra=0; wndclass.hInstance=hInstance; wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName=NULL; wndclass.lpszClassName=szAppName; if (!RegisterClassW(&wndclass)) { MessageBox(NULL,TEXT("er"),szAppName,MB_ICONERROR); return 0; } hwnd=CreateWindowW(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 instance handle NULL) ; // creation parameters ShowWindow(hwnd, nShowCmd) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE : PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ; //处理WM_CREATE消息 return 0 ; case WM_PAINT : //处理WM_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 ; case WM_DESTROY : //处理WM_DESTROY消息 PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }

    最新回复(0)