特殊说明:版权归个人所有,请勿转载,谢谢合作。
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就会失去与应用程序通信的途径,也就不能再控制窗口的行为。