Windows程序设计03:创建窗口类
窗口是位于屏幕中的一个矩形区域,它用于接收用户的输入,然后以文本或图形的形式显示输出。
那么如何来创建一个窗口呢?可以通过以下五个步骤来完成。
第一,创建窗口类,用于指定窗口的宏观特性。
第二,调用RegisterClass函数注册窗口类。
第三,调用CreateWindow函数创建窗口,用于指定窗口的细节式样。
第四,调用ShowWindow函数显示窗口,调用UpdateWindow函数更新窗口。
第五,消息循环,处理窗口过程函数。
在本篇博文中,将详细介绍如何来创建一个窗口类,也就是上述五个步骤中的第一步,其他几步的实现方法将在后续的博文中一一介绍。
1.窗口类
当应用程序在创建窗口时,首先需要注册一个窗口类,用于指定窗口的宏观特性。窗口类为窗口定义了窗口风格、窗口过程、显示和绘制窗口所需要的信息等。
在winuser.h头文件中定义了窗口类的两种不同形式(ASCII版本的WNDCLASS和Unicode版本的WNDCLASS),如下:
1 typedef struct tagWNDCLASSA { 2 UINT style; 3 WNDPROC lpfnWndProc; 4 int cbClsExtra; 5 int cbWndExtra; 6 HINSTANCE hInstance; 7 HICON hIcon; 8 HCURSOR hCursor; 9 HBRUSH hbrBackground; 10 LPCSTR lpszMenuName; 11 LPCSTR lpszClassName; 12 } WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA; 13 typedef struct tagWNDCLASSW { 14 UINT style; 15 WNDPROC lpfnWndProc; 16 int cbClsExtra; 17 int cbWndExtra; 18 HINSTANCE hInstance; 19 HICON hIcon; 20 HCURSOR hCursor; 21 HBRUSH hbrBackground; 22 LPCWSTR lpszMenuName; 23 LPCWSTR lpszClassName; 24 } WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW; 25 #ifdef UNICODE 26 typedef WNDCLASSW WNDCLASS; 27 typedef PWNDCLASSW PWNDCLASS; 28 typedef NPWNDCLASSW NPWNDCLASS; 29 typedef LPWNDCLASSW LPWNDCLASS; 30 #else 31 typedef WNDCLASSA WNDCLASS; 32 typedef PWNDCLASSA PWNDCLASS; 33 typedef NPWNDCLASSA NPWNDCLASS; 34 typedef LPWNDCLASSA LPWNDCLASS; 35 #endif // UNICODE
这两个版本的唯一区别在于最后两个结构体数据成员的类型不一样。在ASCII版本中是指向ASCII字符的常量字符串,在Unicode版本中则是指向宽字符的常量字符串。这样做的目的是使程序更容易兼容和扩展,在Charles Petzold的《Windows程序设计》第二章Unicode简介中对这部分内容做了详细的介绍,这里就不再赘述了。
下面以ASCII版本的WNDCLASS为例对窗口类结构体做一个简单的说明。
1.1定义
在WinMain中,我们通常用如下形式来定义一个WNDCLASS类型的结构体。
WNDCLASS wndclass;
然后对该结构体的10个数据成员进行初始化。
1.2类风格(style)
WNDCLASS结构体中的数据成员style用于描述类风格。该数据成员可以是“Class styles”的任意组合。“Class styles”在winuser.h头文件中定义如下:
1 /* 2 * Class styles 3 */ 4 #define CS_VREDRAW 0x0001 //窗口垂直变化,重新绘制窗口 5 #define CS_HREDRAW 0x0002 //窗口水平变化,重新绘制窗口 6 #define CS_DBLCLKS 0x0008 //窗口允许两次连续点击 7 #define CS_OWNDC 0x0020 //每个窗口拥有自己的DC 8 #define CS_CLASSDC 0x0040 //所有该类型窗口使用同一DC 9 #define CS_PARENTDC 0x0080 //使用父窗口的DC 10 #define CS_NOCLOSE 0x0200 //系统菜单中没有Close菜单项 11 #define CS_SAVEBITS 0x0800 //保存窗口覆盖区 12 #define CS_BYTEALIGNCLIENT 0x1000 //窗口对齐方式,以8的倍数对齐 13 #define CS_BYTEALIGNWINDOW 0x2000 //窗口客户区对齐方式,以8的倍数对齐 14 #define CS_GLOBALCLASS 0x4000 //表明该窗口类是应用程序的全局类 15 16 #define CS_IME 0x00010000 17 #if(_WIN32_WINNT >= 0x0501) 18 #define CS_DROPSHADOW 0x00020000 //窗口阴影 19 #endif /* _WIN32_WINNT >= 0x0501 */
以这种方式定义的标识符称为“位标记”,因为每个标识符都只能影响复合值中的一位,所以可以通过按位或运算(|)进行组合应用。这些类风格中,只有少数会被经常使用。其中,CS_VREDRAW和CS_HREDRAW组合使用,可以指定无论何时窗口的水平尺寸或垂直尺寸被改变,所有基于该窗口类的窗口都将被重新绘制。这样就可以保证绘制在窗口某一位置的文本字符串(或图片)在窗口尺寸改变后仍能保持其相对位置。
1.3窗口过程(lpfnWndProc)
WNDCLASS结构体中的数据成员lpfnWndProc用于指定该窗口类的窗口过程。窗口过程用于处理传递给所有基于该窗口类创建的窗口的所有消息。关于窗口过程的使用方法将在下一篇博文中详细介绍。
1.4预留空间(cbClsExtra、cbWndExtra)
WNDCLASS结构体中的数据成员cbClsExtra用于在类结构中预留一些额外的空间。
WNDCLASS结构体中的数据成员cbWndExtra用于在Windows内部维护的窗口结构中预留一些额外的空间。
1.5应用程序实例句柄(hInstance)
WNDCLASS结构体中的数据成员hInstance用于指定应用程序的实例句柄。可以直接使用WinMain函数的第一个参数对其赋值即可。
1.6窗口图标句柄(hIcon)
WNDCLASS结构体中的数据成员hIcon用于指定应用程序的图标式样。当应用程序程序运行时,图标会出现在Window任务栏和程序窗口的标题栏左侧。
图标是一幅位图格式的小图。可以创建自定义的图标,也可以使用系统预定义的图标。可以通过调用LoadIcon函数来获取系统预定义的图标句柄。
在winuser.h头文件中定义了LoadIcon函数的原型如下:
1 WINUSERAPI 2 HICON 3 WINAPI 4 LoadIconA( 5 __in_opt HINSTANCE hInstance, 6 __in LPCSTR lpIconName); 7 WINUSERAPI 8 HICON 9 WINAPI 10 LoadIconW( 11 __in_opt HINSTANCE hInstance, 12 __in LPCWSTR lpIconName); 13 #ifdef UNICODE 14 #define LoadIcon LoadIconW 15 #else 16 #define LoadIcon LoadIconA 17 #endif // !UNICODE
该函数的返回值是HICON(图标句柄),第一个参数为HINSTANCE(实例句柄),即相应程序的实例句柄,第二个参数为LPCSTR,用于标识不同的图标式样。通过第二个参数可以传入系统预定义的图标,这些预定义图标是一些带有IDI前缀的标识符,同样定义在头文件winuser.h中,如下:
1 /* 2 * Standard Icon IDs 3 */ 4 #ifdef RC_INVOKED 5 #define IDI_APPLICATION 32512 6 #define IDI_HAND 32513 7 #define IDI_QUESTION 32514 8 #define IDI_EXCLAMATION 32515 9 #define IDI_ASTERISK 32516 10 #if(WINVER >= 0x0400) 11 #define IDI_WINLOGO 32517 12 #endif /* WINVER >= 0x0400 */ 13 #if(WINVER >= 0x0600) 14 #define IDI_SHIELD 32518 15 #endif /* WINVER >= 0x0600 */ 16 #else 17 #define IDI_APPLICATION MAKEINTRESOURCE(32512) 18 #define IDI_HAND MAKEINTRESOURCE(32513) 19 #define IDI_QUESTION MAKEINTRESOURCE(32514) 20 #define IDI_EXCLAMATION MAKEINTRESOURCE(32515) 21 #define IDI_ASTERISK MAKEINTRESOURCE(32516) 22 #if(WINVER >= 0x0400) 23 #define IDI_WINLOGO MAKEINTRESOURCE(32517) 24 #endif /* WINVER >= 0x0400 */ 25 #if(WINVER >= 0x0600) 26 #define IDI_SHIELD MAKEINTRESOURCE(32518) 27 #endif /* WINVER >= 0x0600 */ 28 #endif /* RC_INVOKED */ 29 30 #if(WINVER >= 0x0400) 31 #define IDI_WARNING IDI_EXCLAMATION 32 #define IDI_ERROR IDI_HAND 33 #define IDI_INFORMATION IDI_ASTERISK 34 #endif /* WINVER >= 0x0400 */
其中常用的一些预定义窗口图标式样如图1所示。
图1 窗口图标系统预定义式样
1.7鼠标指针句柄(hCursor)
WNDCLASS结构体中的数据成员hCursor用于指定应用程序的鼠标式样。
通过调用LoadCursor函数可以获取系统预定义的鼠标句柄。在winuser.h头文件中定义了LoadCursor函数的原型如下:
1 WINUSERAPI 2 HCURSOR 3 WINAPI 4 LoadCursorA( 5 __in_opt HINSTANCE hInstance, 6 __in LPCSTR lpCursorName); 7 WINUSERAPI 8 HCURSOR 9 WINAPI 10 LoadCursorW( 11 __in_opt HINSTANCE hInstance, 12 __in LPCWSTR lpCursorName); 13 #ifdef UNICODE 14 #define LoadCursor LoadCursorW 15 #else 16 #define LoadCursor LoadCursorA 17 #endif // !UNICODE
该函数的返回值是HCURSOR(鼠标句柄),第一个参数为HINSTANCE (实例句柄),即相应程序的实例句柄,第二个参数为LPCSTR ,用于标识不同的鼠标式样。通过第二个参数可以传入系统预定义的鼠标式样,这些预定义鼠标式样是一些带有IDC前缀的标识符,同样定义在头文件winuser.h中,如下:
1 /* 2 * Standard Cursor IDs 3 */ 4 #define IDC_ARROW MAKEINTRESOURCE(32512) //标准箭头光标 5 #define IDC_IBEAM MAKEINTRESOURCE(32513) //I型光标 6 #define IDC_WAIT MAKEINTRESOURCE(32514) //沙漏 7 #define IDC_CROSS MAKEINTRESOURCE(32515) //十字光标 8 #define IDC_UPARROW MAKEINTRESOURCE(32516) //上箭头光标 9 #define IDC_SIZE MAKEINTRESOURCE(32640) /* OBSOLETE: use IDC_SIZEALL */ 10 #define IDC_ICON MAKEINTRESOURCE(32641) /* OBSOLETE: use IDC_ARROW */ 11 #define IDC_SIZENWSE MAKEINTRESOURCE(32642) //指向西北和东南方向的箭头光标 12 #define IDC_SIZENESW MAKEINTRESOURCE(32643) //指向东北和西南方向的箭头光标 13 #define IDC_SIZEWE MAKEINTRESOURCE(32644) //指向东西方向的箭头光标 14 #define IDC_SIZENS MAKEINTRESOURCE(32645) //指向南北方向的箭头光标 15 #define IDC_SIZEALL MAKEINTRESOURCE(32646) //十字箭头光标 16 #define IDC_NO MAKEINTRESOURCE(32648) /*not in win3.1 */ 17 #if(WINVER >= 0x0500) 18 #define IDC_HAND MAKEINTRESOURCE(32649) //手型光标 19 #endif /* WINVER >= 0x0500 */ 20 #define IDC_APPSTARTING MAKEINTRESOURCE(32650) /*not in win3.1 */ 21 #if(WINVER >= 0x0400) 22 #define IDC_HELP MAKEINTRESOURCE(32651) //带问号的光标 23 #endif /* WINVER >= 0x0400 */
1.8客户区背景色(hbrBackground)
WNDCLASS结构体中的数据成员hbrBackground用于指定客户区背景色,其数据类型为HBRUSH(画刷句柄)。画刷是一个图形学术语,表示用于区域填充的像素着色模式。
通过调用GetStockObject函数可以获取系统预定义的“库存”画刷。在头文件winGDI.h中定义了GetStockObject函数的原型如下:
WINGDIAPI HGDIOBJ WINAPI GetStockObject( __in int i);
该函数的返回值类型为HGDIOBJ,仅有一个参数i,通过该参数可以传入系统预定义的库存画刷,这些库存画刷同样定义在头文件winGDI.h
中,如下:
1 /* Stock Logical Objects */ 2 #define WHITE_BRUSH 0 //白色画刷 3 #define LTGRAY_BRUSH 1 //亮灰色画刷 4 #define GRAY_BRUSH 2 //灰色画刷 5 #define DKGRAY_BRUSH 3 //暗灰色画刷 6 #define BLACK_BRUSH 4 //黑色画刷 7 #define NULL_BRUSH 5 //空画刷 8 #define HOLLOW_BRUSH NULL_BRUSH 9 #define WHITE_PEN 6 //白色钢笔 10 #define BLACK_PEN 7 //黑色钢笔 11 #define NULL_PEN 8 //空钢笔 12 #define OEM_FIXED_FONT 10 //原始设备制造商相关固定间距(等宽)字体 13 #define ANSI_FIXED_FONT 11 //在Windows中为固定间距(等宽)系统字体 14 #define ANSI_VAR_FONT 12 //在Windows中为变间距(比例间距)系统字体 15 #define SYSTEM_FONT 13 //系统字体 16 #define DEVICE_DEFAULT_FONT 14 //在WindowsNT中为设备相关字体 17 #define DEFAULT_PALETTE 15 //缺省调色板 18 #define SYSTEM_FIXED_FONT 16 //固定间距(等宽)系统字体
1.9窗口类的菜单(lpszMenuName)
WNDCLASS结构体中的数据成员lpszMenuName用于指定窗口类的菜单。
1.10窗口类名称(lpszClassName)
WNDCLASS结构体中的数据成员lpszClassName用于指定窗口类的名称。
2.创建自己的窗口类
学习了窗口类结构体,并了解了结构体中各个数据成员的含义和使用方法之后,就可以创建一个自己的窗口类了,具体方法如下所示:
1 static TCHAR szAppName[] = TEXT("HelloWindows"); 2 WNDCLASS wndclass; 3 //为窗口类各个数据成员赋值 4 wndclass.style = CS_HREDRAW | CS_VREDRAW; 5 wndclass.lpfnWndProc = WndProc; 6 wndclass.cbClsExtra = 0; 7 wndclass.cbWndExtra = 0; 8 wndclass.hInstance = hInstance; 9 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 10 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 11 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 12 wndclass.lpszMenuName = NULL; 13 wndclass.lpszClassName = szAppName;
在上述代码中,首先定义了一个字符串“HelloWindows”,用作窗口类的名称。然后定义了一个窗口类wndclass,并为该窗口类wndclass的各个数据成员赋值。其中,语句wndclass.lpfnWndProc = WndProc;指定了窗口类wndclass的窗口过程为WndProc(此处并没有实现该函数方法)。语句wndclass.hInstance = hInstance;指定了窗口类wndclass的实例句柄为hInstance(hInstance是WinMain函数的第一个参数)。
相关资料:
WNDCLASS类简介
http://zifeiyu520yang.blog.sohu.com/115399630.html
探索Win32系统之窗口类
http://www.moon-soft.com/doc/6744.htm
Win32编程day03 学习笔记
http://www.cnblogs.com/tangzhengyue/archive/2012/08/11/2633933.html
MSDN手册
http://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx