本书使用C语言编写Windows程序,因此需要搭建C语言开发环境,使用Visual Studio作为C语言的开发工具。
本节必须掌握的知识点:
第8练:Windows程序模型
第9练:注册窗口类
第10练:创建、显示和更新窗口
第11练:消息循环
第12练:窗口过程
第13练:处理WM_PAINT消息
第14练:处理WM_DESTROY消息
第15练:处理WM_CLOSE消息
第16练:处理WM_SIZE消息
第17练:处理WM_CHAR消息
第18练:处理WM_LBUTTONDOWN消息
2.2.1 第8练:Windows程序模型
/*------------------------------------------------------------------------
008 编程达人win32 API每日一练
第8个例子HELLOWIN.C:Windows程序模型
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //窗口过程-消息回调函数
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow) //PSTR非宽字符串指针char*
{
static TCHAR szAppName[] = TEXT ("HelloWin") ; // 窗口类名
HWND hwnd ; //窗口句柄
MSG msg ; //消息 结构变量-F1
WNDCLASS wndclass ; //窗口类结构变量
//第一步:初始化窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口类风格-水平和垂直标识符-被
//改变时该类所有窗口重绘
wndclass.lpfnWndProc = WndProc ; //窗口处理函数---函数指针
wndclass.cbClsExtra = 0 ; //窗口扩展,cb表示字节数
wndclass.cbWndExtra = 0 ; //窗口实例扩展
wndclass.hInstance = hInstance ; //进程实例句柄
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; //窗口的最小化图标
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; //窗口鼠标光标
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);//背景色
wndclass.lpszMenuName = NULL ; //窗口菜单
wndclass.lpszClassName = szAppName ; // 窗口类名
//第二步:注册一个窗口类
if (!RegisterClass (&wndclass))
{
MessageBox(NULL, TEXT("注册失败!"),szAppName, MB_ICONERROR);
return 0;
}
//第三步:创建窗口
hwnd = CreateWindow( szAppName, // 窗口类名
TEXT ("创建窗口"), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, // 窗口的初始水平位置
CW_USEDEFAULT, // 窗口的初始垂直位置
CW_USEDEFAULT, // 窗口的宽度(以设备单位为单位)
CW_USEDEFAULT, // 窗口的高度(以设备单位为单位)
NULL, // 父窗口句柄
NULL, // 窗口菜单句柄
hInstance, // 程序实例句柄
NULL) ; // 创建参数
//第四步显示和更新窗口
ShowWindow (hwnd, iCmdShow) ; //显示窗口
UpdateWindow (hwnd) ; //更新窗口
//第五步:消息循环
while (GetMessage (&msg, NULL, 0, 0)) //消息队列中获取消息,message为
//WM_QUIT则返回0,退出消息循环
{
//将msg结构返还windows,并将虚拟键消息转换为字符消息
TranslateMessage(&msg);
//再次将msg结构返还windows,分发一个消息给窗口程序
DispatchMessage(&msg);
}
//msg.wParam 来自一条表示退出的消息,返回这个值给系统,从而退出
return msg.wParam ;
}
//窗口过程
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM Param)
{
HDC hdc ; //外部环境句柄(设备上下文句柄)
PAINTSTRUCT ps ; //用于绘制该应用程序拥有的窗口的客户区
RECT rect ; //RECT结构通过其左上角和右下角的坐标定义一个矩形
switch (message)
{
case WM_CREATE: //创建窗口消息
return 0 ;
case WM_PAINT: //重绘窗口消息
hdc = BeginPaint (hwnd, &ps);//开始画图(获取设备环境)-本例为显示器
GetClientRect (hwnd, &rect) ; //该函数获取窗口客户区的大小
DrawText (hdc, TEXT ("Hello, Windows!"), -1, &rect, //-1表示零字符串
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; //绘制格式化:在指定
//的矩形文本,水平垂直居中单行显示
EndPaint (hwnd, &ps) ;//标记指定窗口绘图的结束
return 0 ;
case WM_DESTROY: //退出消息循环
PostQuitMessage (0) ; //向系统指示线程已请求终止(退出)
return 0 ;
}
//调用操作系统默认窗口过程为应用程序未处理的任何窗口消息提供默认处理
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
运行结果:【注】图标、标题和系统菜单都位于标题栏中,标题栏以下区域为客户区。
图2-1 创建窗口
在HELLOWIN.C程序的功能是绘制一个自定义的窗口,并在窗口客户区输出文本字符串“Hello,Windows!”。
程序分为两个部分:主程序WinMain和窗口过程WndProc。
■主程序
●Winmain函数
Winmain函数是Windows窗口程序的入口函数,而控制台程序的入口函数是main函数。当创建项目时,选择的是桌面应用程序,因此“项目属性->链接器->系统->子系统”选项默认为“窗口”,如图。当然如果该选项设置为“未设置”,链接器会根据函数名“Winmain”来判断是窗口程序。
2-2 VS窗口子系统选项
WinMain函数原型:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine,int iCmdShow);
也可以是:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow);
函数返回值类型为:int类型。返回值为0表示正常结束,非0为非正常结束。
函数调用约定为:WINAPI,即stdcall调用约定。
参数1 HINSTANCE hInstance:为当前进程句柄,创建并初始化进程时由操作系统给定。
参数2 HINSTANCE hPrevInstance:前一个进程句柄,这是Win16系统遗留的参数。Win16系统为16位单任务实模式操作系统,该参数用于判断前一个进程是否已经结束,只有前一个进程结束,才可以创建当前进程。
参数3 PSTR szCmdLine:是一个长指针,指向一个NULL结尾的字符串,这个字符串中包含了命令行参数。
参数4 int iCmdShow:一个整数值,决定主窗口如何显示。它可以是如下一些常量:
SW_HIDE:隐藏窗口,并激活另一个窗口。
SW_MAXIMIZE:最大化窗口,并激活它。
SW_MINIMIZE:最小化窗口,并激活下一个顶级窗口。
SW_RESTORE:激活并显示窗口。如果窗口是最小化或最大化,Windows会恢复其原始大小和位置。
SW_SHOW:在窗口的当前大小和位置下展示并激活它。
SW_SHOWDEFAULT:根据启动应用程序时传递给CreateProcess函数的STARTUPINFO结构体的wShowWindowfield来设置窗口的首次显示状态。
SW_SHOWMAXIMIZED:最大化窗口,并激活它。
SW_SHOWMINIMIZED:最小化窗口,并激活它。
SW_SHOWMINNOACTIVE:最小化窗口,但不激活它。
SW_SHOWNA:以窗口的当前状态显示窗口,但不把它激活。
SW_SHOWNOACTIVATE:以最近的大小和位置显示窗口,但不把它激活。
SW_SHOWNORMAL:和 SW_RESTORE 类似,根据窗口的默认位置和大小来显示并激活它。
默认是正常大小显示窗口。
WinMain函数中需要定义4个变量:
static TCHAR szAppName[] = TEXT ("HelloWin") ;定义一个窗口类名,通常也是进程的名称。
HWND hwnd ;定义一个窗口句柄,通过句柄对窗口进行操作。
MSG msg ; 定义一个消息结构变量。
WNDCLASS wndclass ; 定义一个窗口类结构变量。在上一节中已经解释过窗口类,不再赘述。
如果我们想查看相关的帮助信息,可以选中后按F1键,查阅MSDN相关信息。也可以选中后点击鼠标右键,速揽定义或转到定义,VS操作非常方便。
●主程序创建窗口的五个步骤:
步骤一:初始化窗口类。如果要创建一个自定义的窗口,必须要明确该窗口的相关信息,并传递给操作系统。简单点说就是初始化窗口类结构体的各个字段了。如果不需要或者没有的字段可以设置为NULL。但是,窗口风格、窗口过程和窗口类名这几个字段是必须要有的。
步骤二:注册一个窗口类。当我们初始化一个新的窗口类之后,需要将窗口类向Winodws系统注册生效,调用API函数RegisterClass。
步骤三:调用API函数CreateWindow创建窗口。
步骤四:显示和更新窗口。
ShowWindow (hwnd, iCmdShow) ; //显示窗口
UpdateWindow (hwnd) ; //更新窗口
步骤五:消息循环。
//消息队列中获取消息,message为WM_QUIT则返回0,退出消息循环
while (GetMessage (&msg, NULL, 0, 0))
{ //将msg结构返还windows,并将虚拟键消息转换为字符消息
TranslateMessage(&msg);
//再次将msg结构返还windows,分发一个消息给窗口程序
DispatchMessage(&msg);
}
消息循环是一个While循环结构,调用GetMessage函数从窗口消息队列中获取消息,如果message为WM_QUIT则返回0,退出消息循环。否则交给TranslateMessage函数和DispatchMessage函数处理。
主程序创建窗口的五个步骤是必须的,而且是固定的,可以作为Windows程序的模板使用。多数情况下直接拷贝使用就可以了。如果要增加子窗口或者处理控件消息或菜单快捷键消息时,稍微改一下就可以了。Windows程序设计的基础要比看起来要简单的多。
■窗口过程
窗口过程负责处理Windows系统发送给窗口的各种消息。
LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam);
我们会发现,窗口过程的四个参数就是MSG消息结构的前4个字段。这是Windows操作系统传递的消息参数。
参数hwnd:当前窗口的句柄。用于识别窗口或应用程序子窗口的唯一身份标识。
参数message:描述了窗口所需要处理的消息类型,例如WM_PAINT(需要重绘窗口)、WM_DESTROY(窗口将要销毁)等。
参数wParam:是消息特定的附加信息。其内容会根据具体的message类型而变化。
参数lParam:也是消息特定的附加信息。其内容会根据具体的message类型而变化。
返回值LRESULT则是消息处理后的返回信息,该返回信息根据消息类型各不相同。
每当有事件发生(如鼠标点击、键盘按键、计时器事件等)时,Windows会向相应的窗口发送一个消息(message),并通过WndProc函数进行消息的处理。
对于大部分消息,一旦WndProc处理了这些消息,它就应该返回0。对于某些消息,如WM_CREATE和WM_PAINT,返回的值可能包含某些信息。所有的这些信息都在每个消息的具体说明中有详细的解释。
如果WndProc没有处理某个消息,那么应该调用Windows系统默认的窗口过程DefWindowProc函数,让系统按照默认的方式处理。绝大多数消息都是由DefWindowProc函数处理的。
WndProc的调用约定是CALLBACK,和WINAPI相同,同样是stdcall调用约定。之所以使用CALLBACK,顾名思义,WndProc是一个回调函数,即Windows系统调用WndProc来处理各种消息。
WndProc窗口过程主要包含一个switch结构,根据message消息ID的不同分别进行处理,处理完之后,直接return 0 ;返回。没有处理的其他消息则交给DefWindowProc按照Windows系统默认方式处理。
接下来我们将这个模板程序进行分解,一步一步实现窗口的创建过程,以便于我们分析窗口实现的过程。
2.2.2 第9练:注册窗口类
/*------------------------------------------------------------------------
009 编程达人win32 API每日一练
第9个例子RegisterClass.c:注册窗口类
WNDCLASS结构
RegisterClass 函数
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
/*****************************回调函数*****************************************
LRESULT:返回值,被宏定义为long型
CALLBACK:函数参数进栈顺序(从右到左,反汇编求证)---_stdcall调用约定
4个参数:为MSG结构体的前4个成员变量 ******************************************************************************/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 *****************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{ // 窗口类名---一个以\0结尾字符串数组,用来保存程序名称,
//即注册到操作系统的名称,一般为进程名称!
static TCHAR szAppName[] = TEXT("HelloWin");
//***********************************1、设计窗口类*****************************
WNDCLASS wndclass; //定义窗口变量
wndclass.style = CS_HREDRAW | CS_VREDRAW; // 窗口类型:水平、垂直重绘,即改变窗口大小时自动发送WM_PAINT消息,而不必手动发送。被改变时该类所有窗口重绘
wndclass.lpfnWndProc = WndProc; //窗口处理函数---回调函数的地址
wndclass.cbClsExtra = 0; //窗口扩展:预留空间的附加值,此程序没用到
wndclass.cbWndExtra = 0; //窗口实例扩展:预留空间的附加值,此程序没用到
wndclass.hInstance = hInstance; //应用程序实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //装载窗口标题栏图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //装载窗口鼠标
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//背景白色
wndclass.lpszMenuName = NULL; //装载窗口菜单,此处程序没有
wndclass.lpszClassName = szAppName; // 窗口类名---程序的名字
//******************************2、注册窗口类*****************************
if (RegisterClass(&wndclass)) //成功注册一个窗口类
{
MessageBox(NULL, TEXT("爱达人---成功注册一个窗口类!"),
szAppName, MB_ICONEXCLAMATION);
}
return 0;
}
//**********************回调函数——消息处理过程******************************
//窗口回调函数,此函数只有声明和定义,没有调用!这说明此函数确实是由操作系统调用的!
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0; //未处理任何窗口消息
}
/*
窗口总是基于窗口类创建的。窗口类确定了处理窗口消息的窗口过程。
在创建应用窗口之前,必须调用函数RegisterClass来注册窗口类。
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
本例中未作任何消息处理,后面的例题中将会详细介绍
*******************************************************************
WNDCLASS结构:
typedef struct
{
UINT style ;
WNDPROC lpfnWndProc ;
int cbClsExtra ;
int cbWndExtra ;
HINSTANCE hInstance ;
HICON hIcon ;
HCURSOR hCursor ;
HBRUSH hbrBackground ;
LPCTSTR lpszMenuName ; //LPCWSTR (Unicode版本)
LPCTSTR lpszClassName ; //LPCWSTR (Unicode版本)
}
WNDCLASS, * PWNDCLASS ;
*******************************************************************************
RegisterClass 函数:注册一个窗口类,供以后在对CreateWindow或CreateWindowEx函数的调用中使用。
【注意】如果使用reateWindowEx函数创建创建,相应的需要使用RegisterClassEx注册窗口。但是,如果不需要设置类小图标,则仍然可以使用CreateWindow函数和RegisterClass函数。
ATOM RegisterClassA(
const WNDCLASSA *lpWndClass //指向WNDCLASS结构的指针。在将结构传递给函数之前,必须用适当的类属性填充该结构。
);
返回值编辑
ATOM类型为Windows中定义的新数据类型,其即unsigned short类型,在<WinDef.h>中的定义如下:
typedef WORD ATOM;
typedef unsigned short WORD;
如果函数成功,返回值是唯一标识已注册的类的一个原子;如果函数失败,返回值为0。若想获得更多错误信息,请调用GetLastError函数。
*/
运行结果:注册成功后,返回值非0,弹出一个对话框。
图2-3 注册窗口类
毫无疑问,仅仅只是注册窗口类是无法完成窗口队的创建。
【注意】程序中的窗口过程WndProc并未做任何事情,直接返回操作系统。让我们继续进行下一步。
2.2.3 第10练:创建、显示和更新窗口
/*------------------------------------------------------------------------
010 编程达人win32 API每日一练
第10个例子CreateWindow.c:创建、显示和更新窗口---此例尚不能显示窗口
CreateWindow函数
ShowWindow函数
UpdateWindow函数
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
/****************************回调函数*****************************************/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数
/*************************程序入口 ******************************************/
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名
//***********************************1、设计窗口类*****************************
WNDCLASS wndclass; //定义窗口类结构变量
wndclass.style = CS_HREDRAW | CS_VREDRAW; // 窗口风格
wndclass.lpfnWndProc = WndProc; //窗口过程---回调函数的地址
wndclass.cbClsExtra = 0; //窗口扩展:预留空间的附加值,此程序没用到
wndclass.cbWndExtra = 0; //窗口实例扩展:预留空间的附加值,此程序没用到
wndclass.hInstance = hInstance; //应用程序实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //装载窗口标题栏图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //装载窗口鼠标
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //初始化窗体背景(白色画刷)
wndclass.lpszMenuName = NULL; //装载窗口菜单(此处程序没有菜单)
wndclass.lpszClassName = szAppName; // 窗口类名---程序名
/********************************2、注册窗口类***************************/
if (!RegisterClass(&wndclass)) //返回值为0,未成功注册
{
MessageBox(NULL, TEXT("注册失败!"),
szAppName, MB_ICONEXCLAMATION);
return 0;
}
/*****************************3、创建窗口********************************/
HWND hwnd;
hwnd = CreateWindow(szAppName, //已注册的窗口类名
TEXT("我的第一个窗口程序!"),//窗体标题
WS_OVERLAPPEDWINDOW, //窗体风格(重叠窗口、标题栏和边框
CW_USEDEFAULT, //窗体左上角x坐标
CW_USEDEFAULT, //窗体左上角y坐标
CW_USEDEFAULT, //窗口的宽度
CW_USEDEFAULT, //窗口的高度
NULL, //父窗口句柄,此处NULL
NULL, //窗口菜单句柄,此处NULL
hInstance,//应用程序句柄
NULL //指向一个值的指针,该值传递给窗口WM_CREATE消息
);
/*创建窗口完成(只存在于内存中),系统会发送第一条消息WM_CREATE,注意此时,程序并未执行到消息循环处,操作系统绕过应用程序的消息队列,直接将WM_CREATE消息发送给窗口过程。该消息在所有的消息之前,目的是为了在程序程序执行之前可以有机会进行一些初始化工作或装载动态链接库等操作。
A) CreateWindow 的第一个参数就是窗口类名,通过这个名字可以找到刚才注册的窗口类,然后再根据它来创建窗口。
B) 显示器上的坐标与数学中的不同,显示器的左上角是坐标原点,从原点向右是x轴,向下是y轴,都是正坐标,没有负数。
C) 参数 hInstance 是通过主函数 WinMain 传入的。
【注意】通过 CreateWindows() 函数创建窗口后,仅仅是为窗口分配了内存空间,获得了句柄,但窗口并没有显示出来,所以还需要调用 ShowWindow() 函数来显示窗口。
*/
/**********************************4、显示窗口和更新窗口**********************/
ShowWindow(hwnd, nCmdShow); //发送WM_SIZE和WM_SHOWWINDOW消息送入消息队列
/*1、单纯一个ShowWindow,照样会正确绘制出窗口内容,只不过是在消息队列取空之后才绘制的,有时我们希望窗口被立即重画,而不是去等待那个不确定的消息队列,此时就需要用到UpdateWindow。UpdateWindow的作用只有一个:假若当前存在被标记为重绘的区域(即无效区域,如果其不存在的话它什么也不做),那么立刻让Windows使用SendMessage的方式来对你的窗口发送WM_PAINT。
2、ShowWindow本身并不发送重绘消息,它的作用仅仅是把窗口显示出来。
所以还需要调用 UpdateWindow() 函数,生成 VM_PAINT 消息,将客户区中的各种控件绘制出来。不过,当窗口显示的时候,Windows会自动探测窗口的内容是否需要重绘、以及需要重绘的区域。
3、ShowWindow调用中会发送WM_SIZE和WM_SHOWWINDOW消息给窗口过程。
4、第一次调用和以后的调用是有区别的。第一次调用时,它的输入参数 nCmdShow 是需要输入WinMain 函数里传入来的nCmdShow 参数,而不能是其它参数。在此以后就不再需要nCmdShow 参数了。
*/
UpdateWindow(hwnd); //发送WM_PAINT消息给窗口过程
/*执行UpdateWindow时会立即发送一条WM_PAINT消息,同样由操作系统直接发出。UpdateWindow执行时,先判断无效区域是否为空,不为空,则发送WM_PAINT消息,为空为不发送。可见,将上面的ShowWindow注释掉,则不发送。因为未显示出来,当然没有无效区域,即无效区域为空。
*/
/*UpdateWindow和Invalidate的区别:
1、UpdateWindow()的作用是使窗口立即重绘(因为操作系统绕过应用程序的消息队列,直接给窗口过程发WM_PAINT消息,从而导致窗口立即重绘。
而Invalidate等函数(功能为使整个客户区无效)调用后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。
2、UpdateWindow执行时,先判断无效区域是否为空,不为空才发送消息,Invalidate的无效区为整个客户区(注意与InvalidRect函数(功能为使指定的区域无效)的区别)
*/
return 0;
}
//*****************************回调函数——消息处理过程***********************
//窗口过程回调函数,这里只有声明和定义,直接返回!这说明此函数确实是由操作系统调用的!
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0; //未处理任何窗口消息
}
/*****************************************************************************
CreateWindow函数:创建一个重叠窗口,弹出窗口或子窗口。它指定窗口类,窗口标题,窗口样式,以及(可选)窗口的初始位置和大小。
该函数还指定窗口的父级或所有者(如果有)以及窗口的菜单。
要使用CreateWindow支持的样式以及扩展窗口样式,请使用CreateWindowEx函数。
void CreateWindowA(
lpClassName,//窗口的类名
lpWindowName,//窗体标题名称
dwStyle, //指定创建窗口的风格。
x, //窗体左上角x坐标
y, //窗体左上角y坐标
nWidth, //窗口的宽度
nHeight, //窗口的高度
hWndParent, //父窗口句柄
hMenu, //窗口菜单句柄
hInstance, //应用程序句柄
lpParam //指向一个值的指针,该值传递给窗口WM_CREATE消息。
);
*****************************************************************************
ShowWindow函数 :该函数设置指定窗口的显示状态。
BOOL ShowWindow(
HWND hWnd, //窗口的句柄。
int nCmdShow //窗口显示的状态
);
返回值:如果窗口之前可见,则返回值为非零。如果窗口之前被隐藏,则返回值为零。
**************************************************************************
UpdateWindow函数:如果窗口的更新区域不为空,则UpdateWindow函数通过向该窗口过程发送WM_PAINT消息来更新指定窗口的客户区。
该函数绕过应用程序消息队列,直接将WM_PAINT消息发送到指定窗口的窗口过程。如果更新区域为空,则不发送消息。
UpdateWindow(
HWND hWnd //要更新的窗口句柄
);
*/
总结
1.细心的读者可能会注意到,我们在初始化窗口类时已经给出了窗口风格,为何在调用CreateWindow函数创建窗口时还要再次给出窗口风格呢?
这是因为,一个窗口类可以创建多个窗口,每个窗口都可以有属于自己的独特风格,这样设计更灵活。
2.还有一个重复项,初始化窗口类时给定了窗口菜单,CreateWindow函数同样也有一个参数是菜单句柄。道理是相同的,如果该窗口类只创建了一个窗口,绑定菜单可以在初始化窗口类时进行,也可以在调用CreateWindow时进行。假设一个窗口类需要创建多个窗口时,则每个窗口都可以绑定不同的菜单。
3.虽然此例程已经创建窗口、显示窗口和更新窗口,但新创建的窗口仅存在于内存中,并未真正显示出来。执行CreateWindow函数后,会产生程序的第一个消息WM_CREATE。只有当Windows系统将WM_CREATE消息直接发送给窗口过程WndProc并处理之后,才会真正在屏幕显示窗口。
4.【注意】注释中解释了显示窗口的四个不同函数的区别:ShowWindow、UpdateWindow、Invalidate、InvalidRect。其中执行ShowWindow函数会产生两个消息WM_SIZE和WM_SHOWWINDOW并送入窗口消息队列。执行UpdateWindow函数会产生WM_PAINT消息无需送入窗口消息队列,而是直接交给窗口过程,立即更新窗口。
【注】此时,由于窗口过程尚未处理任何消息,屏幕仍无法显示窗口。