特殊说明:版权归个人所有,请勿转载,谢谢合作。
Win32应用程序,窗口作为最主要的组成部分,创建过程会相对复杂一些,窗口创建包括五个步骤:
(1)设计一个窗口类;
(2)注册窗口;
(3)创建窗口;
(4)显示及更新窗口;
(5)进入消息循环。
只有经过这五个步骤,窗口才能显示出来。【例3-1】通过这五个步骤,实现窗口的创建,具体实现代码如下所示。
//**************************************************************
// NAME : Demo_03
//**************************************************************
// POWER : Copyright (c) 2012 for lixinghua.
// AUTHOR : 2012-2-6 11:50 Create by lixinghua for functions.
// VERSION : V1.0.0.1
// NOTE : 手工整理的创建窗口的代码,本程序只创建一个名称
// 为MyWin的窗口。
//**************************************************************
//
#include <Windows.h>
// 过程处理函数,用于系统消息的处理。
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//-----------------------------------------------------------------------------
// FUNC : 入口函数
//-----------------------------------------------------------------------------
// IN : hInstance,进程的实例句柄;
// hPrevInstance,前一个进程实例句柄,默认为NULL即可;
// lpCmdLine,命令行参数;
// nCmdShow,当前窗口显示状态。
// OUT : void
// RETURN : 返回为整型,代表窗口的状态,其中APIENTRY描述了
// 压栈的顺序。
// AUTHOR : 2012-2-6 11:18 Create by lixinghua for functions.
// NOTE : 此函数为Win32入口函数。
//-----------------------------------------------------------------------------
//
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// 定义所用到的参数
//
char szWindowClass[] = { "WinClsName" }; // 窗口类的名称
HWND hWnd = NULL; // 用于存放窗口句柄
char szTitle[] = { "MyWin" }; // 窗口标题名称
MSG msg; // 存放消息的结构体,
// 由系统提供
// 1. 设计一个窗口类
//
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = NULL;
// 2. 注册窗口
//
RegisterClassEx(&wcex);
// 3. 创建窗口
//
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
// 判断创建是否成功
if (!hWnd)
{
return FALSE;
}//end if
// 4. 显示并更新窗口
ShowWindow(hWnd, nCmdShow); // 请注意 nCmdShow 参数
UpdateWindow(hWnd);
// 5. 进入消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}//end while
return 0;
}
//-----------------------------------------------------------------------------
// FUNC : 过程处理函数
//-----------------------------------------------------------------------------
// IN : hWnd,窗口句柄;
// message,要处理的消息ID,以此来区分消息;
// wParam,消息参数,根据消息的不同内容也有所不同;
// lParam,消息参数,根据消息的不同内容也有所不同。
// OUT : void
// RETURN : void
// AUTHOR : 2012-2-6 11:36 Create by lixinghua for functions.
// NOTE : 此函数用于系统消息的处理。
//-----------------------------------------------------------------------------
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// 调用系统默认消息处理,即交给系统处理。
return DefWindowProc(hWnd, message, wParam, lParam);
}//end switch
return 0;
}
程序的运行结果如图3.4所示。

