Windows程序内部运行机制
要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制。本文将深入剖析Windows程序的内部运行机制,为读者扫清VC++学习路途中的第一个障碍,为进一步学习MFC程序打下基础。 1
API与SDK
我们在编写标准C程序的时候,经常会调用各种库函数来辅助完成某些功能:初学者使用得最多的C库函数就是printf了,这些库函数是由你所使用的编译器厂商提供的。在Windows平台下,也有类似的函数可供调用,不同的是,这些函数是由Windows操作系统本身提供的。
Windows操作系统提供了各种各样的函数,以便我们开发Windows应用程序。这些函数是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称API函数。我们在编写Windows程序时所说的API函数,就是指系统提供的函数,所有主要的Windows函数都在Windows.h头文件中进行了声明。
Windows操作系统提供了1000多种API函数,作为开发人员,要全部记住这些函数调用的语法几乎不可能。那么我们符合才能更好地去使用和掌握这些函数呢?微软提供的API函数大多是有意义的单词的组合,每个单词的首字母大写,例如CreateWindow,读者从函数的名字上就可以猜到,这个函数是用来为程序创建一个窗口的。其他的,例如,ShowWindow(用于显示窗口),LoadIcon(用于加载图标),SendMessage(用于发送消息)等,这些函数的准确拼写与调用语法都可以在MSDN中查找到。
你可以把MSDN理解为微软向开发人员提供的一套帮助系统,其中包含大量的开发文档、技术文章和示例代码。MSDN包含的信息非常全面,程序员可以利用MSDN开辅助开发,还可以利用MSDN来进行学习,从而提高自己。对于初学者来说,学会使用MSDN并从中汲取知识,是必须要掌握的技能。
我们在程序开发过程中,没有必要去死记硬背函数的调用语法和参数信息,只要能快速地从MSDN中找到所需的信息就可以了,等使用的次数多了,这些函数自然也就记住了。
我们经常听人说Win32 SDK开发,那么什么是SDK呢。SDK的全程是Software Development Kit,中文为软件开发包。假如现在我们要开发呼叫中心,在购买语音卡的同时,厂商就会提供语音卡的SDK开发包,以便我们对语音卡的编程操作。这个开发包通常都会包含语音卡的API函数库、帮助文档、使用手册、辅助工具等资源。也就是说,SDK实际上就是开发所需资源的一个集合。现在读者应该明白Win32 SDK的含义了吧,即Windows 32位平台下的软件开发包,包括了API函数、帮助文档、微软提供的一些辅助开发工具。
提示:API和SDK是一种广泛使用的专业术语,并没有专指某一种特定的API和SDK,例如,语音
卡API、语音卡SDK、Java API、Java SDK等。 2
窗口与句柄
窗口是Windows应用程序中一个非常重要的元素,一个Windows应用程序至少要有一个窗口,称为主窗口。窗口是屏幕上的一块巨型区域,是Windows应用程序与用户进行交互的接口,利用窗口可以接收用户的输入,以及显示输出。
一个应用程序窗口通常都包含标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,有的还有滚动条。
窗口可以分为客户区和非客户区,客户区是窗口的一部分,应用程序通常在客户区中显示文字或者绘制图形。标题栏、菜单栏、系统菜单、最小化框和最大化框、可调边框统称为窗口的非客户区,它们由Windows系统来管理,而应用程序则主要管理客户区的外观及操作。
窗口可以有一个父窗口,有父窗口的窗口称为子窗口。除了图示的窗口以外,对话框和消息框也是一种窗口。在对话框上通常还包含许多子窗口,这些子窗口的形式有按钮、单选按钮、复选框、组框、文本编辑框等。
提示:我们在启动Windows系统后,看到的桌面也是一个窗口,称为桌面窗口,它由Windows系统
创建和管理。
在Windows应用程序中,窗口是通过窗口句柄(HWND)来标识的。我们要对某个窗口进行操作,首先就要得到这个窗口的句柄。句柄(HANDLE)是Windows程序中一个重要的概念,使用也非常频繁。在Windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时会为它们分配内存,并返回标识这些资源的标识号,即句柄。在后面的内容中我们还会看到图标句柄(HICON)、光标句柄(HCURSOR)和画刷句柄(HBRUSH)。 3
消息与消息队列
在传统的C程序中,我们调用fopen函数打开文件,这个库函数最终调用操作系统(提供的函数)来打开文件。而在Windows中,不仅用户程序可以调用系统的API函数,返回来,系统也会调用用户程序,这个调用是通过消息来进行的。
Windows程序设计是一种完全不同于传统的DOS方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要是基于消息的。例如,当用户在窗口中画图的时候,按下鼠标左键,此时,操作系统会感知到这一事件,于是将这个事件包装成一个消息,投递到应用程序的消息队列中,然后应用程序从消息队列中取出消息并进行相应。在这个处理过程中,操作系统也会给应用程序“发送消息”。所谓“发送消息”,实际上是操作系统调用程序中一个专门处理消息的函数,这个函数称为窗口过程。
1.消息
在Windows程序中,消息是由MSG结构体来表示的。MSG结构体的定义如下(参见MSDN):
typedef struct tagMSG{ HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam;
DWORD time; POINT pt;
}MSG;
该结构体中各成员变量的含义如下:
第一个成员变量hwnd表示消息所属的窗口。我们通常开发的程序都是窗口应用程序,一个消息一般都是与某个窗口相关联的。例如,在某个活动窗口中按下鼠标左键,产生的按键消息就是发给该窗口的。在Windows程序中,用HWND类型的变脸来标识窗口。
第二个成员变量message指定了消息的标识符。在Windows中,消息是由一个数值来表示的,不同的消息对应不同的数值。但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM是Windows Message的缩写)的形式,XXX对应某种消息的英文拼写的大写形式。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是WM_CHAR,等等。在程序中我们通常都是以WM_XXX宏的形式来使用消息的。
提示:如果想知道WM_XXX消息对应的具体数值,可以在Visual C++开发环境中选中WM_XXX,然后
单击鼠标右键,在弹出菜单中选择goto definition,即可看到该宏的具体定义。跟踪或查看某个变量的定义,都可以使用这个方法。
第三、四个成员变量wParam和lParam,用于指定消息的附加信息。例如,当我们收到一个字符消息的时候,message成员变量的值就是WM_CHAR,但用户到底输入的是什么字符,那么就由wParam和lParam来说明。wParam、lParam表示的信息随消息的不同而不同。如果想知道这两个成员变量具体表示的信息,可以在MSDN中关于某个具体消息的说明文档查看到。读者可以在VC++开发环境中通过goto definition查看一下WPARAM和LPARAM这两种类型的定义,可以发现这两种类型实际上就是unsigned int和long。
最后两个变量分别表示消息投递到消息队列中的时间和鼠标的当前位置。 2.消息队列
每一个Windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。例如,当我们按下鼠标左键的时候,将会产生WM_LBUTTONDOWN消息,系统会将这个消息放到窗口所属的应用程序消息队列中,等待应用程序的处理。Windows将产生的消息依次放到消息队列中,而应用程序则通过一个消息循环不断地从消息队列中取出消息,并进行相应。这种消息机制,就是Windows程序运行的机制。关于消息队列和消息响应,在后门我们还会详细讲述。
3.进队消息和不进队消息
Windows程序中的消息可以分为“进队消息”和“不进队消息”。进队的消息将由系统放入到应用程序的消息队列中,然后由应用程序取出并发送。不进队的消息在系统调用窗口过程时直接发送给窗口。不管是进队消息还是不进队消息,最终都由系统调用串口过程函数对消息进行处理。
4 WinMain函数
接触过Windows编程方法的读者都知道,在应用程序中有一个重要的函数WinMain,这个函数是应用程序的基础。当Windows操作系统启动一个程序时,它调用的就是该程序的WinMain函数(实际是由插入到可执行文件中的启动代码调用的)。WinMain是Windows程序的入口点函数,与DOS程序的入口点函数main的作用相同,当WinMain函数结束或返回是,Windows应用程序结束。
下面,让我们来看一个完整的Win32程序,该程序实现的功能是创建一个窗口,并在该窗口中相应键盘及鼠标消息,程序实现的步骤为:
WinMain函数的定义; 创建一个窗口; 进行消息循环; 编写窗口过程函数 4.1 WinMain函数的定义
WinMain函数的原型声明如下:
int WINAPI WinMain{
HINSTANCE hInstance, //handle to current instance HINSTANCE hPreInstance, //handle to previou instance LPSTR lpCmdLine, //command line int nCmdShow //show state
};
WinMain函数接收4个参数,这些参数都是在系统调用WinMain函数时,传递给应用程序的。
第一个参数hInstance表示该程序当前运行的实例的句柄,这是一个数值。当程序在Windows下运行时,它唯一标识运行中的实例(注意,只有运行中的程序实例,才有实例句柄)。一个应用程序可以运行多个实例,每运行一个实例,系统都会给该实例分配一个句柄值,并通过hInstance参数传递给WinMain函数。
第二个参数hPrevInstance表示当前实例的前一个实例的句柄。通过查看MSDN我们可以知道,在Win32环境下,这个参数总是NULL,即在Win32环境下,这个参数不再起作用。
第三个参数lpCmdLine是一个以空字符终止的字符串,指定传递给应用程序的命令行参数。例如:在D盘下有一个sunxin.txt文件,当我们用鼠标双击这个文件时将启动记事本程序(notepad.exe),此时系统会将D:\sunxin.txt作为命令行参数传递给记事本程序的WinMain函数,记事本程序在得到这个文件的全路径名后,就在窗口总显示该文件的内容。要在VC++开发环境中向应用程序传递参数,可以单击菜单【Project】【Setting】,选择“Debug”选项卡,在“Program arguments”编辑框中输入你想传递给应用程序的参数。
第四个参数nCmdShow指定程序的窗口应该如何显示,例如最大化、最小化、隐藏等。这个参数的值由该程序的调用者所指定,应用程序通常不需要去理会这个参数的值。
关于WinMain函数前的修饰符WINAPI,请参看下面关于__stdcall的介绍。读者可以利用goto definition功能查看WINAPI的定义,可以看到WINAPI其实就是__stdcall。
4.2 窗口的创建
创建一个完整的窗口,需要经过下面几个操作步骤 设计一个窗口类; 注册成口类; 创建窗口;
显示及更新窗口。
下面的四个小分节将分别介绍创建窗口的过程。
1.设计一个窗口类
一个完整的窗口具有许多特征,包括光标(鼠标进入该窗口时的形状)、图标、背景色等。窗口的创建过程类似于汽车的制造过程。我们在生产一个型号的汽车之前,首先要对该型号的汽车进行设计,在图纸上画出汽车的结构图,设计各个零部件,同时还要给该型号的汽车取一个响亮的名字,例如“奥迪A6”。在完成设计后,就可以按照“奥迪A6”这个型号生产汽车了。 类似地,在创建一个窗口前,也必须对该类型的窗口进行设计,指定窗口的特征。当然,在我们设计一个窗口时,不像汽车设计这么复杂,因为Windows已经为我们定义好了一个窗口所应有的基本属性,我们只需要像考试时做填空题一样,将需要我们填充的部分填写完整,一种窗口就设计好了。在Windows中,要达到做填空题的效果,只能通过结构体来完成,窗口的特征就是由WNDCLASS结构体来定义的。WNDCLASS结构体的定义如下(请读者自行参看MSDN):
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor;
HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName;
} WNDCLASS
下面对该结构体的成员变量做一个说明。
第一个成员变量style指定这一类型窗口的样式,常用的样式如下: CS_HREDRAW
当窗口水平方向上的宽度发生变化时,将重新绘制整个窗口。当窗口发生重绘时,窗口中的文字和图形将被擦除。如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。
CS_VREDRAW
当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。当窗口发生重绘时,窗口中的文字和图形将被擦除。如果没有指定这一样式,那么在垂直方向上调整窗口宽度时,将不会重绘窗口。
CS_NOCLOSE
禁用系统菜单的Close命令,这将导致窗口没有关闭按钮。 CS_DBLCLKS
当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。 style成员的其他取值请参阅MSDN。
知识点 在Windows.h中,以CS_开头的类样式(Class Style)标识符被定义为16位的常量,这些常量都只有某一位为1.在VC++开发环境中,利用goto definition功能,可以看到CS_VREDRAW=0x0001, CS_HREDRAW=0x0002,CS_DBLCLKS=0x0008,CS_NOCLOSE=0x0200,读者可以将这些16进制数转换为2进制数,就可以发现它们都只有1位为1,并且为1的位各不相同。用这种方式定义的标识符成为“位标志”,我们可以使用位运算操作符来组合使用这些样式。例如,要让窗口在水平和垂直尺寸发生变化时发生重绘,我们可以使用位( | )操作符将CS_HREDRAW和CS_VREDRAW组合起来,如style=CS_HREDRAW|CS_VREDRAW。假如有一个变量具有多个样式,那么可以先对该样式标识符进行取反(~)操作,然后再和这个变量惊醒与(&)操作即可实现。例如要去掉先前的style变量所具有的CS_VREDRAW样式,可以编写代码:style=style&~CS_VREDRAW。 在Windows程序中,经常会用到这种位标志标识符,后面我们在创建窗口时用到的窗口样式,也是属于位标志标识符。
1.API与SDK
api是windows操作系统提供给应用程序编程的接口(Application Proramming Interface)简称api函数,windows有1000多种api函数。
sdk全称是Software Development Kit,中文译为软件开发包。例如:购买语音卡的同时,厂商就会提供语音卡的sdk,这个sdk通常包含语音卡的api函数库、帮助文档、使用手册、辅助工具等资源。
2.句枘
句柄(HANDLE)是Windows程序中一个重要的概念,使用非常频繁。在windows程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源会为它们分配内存,并返回标识这些资源的标识号,即句柄。
例窗口句枘(HWND)、图标句柄(HICON)、光标句柄(HCURSOR)、画刷句柄(HBRUSH)
3.消息
windows程序设计是一事件驱动方式的程序设计模式,主要是基于消息的。
在windows程序中,消息是由MSG结构体来表示的。MSG结构体定义如下
typedef struct tagMSG {
HWND hwnd;
//消息所属的窗口
UINT message;
//消息的标识符,消息由数值表识的不便记忆,所以定义为宏WM_XXX(WM是windows message的缩写)XXX对定消息英文大写,例如鼠标左键按下消息WM_LBUTTONDOWN,键盘按下消息WM_KEYDOWN,字符消息就是WM_CHAR。
WPARAM wParam;
LPARAM lParam;
//wParam和lParam用于指定消息的附加信息。例如收到一个字符消息时,message的成员变量就是WM_CHAR,但用户到底输入的到底什么字符,就由wParam和lParam来说明。
DWORD time;
//消息投递到消息队列的时间。
POINT pt;
//鼠标的当前位置。
} MSG;
4.消息队列
每一个windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。
当消息投放到消息队列中后,应用程序则通过一个消息循环不断地从消息队列中取出消息,并进行响应。这种消息机制,就是windows程序运行的机制。
5.进队消息和不进队消息
进队消息将由系统放入到应用程序的消息队列中,然后由应用程序取出并发送。不进队消息在系统调用窗口过程时直接发送给窗口。不管是进队消息还是不进队消息,最终都由系统调用窗口过程函数对消息进行处理。
6.WinMain函数
当Windows操作系统启动一个程序时,它调用的就是该程序的WinMain函数(实际是由插入到可执行文件中的启动代码调用的)。WinMain是Windows程序的入口点函数。当WinMain函数结束或返回时,Windows应用程序结束。
int WINAPI WinMain(
HINSTANCE hInstance,
//当前运行实例的句柄
HINSTANCE hPrevInstance,
// 当前实例的前一个实例的句柄,在win32环境下,这个参数总是NULL
LPSTR lpCmdLine,
//以空终止的字符串,指定传递给应用程序的命令行参数。
int nCmdShow
//指定程序的窗口应该如何显示,例如最大化、最小化、隐藏等。这个参数的值由该程序的调用者所指定,应用程序通常不需要理会这个参数的值。
);
7.窗口的创建过程
1.设计一个窗口类;
2.注册窗口类;
3.创建窗口;
4.显示及更新窗口;
8.窗口类
typedef struct _WNDCLASS {
UINT style;
//指定这一类型窗口的样式,CS_HREDRAW水平重画,CS_VREDRAW垂直重画,CS_NOCLOSE 禁用Close命令,这将导致窗口没有关闭按钮。CS_DBLCLKS当用户在窗口中双击时,向窗口过程发送鼠标双击消息。CS(Class Style)
WNDPROC lpfnWndProc;
//函数指针,指向窗口过程函数,窗口过程函数就是一个回调函数。一个Windows程序可以包包含多个窗口过程函数,一个窗口过程总是与某一个特定的窗口相关联(通过本成员变量指定),基于该窗口类创建的窗口使用同一窗口过程。
int cbClsExtra;
//可以为窗口类分配一个附加的内存空间,由属于这种窗口类的所有窗口所共享,我们一般将这个参数设置为0。
int cbWndExtra;
//为窗口分配一个附加内存空间,应用程序可以用这部分内存存储窗口的特有的数据。初始化一般为0。如果应用程序用WNDCLASS结构注册对话框(用资源文件中的CLASS伪指令创建)必须给DLGWINDOWEXTRA设置这个成员。
HINSTANCE hInstance;
//指令包含窗口过程的程序的实例句柄。
HICON hIcon;
//指定窗口类的图标句柄。如果这个成员为NULL,那么系统提供一个默认的图标。在为hIcon变量赋值时可以调用LoadIcon函数加载一个图标资源,返回一个图标句柄。
HCURSOR hCursor;
//为光标变量赋值时,可以用LoadCusrsor加载一个光标资源,返回光标句柄。用法同LoadIcon
HBRUSH hbrBackground;
//指定窗口类的画刷句柄,当窗口发生重绘时,系统使用这里指定的画刷来擦除窗口背景。,既可以指定画刷句柄,也可以指定标准系统颜色值。GetStockObject得到系统标准刷。还可以获取画笔、字体和调色板句柄,因为反回多种资源对像句柄,使用时要根据具体情况,强制转换。
LPCTSTR lpszMenuName;
//指定菜单资源的名字。如果为NULL那么基于这个窗口类创建的窗口将没有菜单。
LPCTSTR lpszClassName;
//指定窗口类的名字。
} WNDCLASS;
9.LoadIcon
HICON LoadIcon(HINSTANCE hInstance,LPCTSTR lpIconName)
如果加载的是系统的标准图标,第一个参数为NULL。
10.回调函数
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应。
11.回调函数实现机制
1.创建回调函数
2.函数实现一方在初始化时,将回调函数的指外针注册给调用者。
3.当特定的事件或条件发生时,调用函数通过函数指针调用函数对事件进行处理。
12.注册窗口类
RegisterClass(CONST WNDCLASS *lpWndClass)
13.创建窗口
HWND CreateWindow(
LPCTSTR lpClassName,
//指定一个注册过的窗口类的名字
LPCTSTR lpWindowName,
//指定一个窗口名字,如果有窗口标题栏,窗口名字将显示在标题栏上
DWORD dwStyle,
//指定创建窗口的样式。与窗口类中的style不同,这里的样式,指的是特定某一窗口的样式,窗口类中的样式基于该窗口类创建的窗口都有该样式。
int x, // horizontal position of window
//如果被设为CW_USEDEFAULT将默认左上角坐标,忽略y坐标。
int y, // vertical position of window
int nWidth, // window width
//如果被设为CW_USEDEFAULT将默认窗品宽度,并忽略长度。
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
//子窗口必须有WS_CHILD样式。对父窗口的操作影响子窗口。
HMENU hMenu, // menu handle or child identifier
//指定窗口菜单句柄。
HINSTANCE hInstance, // handle to application instance
//指定窗口所属应用程序实例句柄
LPVOID lpParam // window-creation data
//做为WM_CREATE消息的附加参数lParam传入的数据指针。创建多文档界面的客户窗口时,lpParam必须指向CLIENTCREATESTRUCT结构体。多数窗口将这个参数设置为NULL。
);
14.显示窗口
调用ShowWindow显示窗口
BOOL ShowWindow(
HWND hWnd,
//调用CreateWindow时返回的句柄
int nCmdShow
//指定窗口显示状态,常用有如下
SW_HIDE:隐藏并激活其他窗口
SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。
SW_SHOWMAXIMIZED:激活窗口并将其最大化显示。
SW_SHOWMINIMIZED:激活窗口并将其最小化显示。
SW_SHOWNORMAL:激活并显示窗口。如果窗口最小化或或最大化状态,系统将其恢复到原来的尺寸和大小。
);
15.更新窗口
调用ShowWindow后紧接着调用UpdateWindow来刷新窗口
BOOL UpdateWindow(
HWND hWnd
);
//此处也是CreateWindows返回的窗口句柄
UpdateWindow函数通过发送一个WM_PAINT消息来刷新窗口。UpdateWindow将WM_PAINT消息直接发送给了窗口过程函数进行处理。此消息并没有放进消息队列。
16.消息循环
在创建窗口,显示窗口,更新窗口后,需要一个消息循环,不断地从消息队列中取出消息,并进行响应。
BOOL GetMessage(
LPMSG lpMsg, // message information
//指向一个消息结构体,GetMessage从线程的消息队列中取出的消息将保存在该结构体对象中。
HWND hWnd, // handle to window
//指定接收属于哪一窗口的消息。通常设置成NULL,用于接收属于调用线程的所有窗口的窗口消息。
UINT wMsgFilterMin, // first message
//指定获取消息的最小值,通常设置为0。
UINT wMsgFilterMax // last message
//获取的消息的最大值。如果都设置为0将获取所有消息。
);
GetMessage函数接收到除WM_QUIT外的消息均返回非零值。对于WM_QUIT消息,该函数返回零。如果出现错误,如果返回-1。
BOOL PeekMessage(
LPMSG lpMsg, // message information
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal options
);
另一种从消息队列中获取消息的方法,唯一不同的是最后多一个参数,当参数是PM_NOREMOVE时,消息不会从消息队列中移除,PM_REMOVE时跟GetMessage一样
发送消息可以使用SendMessage和PostMessage函数。SendMessage将消息发送给窗口,并调用该窗口过程进行处理。在窗口过程对消息处理完毕后,该函数才返回。(SendMessage为不进队消息)。PostMessage函数将消息放入与创建窗口的线程相关联的消息队列后立即返回。PostThreadMessage函数用于向线程发送消息。
17.窗口过程函数
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
18.DC
设备描述表,Device Context简称DC。DC是一个包含设备(物理输出设备,如显示器,以及设备驱动程序)信息的结构体,在Windows平台下,所有的图形操作都是利用DC来完成的。比如要得到与窗口相关联的DC句柄 HDC hdc=GetDC(hWnd);
19.WM_PAINT
当窗口客户区的一部分或者全部变为”无效时”系统会发送WM_PAINT消息,如果窗口从无到有、改变尺寸、最小化后在恢复、被其它窗口遮盖后再显示时。窗口大小变化是否会发生重绘也取决于在窗口类设计时style是否不水平重绘和垂直重绘。
在响应WM_PAINT消息代码中,要得到窗口的DC,必须调用BeginPaint函数。BeginPaint函数也只能在WM_PAINT消息响应代码中使用,在其他地方,只能使用GetDC。另代,BeginPaint函数得到的DC必须用EndPaint函数匀释放。
20.PostQuitMessage
投放一个WM_QUIT消息到消息队列中函数参数将做为WM_QUIT的wParam一般用做WinMain函数的返回值,GetMessage接到WM_QUIT消息时返回0消息循环结束程序退出。
21.DefWindowProc
此函数调用默认窗口过程,对应用程序没有处理的消息进行默认处理。在编写窗口过程时一般放在default里