五.注册窗口类
1.窗口类的概念:
<1>.窗口类包含了窗口各种参数信息的结构
<2>.每个窗口都有一个窗口类,基于窗口类创建窗口
<3>.每个窗口类都有一个名称,使用前必须注册到操作系统内核中
2.窗口的分类
<1>.系统窗口类
系统已经定义好的窗口类,所有应用程序都可以使用,例如Button(按钮),edit(编辑框)
<2>.应用程序全局窗口类
由用户自己定义,当前应用程序所有模块中都可以使用
<3>.应用程序局部窗口类
由用户自己定义,当前应用程序中只有本模块可以使用
3.注册窗口类函数
ATOM RegisterClassExW( [in] const WNDCLASSEXW *unnamedParam1 );
在这里带大家看一下WNDCLASS结构体:
typedef struct tagWNDCLASSA { UINT style; //窗口风格 WNDPROC lpfnWndProc; //窗口处理函数 int cbClsExtra; //窗口类的附加数据buff的大小 int cbWndExtra; //窗口类的附加数据buff的大小 HINSTANCE hInstance; //当前模块的实例句柄 HICON hIcon; //窗口图标句柄 HCURSOR hCursor; //鼠标句柄 HBRUSH hbrBackground; //绘制窗口背景的画刷句柄 LPCSTR lpszMenuName; //窗口菜单的资源id字符串 LPCSTR lpszClassName; //窗口类的名称 } WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
大家也可以在微软的MSDN官方文档中查看:WNDCLASS结构体
4.style窗口类风格
应用程序全局窗口类的注册,需要在窗口类的风格中增加CS_GLOBALECLASS
应用程序局部窗口类,在注册窗口类时,不添加CS_GLOBALECLASS
具体的窗口类风格,大家可以到MSDN官方文档中查看:窗口类风格style
六.创建窗口
我们来看看创建窗口函数:
HWND CreateWindowExW( [in] DWORD dwExStyle, //窗口的拓展风格 [in, optional] LPCWSTR lpClassName, //已经注册的窗口类名称 [in, optional] LPCWSTR lpWindowName, //窗口标题栏名称 [in] DWORD dwStyle, //窗口的基本风格 [in] int X, [in] int Y, [in] int nWidth, [in] int nHeight, //以上四个参数是决定窗口的位置 [in, optional] HWND hWndParent, //父窗口句柄 [in, optional] HMENU hMenu, //窗口的菜单句柄 [in, optional] HINSTANCE hInstance, //当前应用程序实例句柄 [in, optional] LPVOID lpParam //窗口创建时的附加参数 );
这里给出MSDN官方文档地址:CreateWindowExW函数
那么在CreateWindow函数中,具体是如何实现的?
这里先给出一张图,我们围绕这张图来讲解:
CreateWindow函数创建窗口过程:
- 1:CreateWindow函数内部根据传入的窗口类名称,在应用程序局部窗口类中查找,如果找到与之匹配的,执行2,如果没有找到,执行3
- 2:比较局部窗口类与创建窗口时传入的HINSTANCE变量,如果相等,说明创建和注册的窗口在同一个模块,创建窗口并返回
- 3:在应用程序全局窗口类中查找窗口类名称,如果找到,执行4,否则执行5(这时候已经不需要比对hins参数了,因为之前提到了,应用程序全局窗口类当前应用程序任何一个模块都可以使用
- 4:使用找到的窗口类信息,创建窗口并且返回
- 5:在系统窗口类中查找,如果找到,创建窗口并且返回(这时候已不需要比对hins参数,理由同上),如果未找到,创建创库失败,返回0。
这里给大家一段伪代码,表示创建窗口的过程:
CreateWindow(各种参数){ 匹配查找窗口类,即上述过程 if(找到了){ 申请一块内存,将窗口的数据存该内存(该数据包括两部分,CreateWindow函数的参数和找到的窗口类信息) return 本内存句柄; }else{ return 0;
七.无法正常关闭窗口问题
当我们写出第一个windows程序的时候,我们点击程序的关闭按钮,发现程序是不显示了,但是在任务管理器中仍然存在,说明我们的应用程序没有正常关闭。
我们来看看,为什么没有正常关闭?没有正常关闭的话,我们的程序停在了哪?
我们发现在代码的消息处理函数中有这样一段代码:
MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd,NULL, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; }
我们可以发现我们的程序肯定停在了while循环中,我们分析发现,如果GetMessage
函数返回值为0,那么程序就可以正常关闭,那么我们如何让GetMessage
函数的返回值变为零呢?
我们可以将窗口处理函数这样写:
switch(msgID){ case WM_DESTORY:{ PostQuitMessage(0); break; } }
这样我们写的应用程序就可以正常退出了,至于这其中的原理,我们会在后续章节中讲解到。
八.子窗口创建过程
子窗口的创建很简单,我们只需要满足两个条件;
- 1.创建窗口时设置父窗口句柄
- 2.创建窗口时,在style参数中加入
WS_WHILD|WS_VISIBLE
即可。