- 图3.4 第一个窗口程序
这是本书的第一个程序,也是今后所有窗口程序的模板,所有程序将基于此框架。今后的程序设计过程中,只要将此代码复制到相应的工程文件中即可。接下来,将对根据窗口创建的每一个步骤来分析代码的具体含义。
3.2.1 设计一个窗口类
设计窗口类,主要是对WNDCLASSEX(WNDCLASS是一个比较老的结构体,在程序中使用扩展版本WNDCLASSEX)结构体进行初始化操作。此结构体是由系统提供,直接使用即可,原型如下:
typedef struct _WNDCLASSEX {
UINT cbSize; // 指定结构体大小
UINT style; // 窗口风格
WNDPROC lpfnWndProc; // 窗口消息处理函数指针
int cbClsExtra; // 指定窗口类结构后的附加字节数
int cbWndExtra; // 指定窗口事例后的附加字节数
HANDLE hInstance; // 进程的实例句柄
HICON hIcon; // 大图标句柄,应用程序的图标
HCURSOR hCursor; // 光标句柄
HBRUSH hbrBackground; // 背景画刷句柄
LPCTSTR lpszMenuName; // 菜单名称
LPCTSTR lpszClassName; // 窗口类的名称
HICON hIconSm; // 小图标句柄,窗口左上角图标
} WNDCLASSEX;
在使用WNDCLASSEX结构体时,首先需要告诉程序,此结构体在内存中占用空间的大小。这个非常重要,否则将会出现一些意想不到的问题,所以在程序中这样写:
wcex.cbSize = sizeof(WNDCLASSEX);
参数style,用来指定窗口的风格,程序中CS_HREDRAW | CS_VREDRAW风格,指定了窗口客户区高度与宽度改变时,则重绘整个窗口。其中“CS_”代表的是“class style”,窗口风格标识如表3.1所示。
- 表3.1 窗口风格标识
| 标识 | 描述 |
| CS_BYTEALIGNCLIENT | 在字节边界上(在x方向上)定位窗口的用户区域的位置 |
| CS_BYTEALIGNWINDOW | 在字节边界上(在x方向上)定位窗口的位置 |
| CS_CLASSDC | 该窗口类的所有窗口实例都共享一个窗口类DC |
| CS_DBLCLKS | 允许向窗口发送鼠标双击的消息 |
| CS_GLOBALCLASS | 当调用CreateWindow 或 CreateWindowEx 函数来创建窗口时,允许它的hInstance参数和注册窗口类时,传递给RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。 |
| CS_HREDRAW | 当窗口移动或窗口宽度改变时,重画整个窗口 |
| CS_NOCLOSE | 禁止系统菜单上的关闭命令 |
| CS_OWNDC | 为该类中每个窗口分配一个单值的设备描述表(即给予每个窗口实例它本身的DC)。注意,尽管这样是很方便,但它必须慎重使用,因为每个DC大约要占800个字节的内存。 |
| CS_PARENTDC | 将子窗口的裁剪区域设置到父窗口的DC中去,这样子窗口便可以在父窗口上绘制自身。 |
| CS_SAVEBITS | 以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息。该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失。设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存。 |
| CS_VREDRAW | 当窗口移动或窗口高度改变时,重画整个窗口 |
参数lpfnWndProc,是一个函数指针,指向窗口消息处理函数,窗口消息处理函数是一个回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时,由另一方调用,用于处理当前应用程序的事件消息。回调函数在使用前要对函数进行定义(这个不是必要的),代码如下所示:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
回调函数还需要有实现部分:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// 调用系统默认消息处理,即交给系统处理。
return DefWindowProc(hWnd, message, wParam, lParam);
}//end switch
return 0;
}
回调函数内部的实现功能,将在后面的章节进行介绍,这里就不过多论述。回调函数有了定义部分,同时又有了实现部分,需要在设计窗口类时将函数指针赋值给“lpfnWndProc”,窗口事件发生时,供另一方调用,如何调用,会在3.2.5章节中进一步说明。
参数cbClsExtra和cbWndExtra原是为指示Windows,将附加的运行时间信息保存到Windows类某些单元中,但实际应用中,默认值设置为“0”即可。
参数hInstance,是进程的实例句柄,该句柄是WinMain函数的参数hInstance,它代表所设计的窗口属于当前进程。
参数hIcon,为应用程序的图标句柄,我们通常称它为大图标,在这里不使用图标,设置为NULL即可。
参数hCursor,标识该窗口类的光标,hCursor必须是一个光标资源的句柄。若hCursor字段为NULL,则无论何时鼠标移到应用程序窗口时,应用程序显示设置光标形状(即箭头形状的光标)。如果使用LoadCursor函数,则可以加载系统提供或者自定义光标,系统默认光标值如表3.2所示。
- 表3.2 系统默认光标标识
| 标识 | 描述 |
| IDC_APPSTARTING | 标准箭头+小沙漏 |
| IDC_ARROW | 标准箭头 |
| IDC_CROSS | 横标线 |
| IDC_HAND | 小手状态,Windows NT 5.0和之后的版本使用 |
| IDC_HELP | 标准箭头+问号 |
| IDC_IBEAM | 文本I型标 |
| IDC_NO | 带正斜线的圆圈 |
| IDC_SIZEALL | 四向箭头指向东、西、南、北 |
| IDC_SIZENESW | 双箭头指向东北和西南 |
| IDC_SIZENS | 双箭头指向南北 |
| IDC_SIZENWSE | 双箭头指向西北和东南 |
| IDC_SIZEWE | 双箭头指向东西 |
| IDC_UPARROW | 垂直箭头 |
| IDC_WAIT | 沙漏 |
参数hbrBackground,标识了该窗口类的背景画刷。hbrBackground字段必须是用于绘制背景颜色的物理画刷的句柄,或者是一个颜色的值。如果给出一个颜色的值,它必须是标准系统颜色加1,如代码所示:
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
系统默认的颜色值,必须是下列颜色之一,千万不要忘记加1。
COLOR_ACTIVEBORDER COLOR_ACTIVECAPTION COLOR_APPWORKSPACE COLOR_BACKGROUND COLOR_BTNFACE COLOR_BTNSHADOW COLOR_BTNTEXT COLOR_CAPTIONTEXT COLOR_GRAYTEXT COLOR_HIGHLIGHT COLOR_HIGHLIGHTTEXT COLOR_INACTIVEBORDER COLOR_INACTIVECAPTION COLOR_MENU COLOR_MENUTEXT COLOR_SCROLLBAR COLOR_WINDOW COLOR_WINDOWFRAME COLOR_WINDOWTEXT
参数lpszClassName,描述窗口类名称的字符串,以“\0”作为结束,类名称可以随意定义,但它要与创建窗口时使用的CreateWindow函数的第一个参数名称相同,后面还将对此进行介绍。
参数lpszMenuName,是描述菜单的字符串,若使用一个整数标识菜单,可以使用MAKEINTRESOURCE宏。如果lpszMenuName为NULL,那么该窗口类的窗口将没有默认菜单。
参数hIconSm,窗口左上角图标的句柄,通常称它为小标图,在这里不使用图标,设置为NULL即可。
3.2.2 注册窗口
初始化完WNDCLASSEX结构体,就可以进行注册操作了。注册方法很简单,只要使用RegisterClassEx函数,将初始化完的结构体以参数方式调用即可,代码如下所示:
// 2. 注册窗口 // RegisterClassEx(&wcex);
当然也可以使用RegisterClass来注册窗口,但它的功能相对来说简单一些,所以代码使用标准的扩展注册函数RegisterClassEx函数。
3.2.3 创建窗口
要创建窗口,就需要使用CreateWindow或CreateWindowEx函数来实现。后者相对来说版本更新一些,附加类型参数更多一些,但为了与微软自动生成模板统一,这里选用CreateWindow函数,函数原型如下所示:
HWND CreateWindow(
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu or child-window
// identifier
HANDLE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data
);
参数lpWindowName,即窗口的标题名称。将“szTitle”赋值为“MyWin”。如果创建的窗口为按钮,此参数内容为按钮上的文字,如果是编辑框,则是编辑框里的内容,它是根据创建的内容不同而不同。
参数lpClassName,创建的窗口类的名称。这里设置窗口类名称为“WinClsName”,但需要注意的是,这个名称要与WNDCLASSEX结构的lpszClassName参数赋相同的值。lpszClassName这个参数,还有一个很有意思的设置方法,如果这个参数赋值为“BUTTON”,则创建出来的窗口将是一个大按钮,也就是说所创建的窗口认为是按钮风格,当然也可以创建其他风格的内容,其中有:“BUTTON”、“COMBOBOX”、“EDIT”、“LISTBOX”、“MDICLIENT”、“RichEdit”、“RICHEDIT_CLASS”、“SCROLLBAR”、“STATIC”,当然在这些里,部分是直接赋值即可,还有一部分需要特殊处理,具体的操作方法请参照MSDN。
参数dwStyle,程序中设置为WS_OVERLAPPEDWINDOW,即为拥有所有风格的层叠窗口(标准窗口样式)。可以通过此参数来指定窗口是否存在标题栏、系统菜单、最小化、最大化/还原、关闭和可改变大小的边框。可以指定窗口一种风格,也可以使用组合的方式使用多种风格样式,样式风格如表3.3所示。
- 表3.3 系统默认光标标识
| 风格标识 | 描述 |
| WS_BORDER | 创建具有边框风格的窗口 |
| WS_CAPTION | 创建具有标题框的窗口(包括WS_BODER风格) |
| WS_CHILD | 创建一个子窗口。这个风格不能与WS_POPUP风格合用 |
| WS_CHLDWINDOW | 与WS_CHILD相同 |
| WS_CLIPCHILDREN | 此风格只在创建父窗口时使用,当在父窗口内绘图时,排除子窗口区域 |
| WS_CLlPBLINGS | 排除子窗口之间的相对区域,当一个特定的窗口接收到WM_PAINT消息时,WS_CLIPSIBLINGS 风格将所有层叠窗口排除在绘图之外,只重绘指定的子窗口。如果未指定WS_CLIPSIBLINGS风格,并且子窗口是层叠的,则在重绘子窗口的客户区时,就会重绘邻近的子窗口 |
| WS_DISABLED | 创建一个禁止操作的子窗口。窗口不能接受来自用户的输入信息 |
| WS_DLGFRAME | 创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题栏 |
| WS_GROUP | 指定组控制。这个控制组由第一个和随后定义的控制组成,自第二个控制开始每个控制,具有WS_GROUP风格,每个组的第一个控制带有WS_TABSTOP风格,从而使用户可以在组间移动。 |
| WS_HSCROLL | 创建具有水平滚动条的窗口 |
| WS_ICONIC | 创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE风格相同 |
| WS_MAXIMIZE | 创建一个初始状态为最大化状态的窗口 |
| WS_MAXIMIZEBOX | 创建一个具有最大化按钮的窗口。该风格不能与WS_EX_CONTEXTHELP风格同时出现,同时必须指定WS_SYSMENU风格 |
| WS_OVERLAPPED | 产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同 |
| WS_OVERLAPPEDWINDOW | 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXIMIZEBOX风格的层叠窗口,与WS_TILEDWINDOW风格相同 |
| WS_POPUP | 创建一个弹出式窗口。该风格不能与WS_CHLD风格同时使用 |
| WS_POPUPWINDOW | 创建一个具有WS_BORDER,WS_POPUP,WS_SYSMENU风格的窗口,WS_CAPTION和WS_POPUPWINDOW必须同时设定才能使窗口可见 |
| WS_SIZEBOX | 创建一个可调边框的窗口,与WS_THICKFRAME风格相同 |
| WS_SYSMENU | 创建一个在标题栏上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格 |
| WS_TABSTOP | 创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制 |
| WS_THICKFRAME | 创建一个具有可调边框的窗口,与WS_SIZEBOX风格相同 |
| WS_TILED | 产生一个层叠的窗口。窗口有标题和边框。与WS_OVERLAPPED风格相同 |
| WS_TILEDWINDOW | 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU,WS_THICKFRAME,
WS_MINIMIZEBOX,WS_MAXMIZEBOX风格的层叠窗口。与WS_OVERLAPPEDWINDOW风格相同 |
| WS_VISIBLE | 创建一个初始状态为可见的窗口 |
| WS_VSCROLL | 创建具有垂直滚动条的窗口 |
参数X、Y,它们分别指定窗口初始水平位置与垂直位置。对一个层叠或弹出式窗口,X、Y参数是屏幕坐标系的窗口的左上角的初始X、Y坐标。如果参数被设为CW_USEDEFAULT,则系统为窗口缺省的左上角坐标并忽略X,Y参数。CW_USEDEFAULT只对层叠窗口有效,如果为弹出式窗口或子窗口设定,则X和Y参数被设为零。
参数nWidth、nHeight,指定窗口的宽度与高度。参数是以像素为单位,如果设置为CW_USEDEFAULT,这将由Windows来决定窗口尺寸大小。
参数hWndParent,父窗口句柄。如果取值为NULL,默认以桌面作为父窗口。
参数hMenu,菜单句柄。如果取值为NULL,则默认不使用菜单。
参数hInstance,应用程序的实例句柄。这个参数是WinMain入口函数中的hInstance,直接赋值即可。
参数lpParam,指向一个值的指针,该值传递给窗口 WM_CREATE消息。此参数为高级功能,设置为NULL即可。
CreateWindow函数返回值:如果函数调用成功,返回值为新窗口的句柄;如果函数调用失败,返回值为NULL。若想获得更多错误信息,调用GetLastError函数获得。唯一需要注意的是新窗口的句柄,它是唯一标识一个窗口的,在后面的很多地方都会用到。
3.2.4 显示及更新窗口
一旦窗口创建完成,它可能是可见的,也有可能不可见,完全取决于CreateWindow函数的dwStyle风格,是否设置了WS_VISIBLE。当然本例设置为WS_OVERLAPPEDWINDOW,其中已经包含了WS_VISIBLE,即窗口可见,但如果我们没有设置窗口可见,则需要使用ShowWindow、UpdateWindow函数来显示及更新窗口。当然即使设置了WS_VISIBLE,使用ShowWindow、UpdateWindow函数也无伤大雅。
ShowWindow函数的第一个参数是窗口句柄。这个句柄就是CreateWindow函数返回的窗口句柄;第二个参数是nCmdShow,还记不记得WinMain函数的最后一个参数?需要将WinMain入口函数的nCmdShow值赋值至此。窗口的显示与隐藏完全取决于这里。
UpdateWindow函数为更新窗口内容,并产生一个WM_PAINT消息。UpdateWindow函数的hWnd参数,同样是CreateWindow函数返回的窗口句柄。
3.2.5 进入消息循环
程序从上至下,执行到这里,窗口已经创建完成,并且进行了显示及更新操作。但是,有没有注意到,根据程序的执行过程,从上至下,执行到显示及更新窗口,刚刚创建的窗口将会一闪而过,为什么?因为程序已经结束。所以Win32加入了消息机制的循环处理。
Windows操作系统是以消息作为驱动方式,操作系统将用户的事件,以消息的形式发送给应用程序,应用程序再根据消息的具体内容,进行相应的处理。一条消息包括六方面的内容,它由MSG结构体存放,下面是它的原型。
typedef struct tagMSG { // msg
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
参数hwnd,目标窗口的句柄,即消息将要发向的窗口句柄。
参数message,消息类型标识符,在Win32中,消息是由一个数值来表示的,不同的消息对应不同的数值。但是由于数值不便于记忆,所以Win32中,消息以“WM_”作为开头WM是Window Message的缩写),将其消息内容定义在后面,即WM_XXX形式,XXX对应某种消息的英文拼写的大写形式。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是WM_CHAR,等等。关于消息各种写法与用法,将在后面的章节一一进行介绍。
参数wParam与lParam,用于指定消息的附加信息,附加信息的内容取决于消息,根据消息的不同而不同。
参数time,消息的传递时间。
参数pt,消息发送时光标的屏幕坐标位置。
接下来看一下系统的实现代码:
// 5. 进入消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}//end while
GetMessage函数的功能是从消息队列中获得消息,并存放到msg结构体中,再通过TranslateMessage函数将消息翻译成可传递的消息,最后由DispatchMessage函数,将消息分发到对应窗口的过程处理函数中。发送到哪个过程处理函数,取决于设计窗口类时WNDCLASSEX结构体的第三个参数lpfnWndProc,通过此函数指针,将消息以回调的方式传送到相应的函数中去处理,本例的过程处理函数是WndProc。
当过程处理函数执行完毕后,又回到while循环处,只要GetMessage函数返回值永远为真,那么窗口将永远停留在那里。
3.2.6 过程处理函数
过程处理函数(消息处理函数),就是处理窗口消息的函数。它是一个回调函数,是一个通过函数指针调用的函数。前面已经提到,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。例如,函数A的指针作为参数传递给另一个函数B,那么在函数B中通过这个指针来调用它所指向的函数A,函数A就被称作是回调函数。
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
过程处理函数中,各个参数与MSG结构体中参数一一对应,当遇到DispatchMessage函数时,通过函数指针,找到了过程处理的函数所在,再将msg各个参数分发到过程处理函数所对应的参数列表中,如表3.4所示。
- 表3.4 MSG与WndProc参数对应关系
| MSG | WndProc |
| hwnd | hwnd |
| message | message |
| wParam | wParam |
| lParam | lParam |
| time | – |
| pt | – |
到目前为止,所做的工作是创建窗口,而不是实现一个特定的功能,所以做为创建窗口的代码,可以看作是一个框架,在需要的时候直接拷贝过来即可。而在Win32编程中,所有做的操作都是在过程处理函数中,也就是说,今后写的大部份代码,都会在这个过程处理函数中实现。因为它很重要,所以再次展示给大家:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// 调用系统默认消息处理,即交给系统处理。
return DefWindowProc(hWnd, message, wParam, lParam);
}//end switch
return 0;
}
在过程处理函数中,有一个switch语句,所有捕获的消息都可以写在这里处理,写法是:
case WM_XXX: // 具体消息的实现代码 break;
所要处理的消息,只要将WM_XXX替换成所要处理的消息即可。需要注意的是,这个过程处理函数,一次只能处理一条消息。那有可能会问,在处理消息的时候,大部分消息是由鼠标与键盘产生的,当一边进行键盘操作,而另一边在进行鼠标的操作,像玩游戏,这样的操作是被允许的,但如何处理?其实即使鼠标与键盘同时操作,它们所产生的消息也是有先后顺序的,系统会将它们以先后顺序添加到进程的消息队列中(这部分知识会在3.3节中进行介绍),等待GetMessage的调用。如果想保存上一次消息处理过程的中间结果,可以使用全局变量或静态变量来存储,但要时刻记住,过程处理函数,一次只能执行一个消息,函数执行结束后,函数内部的变量将全部失效,内存将被释放。
本例中,只处理了WM_DESTROY消息,这个消息是在窗口被关闭后触发的,此时调用PostQuitMessage函数,向系统的消息队列里发送一个WM_QUIT的消息,发送完毕后返回,此函数向系统表明有一个线程,请求在随后的某一时间终止。
必须把所有不处理的消息交给DefWindowProc函数处理,也要将WndProc函数的参数原样地返回给Windows,否则Windows就会失去与应用程序通信的途径,也就不能再控制窗口的行为。