MFC-第一节 源生C语言创建一个窗口
想要开发能够在windows平台上运行的应用,那么就要使用windows系统提供的API windows系统提供的api都是纯C语言的 直接用windows的api创建一个窗口,就需要大量的代码,耗时耗力 因此微软采用C++面向对象的思想把纯C语言的windows API封装成面向对象的形式,这样简化使用—-这个封装好的东西就叫MFC。 本节主要介绍,不是用MFC,即直接使用windows API如何创建一个窗口
为什么学习MFC ?
- 如果你是在Windows平台上做GUI开发,MFC(微软基础类库)是一个很好的选择,毕竟Windows累积用户群庞大,市场接受程度高
- GUI: 图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面。
- 与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受
windows的消息机制
- 要想熟练掌握 Windows 应用程序的开发, 首先需要理解 Windows 平台下程序运行的内部机制。
- 如果想要更好的学习掌握 MFC,必须要先了解Windows 程序的内部运行机制
基本概念解释
- 我们在编写标准C程序的时候,经常会调用各种库函数来辅助完成某些功能:初学者使用得最多的C库函数就是printf了,这些库函数是由你所使用的编译器厂商提供的。
- 在Windows平台下,也有类似的函数可供调用:不同的是,这些函数是由Windows操作系统本身提供的。
SDK和API
- SDK: 软件开发工具包(Software Development Kit),一般都是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。
- API函数:
- Windows操作系统提供给应用程序编程的接口(Application Programming Interface)。
- Windows应用程序API函数是通过C语言实现的,所有主要的 Windows 函数都在 Windows.h 头文件中进行了声明。
- Windows 操作系统提供了 1000 多种 API函数。
窗口和句柄
- 窗口:
- 窗口是 Windows 应用程序中一个非常重要的元素,一个 Windows 应用程序至少要有一个窗口,称为主窗口。
- 窗口是屏幕上的一块矩形区域,是 Windows 应用程序与用户进行交互的接口。利用窗口可以接收用户的输入、以及显示输出。
- 一个应用程序窗口通常都包含标题栏、菜单栏、系统菜单、最小化框、最大化框、 可调边框,有的还有滚动条。如下图:
- 窗口可以分为客户区和非客户区, 如上图。
- 客户区是窗口的一部分, 应用程序通常在客户区中显示文字或者绘制图形。
- 标题栏、 菜单栏、 系统菜单、 最小化框和最大化框、 可调边框统称为窗口的非客户区, 它们由 Windows 系统来管理, 而应用程序则主要管理客户区的外观及操作。
- 窗口可以有一个父窗口, 有父窗口的窗口称为子窗口。除了上图所示类型的窗口外, 对话框和消息框也是一种窗口。 在对话框上通常还包含许多子窗口, 这些子窗口的形式有按钮、 单选按钮、 复选框、 组框、 文本编辑框等。(万物皆窗口)
- 句柄
-
在 Windows 应用程序中, 窗口是通过窗口句柄( HWND) 来标识的。 我们要对某个窗口进行操作, 首先就要得到这个窗口的句柄。
-
句柄( HANDLE) 是 Windows 程序中一个重要的概念, 使用也非常频繁。(资源号)
- 在 Windows 程序中, 有各种各样的资源( 窗口、 图标、光标,画刷等),系统在创建这些资源时会为它们分配内存, 并返回标识这些资源的标识号, 即句柄。
- 在后面的内容中我们还会看到图标句柄( HICON)、 光标句柄( HCURSOR) 和画刷句柄( HBRUSH)。
-
消息与消息队列
- Windows 程序设计是一种完全不同于传统的 DOS 方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要是基于消息的。
- 每一个 Windows 应用程序开始执行后, 系统都会为该程序创建一个消息队列, 这个消息队列用来存放该程序创建的窗口的消息。
- 例如,当用户在窗口中画图的时候,按下鼠标左键,此时,操作系统会感知到这一事件,于是将这个事件包装成一个消息,投递到应用程序的消息队列中,等待应用程序的处理。
- 然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应、分发给操作系统处理
- 操作系统处理过程中,也会给应用程序“ 发送消息”。所谓“ 发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。
- 如下图:
WinMain函数
- 当Windows操作系统启动一个程序时,它调用的就是该程序的WinMain函数( 实际是由插入到可执行文件中的启动代码调用的)。
- WinMain是Windows程序的入口点函数,与DOS程序的入口点函数main的作用相同,当WinMain 函数结束或返回时,Windows应用程序结束。
- 由于是在windows平台上开发,所以因该由windows规定入口
Windows 编程模型
- 一个完整的Win32程序(
#include <windows.h>
),该程序实现的功能是创建一个窗口,并在该窗口中响应键盘及鼠标消息,程序的实现步骤为:- WinMain函数的定义
- 创建一个窗口
- 进行消息循环
- 编写窗口过程函数
创建项目
- 打开vs2017->新建->项目
- 点击Visual C++ -> win32项目
- 重名项目名、选择路径->确定
- 选择windows应用程序、空项目(其他选项默认)->完成 (以上步骤,或者之间选择一个空项目即可,默认就是win32的)
- 添加源文件
- 右击源文件->新建项目->创建一个windows.c文件(因为底层是C语言的API)
WinMain函数的定义
int WINAPI WinMain(
HINSTANCE hInstance, //应用程序实例
HINSTANCE hPrevInstance, //上一个应用程序实例
LPSTR lpCmdLine, //命令行参数
int nShowCmd); //窗口显示的样式
- WINAPI:是一个宏,它代表的是__stdcall(注意是两个下划线),表示的是参数传递的顺序:从右往左入栈,同时在函数返回前自动清空堆栈。
- hInstance:表示该程序当前运行的实例的句柄,这是一个数值。当程序在Windows下运行时,它唯一标识运行中的实例(注意,只有运行中的程序实例, 才有实例句柄)。一个应用程序可以运行多个实例,每运行一个实例,系统都会给该实例分配一个句柄值,并通过hInstance参数传递给 WinMain 函数。
- 应用程序的实例句柄,相当于iOS的UIAPPlication对象
- hPrevInstance:表示当前实例的前一个实例的句柄。在Win32环境下,这个参数总是NULL,即在Win32环境下,这个参数不再起作用。
- lpCmdLine:是一个以空终止的字符串, 指定传递给应用程序的命令行参数,相当于C或C++中的main函数中的参数char *argv[]。
- nShowCmd:表示一个窗口的显示,表示它是要最大化显示、最小化显示、正常大小显示还是隐藏显示。
创建一个窗口
- 创建一个完整的窗口,需要经过下面几个步骤:
- 设计窗口类
- 注册窗口类
- 创建窗口类
- 显示和更新窗口
- 通过循环取消息
- 处理消息(窗口过程)
设计窗口类
- 一个完整的窗口具有许多特征, 包括光标(鼠标进入该窗口时的形状)、图标、背景色等。窗口的创建过程类似于汽车的制造过程。
- 我们在生产一个型号的汽车之前, 首先要对该型号的汽车进行设计, 在图纸上画出汽车的结构图, 设计各个零部件, 同时还要给该型号的汽车取一个响亮的名字, 例如“宝马 x6”。
- 类似地, 在创建一个窗口前, 也必须对该类型的窗口进行设计, 指定窗口的特征。在Windows中,窗口的特征就是由WNDCLASS结构体来定义的,我们只需给WNDCLASS结构体对应的成员赋值,即可完成窗口类的设计。
-
WNDCLASS结构体的定义如下:
typedef struct _WNDCLASS{ UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCWSTR lpszMenuName; LPCWSTR lpszClassName; } WNDCLASS;
-
tyle:指定窗口的样式(风格),常用的样式如下:
CS_HREDRAW : 当窗口水平方向上的宽度发生变化时, 将重新绘制整个窗口。 当窗口发生重绘时, 窗口中的文字和图形将被擦除。如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。 CS_VREDRAW: 当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,那么在垂直方向上调整窗口高度时,将不会重绘窗口。 CS_NOCLOSE: 禁用系统菜单的 Close 命令,这将导致窗口没有关闭按钮。 CS_DBLCLKS: 当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。
- lpfnWndProc:指定一个窗口回调函数,是一个函数的指针。
- 当应用程序收到给某一窗口的消息时,就应该调用某一函数来处理这条消息。
- 这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。
- 对于一条消息,操作系统调用的是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。
-
回调函数的定义形式如下:
LRESULT CALLBACK WindowProc( HWND hWnd, //信息所属的窗口句柄 UINT uMsg, //消息类型 WPARAM wParam, //附加信息(如键盘哪个键按下) LPARAM lParam //附加信息(如鼠标点击坐标) );
- cbClsExtra:类的附加内存,通常数情况下为0。
- cbWndExtra:窗口附加内存,通常情况下为0。
- hInstance:当前实例句柄,用WinMain中的形参hInstance为其赋值。
-
hIcon:指定窗口类的图标句柄,设置为NULL,则使用默认图标,也可用如下函数进行赋值:
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName); 如:LoadIcon(NULL, IDI_WARNING); //第一个参数为NULL,加载系统默认图标
-
hCursor:指定窗口类的光标句柄,设置为NULL,则使用默认图标,也可用如下函数进行赋值:
HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName); 如:LoadCursor(NULL, IDC_HELP); //第一个参数为NULL,加载系统默认光标
-
hbrBackground:指示窗口的背景颜色,可用如下函数进行赋值:
HGDIOBJ GetStockObject(int fnObject); 如:GetStockObject(WHITE_BRUSH);
- lpszMenuName:指定菜单资源的名字。如果设置为NULL,那么基于这个窗口类创建的窗口将没有默认菜单。
- lpszClassName:指定窗口类的名字。
-
注册窗口类
- 设计完窗口类(WNDCLASS)后, 需要调用RegisterClass函数对其进行注册,注册成功后,才可以创建该类型的窗口。
-
注册函数的原型声明如下:
ATOM RegisterClass(CONST WNDCLASS *lpWndClass); 使用示例:RegisterClass(&wc);
创建窗口类
- 设计好窗口类并且将其成功注册之后, 即可用CreateWindow函数产生这种类型的窗口了。
-
CreateWindow函数的原型声明如下:
HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
- lpClassName:指定窗口类的名称,此名字必须和WNDCLASS的lpszClassName成员指定的名称一样。
- lpWindowName:指定窗口的名字,即窗口的标题。
- dwStyle:指定创建的窗口的样式,常指定为指WS_OVERLAPPEDWINDOW类型,这是一种多种窗口类型的组合类型。
- x, y:指定窗口左上角的x,y坐标。如果参数x被设为CW_USEDEFAULT,那么系统为窗口选择默认的左上角坐标并忽略y参数。
- nWidth,nHeight:指定窗口窗口的宽度,高度。如果参数nWidth被设为 CW_USEDEFAULT,那么系统为窗口选择默认的宽度和高度,参数nHeight被忽略。
- hWndParent:指定被创建窗口的父窗口句柄,没有父窗口,则设置NULL。
- hMenu:指定窗口菜单的句柄,没有,则设置为NULL。
- hInstance:窗口所属的应用程序实例的句柄,用WinMain中的形参hInstance为其赋值。
- lpParam:作为WM_CREATE消息的附加参数lParam传入的数据指针。通常设置为NULL。
- 返回值说明:如果窗口创建成功,CreateWindow函数将返回系统为该窗口分配的句柄,否则,返回NULL。
-
示例代码:
HWND hWnd = CreateWindow( TEXT("MyWin"), //窗口类名字 TEXT("测试"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口风格 CW_USEDEFAULT, CW_USEDEFAULT, //窗口x,y坐标,使用默认值 CW_USEDEFAULT, CW_USEDEFAULT, //窗口宽度,高度,使用默认值 NULL, //无父窗口 NULL, //无菜单 hInstance, //应用程序实例句柄,为WinMain第1个形参 NULL); //附件信息,通常设置为NULL
显示和更新窗口
-
显示窗口函数原型:
BOOL ShowWindow(HWND hWnd, int nCmdShow);
-
更新窗口函数原型:
BOOL UpdateWindow(HWND hWnd);
-
示例代码
ShowWindow(hWnd, SW_SHOWNORMAL); //SW_SHOWNORMAL为普通模式 UpdateWindow(hWnd);
通过循环取消息
- 在创建窗口、显示窗口、更新窗口后,我们需要编写一个消息循环,不断地从消息队列中取出消息,并进行响应。
- 消息结构体
-
在Windows程序中,消息是由MSG结构体来表示的。MSG结构体的定义如下:
typedef struct tagMSG { HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG;
- hWnd:消息所属的窗口。我们通常开发的程序都是窗口应用程序,一个消息一般都是与某个窗口相关联的。例如,在某个活动窗口中按下鼠标左键,产生的按键消息就是发给该窗口的。
- message:消息的标识符,是由一个数值来表示的,不同的消息对应不同的数值。Windows将消息对应的数值定义为WM_XXX宏(WM是Windows Message的缩写)的形式, XXX对应某种消息的英文拼写的大写形式。例如,鼠标左键按下消息是WM_LBUTTONDOWN,键盘按下消息是WM_KEYDOWN,字符消息是 WM_CHAR……。
- wParam: 指定消息的附加信息,如键盘按下会触发WM_KEYDOWN消息,但是,具体按下哪个按键需要wParam区分。
- lParam:指定消息的附加信息,如鼠标左击会触发WM_LBUTTONDOWN消息,但是,具体点击的坐标需要lParam区分。
- time:标识一个消息产生时的时间。
- pt:表示产生这个消息时光标或鼠标的坐标。
-
- 取消息
-
要从消息队列中取出消息,我们需要调用GetMessage()函数,该函数的原型声明如下:
BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
- lpMsg:指向一个消息结构体(MSG),GetMessage从线程的消息队列中取出的消息信息将保存在该结构体变量中。
- hWnd:指定接收属于哪一个窗口的消息。通常我们将其设置为NULL,用于接收属于调用线程的所有窗口的窗口消息。
- wMsgFilterMin:指定消息的最小值。
- wMsgFilterMax:指定消息的最大值。如果wMsgFilterMin和wMsgFilterMax都设置为0, 则接收所有消息。
- 返回值说明:GetMessage函数接收到除 WM_QUIT 外的消息均返回非零值。对于WM_QUIT消息,该函数返回零。如果出现了错误,该函数返回-1,例如,当参数hWnd是无效的窗口句柄或lpMsg是无效的指针时。
-
-
建立消息循环
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
- TranslateMessage:用于翻译、处理和转换消息并把新消息投放到消息队列中,并且此过程不会影响原来的消息队列。
- DispatechMessage:用于把收到的消息传到窗口回调函数进行分析和处理。即将消息传递给操作系统,让操作系统调用窗口回调函数,来对信息进行处理。
- 消息处理机制
- 操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。
- 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage产生新的消息。
- 应用程序调用DispatchMessage,将消息回传给操作系统。消息是由 MSG结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此, DispatchMessage函数总能进行正确的传递。
-
系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。
处理消息(窗口过程)
- 在完成上述步骤后,剩下的工作就是编写一个窗口过程函数,用于处理发送给窗口的消息。
-
窗口过程函数的名字可以随便取, 如WinProc, 但函数定义的形式必须和下面声明的形式相同:
LRESULT CALLBACK WinProc( //CALLBACK 和WINAPI 作用一样 HWND hWnd, //信息所属的窗口句柄 UINT uMsg, //消息类型 WPARAM wParam, //附加信息(如键盘哪个键按下) LPARAM lParam //附加信息(如鼠标点击坐标) );
-
示例代码
LRESULT CALLBACK WinProc( HWND hWnd, //信息所属的窗口句柄 UINT uMsg, //消息类型 WPARAM wParam, //附加信息(如键盘按键) LPARAM lParam //附加信息(如鼠标点击坐标) ) { switch (uMsg) { case WM_KEYDOWN: //键盘按下 //…… break; case WM_LBUTTONDOWN: //鼠标右键按下 //…… break; case WM_PAINT: //绘图事件 //…… break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow(hWnd); break; default: //以windows默认方式处理 return DefWindowProc(hWnd, uMsg, wParam, lParam); break; } return 0; }
- DefWindowProc函数:DefWindowProc函数调用默认的窗口过程,对应用程序没有处理的其他消息提供默认处理。
- WM_CLOSE:对WM_CLOSE消息的响应并不是必须的,如果应用程序没有对该消息进行响应,系统将把这条消息传给DefWindowProc函数而 DefWindowProc函数则调用DestroyWindow函数来响应这条WM_CLOSE消息。
-
WM_DESTROY:DestroyWindow函数在销毁窗口后,会给窗口过程发送 WM_DESTROY消息,我们在该消息的响应代码中调用PostQuitMessage函数。
- PostQuitMessage函数向应用程序的消息队列中投递一条WM_QUIT消息并返回。WinMain函数中,GetMessage 函数只有在收到WM_QUIT消息时才返回0,此时消息循环才结束,程序退出。传递给 PostQuitMessage函数的参数值将作为WM_QUIT消息的wParam参数,这个值通常用做WinMain函数的返回值。
示例代码
#include<Windows.h>
//6. 处理消息(窗口过程) windowproc函数的实现
/*
CALLBACK: 代表__stdcall,代表参数传递的顺序,从右往左依次入栈,并且在函数返回前自动清空堆栈内容
*/
LRESULT CALLBACK windowProc(
HWND hWnd, //句柄,消息所处的窗口句柄
UINT uMsg,//具体的消息名称
WPARAM wParam,//键盘附加消息
LPARAM lParam //鼠标附加消息
) {
//处理消息
switch (uMsg)
{
case WM_CLOSE://关闭窗口
DestroyWindow(hWnd);//销毁当前窗口
break;
case WM_DESTROY://退出应用程序
//这个方法会让GetMessage返回值为0,那么就退出下面的while(1)循环了。
PostQuitMessage(0);//发送退出消息
break;
case WM_LBUTTONDOWN: //鼠标左键按下
{
int xpos = 0;
int ypos = 0;
xpos = LOWORD(lParam);
ypos = HIWORD(lParam);
//打印操作
TCHAR buf[1024];
wsprintf(buf, TEXT("x=%d,y=%d"), xpos, ypos);
//参数:所属窗口、显示内容、标题内容、关联按键类型
MessageBox(hWnd, buf, TEXT("鼠标左键按下"), MB_OK);
}
break;
case WM_KEYDOWN://键盘消息
MessageBox(hWnd, TEXT("键盘按下"), TEXT("键盘消息"), MB_OK);
break;
case WM_PAINT: //绘图消息
{
PAINTSTRUCT ps;//绘图结构体
//开始绘图
//hdc就相当于画家
HDC hdc = BeginPaint(hWnd,&ps);
//绘制文字
TextOut(hdc, 100, 100, TEXT("hello world !"), strlen("hello world !"));
EndPaint(hWnd, &ps);
}
break;
default:
break;
}
//默认的处理函数
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
/*
WINAPI:__stdcall的宏定义,代表参数传递的顺序,从右往左依次入栈,并且在函数返回前自动清空堆栈内容
winmain:函数入口
hInstance:应用程序的实例句柄,相当于iOS的UIAPPlication对象
hPrevInstance:前一个应用程序的实例句柄,后期没什么作用了,基本为null
lpCmdLine: 相当于argv[],命令行参数数组
nShowCmd: 窗口的显示命令,最大化、最小化
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
//实现底层窗口
//1. 设计窗口类
WNDCLASS wc;
wc.cbClsExtra = 0;//类额外的内存,通常设置为0;
wc.cbWndExtra = 0;//窗口额外的内存,通常也为0;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITENESS); //设置背景,必须设置
wc.hCursor = LoadCursor(NULL, IDC_HAND); //设置光标,如果第一个参数为NULL,代表使用系统提供的默认光标。
wc.hIcon = LoadIcon(NULL, IDI_WARNING); //设置图标
wc.hInstance = hInstance;//直接使用当前的实例句柄
wc.lpfnWndProc = windowProc; //窗口过程函数,即回调函数,可以先随便起个名
wc.lpszClassName = TEXT("WINDOW"); //窗口的名称
wc.lpszMenuName = NULL; //菜单名称,没有填NULL
wc.style = 0;//窗口风格:0位默认风格
//2. 注册窗口类
RegisterClass(&wc);
//3. 创建窗口类
/*
lpClassName :类名
lpWindowName:窗口名称
dwStyle: 显示风格
x, y :窗口坐标
nWidth: 窗口宽度 (显示时默认)
nHeight: 窗口高度(显示时默认)
hWndParent:父窗口
hMenu: 菜单
hInstance: 实例句柄
lpParam: 其他参数
*/
HWND wnd = CreateWindow(wc.lpszClassName, TEXT("TEXT WINDOW"), WS_OVERLAPPEDWINDOW, CWMO_DEFAULT, CWMO_DEFAULT, CWMO_DEFAULT, CWMO_DEFAULT, NULL, NULL, hInstance, NULL);
//4. 显示和更新窗口,SW_NORMAL:正常方式显示
ShowWindow(wnd, SW_NORMAL);
UpdateWindow(wnd);
//5. 通过循环取消息
/*
typedef struct tagMSG {
HWND hwnd; //主窗口
UINT message; //消息名称 WM_XXX
WPARAM wParam; //附加消息 键盘
LPARAM lParam; //附加消息 鼠标
DWORD time; //消息产生时间
POINT pt; //附加消息,鼠标,xy坐标
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
GetMessage参数:
_Out_ LPMSG lpMsg, //消息结构体MSG
_In_opt_ HWND hWnd, //要捕获消息的窗口,NULL:表示捕获所有窗口的消息
_In_ UINT wMsgFilterMin, //过滤的最小消息量
_In_ UINT wMsgFilterMax); //过滤的最大消息量 如果都为0,捕获所有消息
*/
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
/*BOOL isget= GetMessage(&msg, NULL, 0, 0);
if (!isget)
{
break;
}*/
//翻译消息
TranslateMessage(&msg);
//获取到消息,分发消息给window系统
DispatchMessage(&msg);
}
//6. 处理消息(窗口过程)
return 0;
}