3.2 窗口的创建过程

特殊说明:版权归个人所有,请勿转载,谢谢合作。

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就会失去与应用程序通信的途径,也就不能再控制窗口的行为。

 

转载请附上原文出处链接及本声明
李老师的博客 » 3.2 窗口的创建过程

发表评论

提供最优质的文章集合

立即查看 了解详情