窗口类(Window Class)概述
windows窗口编程(通常意义上的win32)有几个比较核心的概念:入口函数WinMain、窗口类Window Class、窗口过程、消息处理机制、通用控件。本文主要介绍窗口类的相关概念,包括:
- 窗口类的类型;
 - 窗口类的注册及使用;
 - 窗口类的构成。
 
窗口类是基于进程的,每个应用程序在创建窗口之前必须注册窗口类(或者使用操作系统定义的窗口类),使用完成之后需要销毁(反注册)。
介绍窗口类的主要目的在于明确windows窗口编程的相关概念,掌握windows内部对于GUI处理的机制。如果你在用mfc或者其他界面框架,本文是没有必要阅读的。
一、窗口类的类型
windows下窗口类分为三种:
- 系统窗口类
 - 应用程序全局窗口类
 - 应用程序局部窗口类
 
三者主要区别在于作用域、注册销毁的时刻及方式上。
1. 系统窗口类
顾名思义,系统窗口类是有操作系统注册的。一些系统窗口类是所有进程都可以访问的,一些系统窗口类是只能被操作系统内部使用的。对于系统窗口类,应用程序是不能销毁的。
操作系统在应用程序首次调用GUI函数时,为当前进程注册系统窗口类。也就是说每个独立的应用程序都会用于一份同样的系统窗口类注册。下面表格中给出了任意进程都可以使用的系统窗口类名称。
| 窗口类 | 描述信息 | 
|---|---|
| Button | 按钮的窗口类名称。 | 
| ComboBox | 组合框的窗口类名称。 | 
| Edit | 编辑框控件的窗口类名称。 | 
| ListBox | 列表框的窗口类名称。 | 
| MDIClient | MDI子窗口的窗口类名称。 | 
| ScrollBar | 滚动条的窗口类名称。 | 
| Static | 静态控件的窗口类名称。 | 
下表是仅由操作系统内部使用的窗口类名称。
| 窗口类 | 描述信息 | 
|---|---|
| ComboLBox | 
 组合列表框的窗口类名称。 The class for the list box contained in a combo box.  | 
| DDEMLEvent | 
 动态数据交换管理库(DDEML)事件的窗口类名称。 The class for Dynamic Data Exchange Management Library (DDEML) events.  | 
| Message | The class for a message-only window. | 
| #32768 | 菜单的窗口类名称。 | 
| #32769 | 桌面窗口的窗口类名称。 | 
| #32770 | 对话框的窗口类名称。 | 
| #32771 | 任务栏切换窗口的窗口类名称。 | 
| #32772 | 图标标题栏的窗口类名称。The class for icon titles. | 
2. 应用程序全局窗口类
应用程序全局窗口类指的是由可执行程序或者DLL注册的,可以被当前进程其他模块使用的窗口类。比如你在某个DLL中注册一个全局窗口类,应用程序可以通过加载该DLL之后就可以使用对应的窗口类。
全局窗口类在不使用时必须由用户自行销毁(使用 UnregisterClass)。
3. 应用程序局部窗口类
应用程序局部窗口类指的是由可执行程序或者DLL注册的,仅用于当前模块的窗口类。我们可以注册很多局部窗口类,但推荐的做法是仅注册一个窗口类,用于创建应用程序主窗口。
操作系统在应用程序退出时自动销毁局部窗口类,我们也可通过 UnregisterClass函数手动销毁局部窗口类。
二、窗口类的注册及使用
窗口类定义了windows下的窗口属性,包括窗口样式、图标、光标、菜单项、窗口过程等。要注册窗口类,首先需要填充WNDCLASS 、WNDCLASSEX结构,然后使用设置好的参数调用RegisterClass、RegisterClassEx函数(二者主要区别在于RegisterClass函数不支持小图标设置,在通常使用中可以统一使用RegisterClassEx函数)。
如果需要注册应用程序全局窗口类,需要设置WNDCLASSEX结构的style属性为 CS_GLOBALCLASS;注册局部窗口类请勿指定 CS_GLOBALCLASS属性。
窗口类主要用于CreateWindow、CreateWindowEx函数的调用(第一个参数lpClassName),原型如下:
HWND WINAPI CreateWindow(
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int x,
_In_ int y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
); HWND WINAPI CreateWindowEx(
_In_ DWORD dwExStyle,
_In_opt_ LPCTSTR lpClassName,
_In_opt_ LPCTSTR lpWindowName,
_In_ DWORD dwStyle,
_In_ int x,
_In_ int y,
_In_ int nWidth,
_In_ int nHeight,
_In_opt_ HWND hWndParent,
_In_opt_ HMENU hMenu,
_In_opt_ HINSTANCE hInstance,
_In_opt_ LPVOID lpParam
);
窗口类是按照字符串进行查找匹配的。
1. 系统如何查找窗口类
操作系统会维护一个按照三种类型分类的窗口类列表,在应用程序需要创建窗口的时候,按照下列顺序搜索定位窗口类:
- 使用窗口类名称及当前模块实例句柄查找应用程序局部窗口类列表(注意当前模块实例句柄主要为了区分不同模块注册的同名窗口类);
 - 若局部窗口类列表中未找到,则继续查找应用程序全局窗口类列表;
 - 若全局窗口类列表中未找到,则超找系统窗口类列表。
 
所有的窗口创建都会遵循上面的查找顺序。因此这样也提供了重写系统窗口类的一种方法,在应用中注册和系统窗口类的同名的局部窗口类,这样即可以修改当前应用程序中替换某些系统窗口,同时不影响其他应用程序的系统窗口类使用。
2. 窗口类的归属
窗口类通常意义可以认为是属于注册该类的可执行程序或者DLL。操作系统使用调用RegisterClassEx函数的WNDCLASSEX结构的hInstance 判断窗口类的所有权。也就是说DLL在注册窗口类的时候必须使用DLL本身的句柄。
当动态加载的DLL卸载时,使用DLL注册窗口类的窗口可能还会存在。这就需要调用者保证DLL卸载之前,关闭所有引用DLL所注册窗口类的窗口,同时使用 UnregisterClass函数销毁窗口类。否则可能存在访问越界等情况。(因为DLL卸载之后,其过程函数地址是无效的。)
三、窗口类的构成
窗口类给出了使用该类的windows窗口的一些属性。主要参数设置位于WNDCLASSEX结构中。其定义如下:
typedef struct tagWNDCLASSEX {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
  HICON     hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
在实际使用时,系统仅要求提供类名称(lpszClassName)、回调函数地址(lpfnWndProc)、实例句柄(hInstance)三个参数,其他参数用于设置window窗口属性。下面逐一说明窗口类的元素构成:
类名称(字段:lpszClassName)
用于唯一标识窗口类。窗口类是进程相关的,使用时必须保证窗口类的类名称在当前进程中是唯一的。
另外由于类名称占用系统私有元表格(system's private atom table),注册时请保证类名尽可能短。
可以使用 GetClassName函数获取当前窗口的窗口类名称。
窗口过程地址(字段:lpfnWndProc)
系统用该地址回调windows所有的消息。具体看参考Window Procedures。原型必须符合下面定义:
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
实例句柄(字段:hInstance)
窗口类需要使用实例句柄来标识其归属的可执行程序或DLL。系统在启动可执行程序或DLL时都会给其设置实例句柄,用于管理各模块,该实例句柄可在可执行模块的入口函数中获取(比如在WinMain或DllMain中)。
类光标(字段:hCursor)
用于指定鼠标在客户区显示的样式。可使用 LoadCursor函数加载一个光标文件,用于设置该字段。
可使用 SetCursor函数设置光标属性。其他详细信息可参考Cursors。
类图标(字段:hIcon和hIconSm)
类图标是一张图片,用于标识特殊的窗口类。分为大图标和小图标。大图标用于窗口切换(ALT+Tab)以及大图标视图下的任务栏和桌面浏览器(explorer.exe)。小图标用于显示在标题栏、小图标视图下的任务栏和桌面浏览器。
图标实际大小可通过 GetSystemMetrics函数获取。设置字段SM_CXICON和SM_CYICON可获取大图标的长宽,设置字段 SM_CXSMICON和SM_CYSMICON可获取小图标的长宽。
类背景画刷(字段:hbrBackground)
用于设置窗口客户区重绘的画刷,详细设置可参考WM_ERASEBKGND消息。使用可以创建自定义画刷,也可使用系统画刷(GetSysColorBrush)。
类菜单(字段:hMenu)
用于设置系统默认菜单。可使用菜单名称,也可以使用 MAKEINTRESOURCE 。详细设置建议参考Menus。
窗口类样式(字段:style)
指定窗口类创建的一些默认参数,可参考Window Class Styles。
附加窗口类存储空间(字段:cbClsExtra)
所有窗口共享的唯一的类存储空间,类似于c++中类的静态成员函数,操作系统默认存储在WNDCLASSEX后面,如果不需要,该字段必须设置为0。
附加类存储空间是分配在系统本地堆(local heap)中的,建议字段长度不要太大(不超过40个字节)。可使用 SetClassWord、SetClassLong函数设置对应字段,使用GetClassWord、GetClassLong函数获取相应参数。
很多经典的win32编程数据可能会提到这个字段,目前多数应用是不需要设置这个参数的。也不推荐使用。
附加窗口存储空间(字段:cbWndExtra)
概念跟附件窗口类存储空间类似,只是附件窗口存储空间是按照每个窗口分配的,类似于c++中的成员变量,每个实例有一个。附件窗口存储空间一般用于存储窗口相关数据。
可使用 SetWindowLong和 GetWindowLong函数来获取、设置附件窗口存储空间中的数据。
四、总结
windows编程的核心在于消息处理机制,而窗口类作为一个独立的抽象单元,为我们提供了注册窗口类并创建的方式,有些内容是值得借鉴和学习的。虽然概念比较老,但是如果想深入了解win32内部的处理方式,还是需要在理解的基础上深化下。
windows窗口类的构成有很多参数,如果有一些默认参数无法确认,建议查看msdn上对应的内容,一般都会有描述的。
窗口类(Window Class)概述的更多相关文章
- Window 窗口类
		
窗口类 WNDCLASS 总结 总结为下面的几个问题: . 什么是窗口类 . 窗口类的三种类型 . 窗口类各字段含义 . 窗口类的注册和注销 . 如何使用窗口类,子类化.超类化是什么 下面分别描述: ...
 - Win32编程:窗口类样式+窗口外观样式+窗口显示样式
		
1.窗口类样式WNDCLASS.style CS_VREDRAW 提供窗口位置变化事件和高度变化事件的处理程序,功能是重绘窗口 CS_HREDRAW 提供窗口位置变化事件和宽度变化事件的处理程序,功能 ...
 - 探索Win32系统之窗口类(转载)
		
Window Classes in Win32 摘要 本文主要介绍win32系统里窗口类的运做和使用机制,探索一些细节问题,使win32窗口类的信息更加明朗化. 在本文中,"类", ...
 - win32键盘记录 -- 自定义窗口类
		
最近学了些关于window api编程的知识,于是琢磨编写一些键盘记录器,能够把输入的按键输出到窗口内,并且实现窗口自动滚动. 封装窗口类使用了GWL_USERDATA字段来保存this指针,比较容易 ...
 - duilib底层机制剖析:窗口类与窗口句柄的关联
		
转载请说明原出处.谢谢~~ 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术. 这些技术都是为了解决c++封装的窗口类与窗口句柄的关联问题. 这里是三篇关于thunk技 ...
 - MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转
		
MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...
 - WIN32窗口类风格和窗口风格(备查询)
		
一.WNDCLASS typedef struct { UINT cbSize //这个结构体的长度,一般用sizeof(WNDCLASSEX)设置 UINT style //窗口式样 WNDPROC ...
 - Qt5布局管理(二)——QDockWidget停靠窗口类
		
转载:LeeHDsniper 停靠窗口类QDockWidget 实例效果 如右图所示,左半部分MainWindow是该窗口的中心窗口,右边的最下面两个停靠窗口可以跳出该窗口: 但是第一个停靠窗口只能停 ...
 - ATL7窗口类详细剖析
		
前言: ATL是微软继MFC之后提供的一套C++模板类库,小巧.精妙.效率极高.它的主要作用是为我们编写COM/DOM/COM+程序提供了丰富的支持.但是ATL只能写COM么?我以前只是MFC程序员的 ...
 
随机推荐
- System.exit(1)
			
用于退出java的虚拟机,也是finally块中语句不被执行的唯一情况
 - 使用opencv设置图像的格式以及帧率
			
最近楼主正在写一个关于图像存储的程序,LZ有一颗求知心,想要了解保存的图像的格式以及获取摄像头帧率.晚些时候会写一篇关于opencv获取摄像头并且保存每帧图像信息方法. 1.修改图像的像素显示: 首先 ...
 - js动画性能提升笔记
			
JavaScript动画的性能并不亚于CSS动画.因此,如果使用了现代的动画库,例如Velocity,那么动画引擎的性能将不再是app的瓶颈,构成瓶颈的只有代码. 网络性能相关 动画是浏览器运行中资源 ...
 - Android test---monkey
			
一.在使用monkey之前,需要用到模拟器,那么怎么启动模拟器呢,先看一下电脑有什么模拟器,通过命令行查看一下 android list avd 二.看到了模拟器列表了,下来就是启动模拟器了.在命令行 ...
 - C++STL算法速查
			
非变易算法 /* 第21章 非变易算法 Non-modifying sequence operations 21.0 advance, distance 为了了解模板,先了解一下这两个迭代器操作函 ...
 - js判断是否安装pdf播放器
			
function isPDFPluginInstall() { if (!isIE()) { //ie 浏览器 和 非ie浏览器支持 // not ie if (navigator.plugins & ...
 - 5.Mybatis的输出映射(就是对查询的结果集的映射)
			
Mybatis的输出映射,也就是对查询结果集的一个映射,主要有两种: 1.resultType(不需要配置,可以直接用) 一般是实体类 基本类型也可以 2.resultMap(需要配置resultMa ...
 - scala 学习心得
			
scala 安装步骤 文件下载地址:www.scala-lang.org(Please report bugs at https://issues.scala-lang.org/. We welcom ...
 - C语课设心得分享(二)
			
咱们今儿说说IDE的事儿. IDE是「集成开发环境」的意思,比如咱们常用的VC6.0,就是开发C语言所用的IDE的一种.对于IDE的认识,可能有些朋友有点儿模糊,咱们捋一捋,我也会给出一些IDE方面的 ...
 - C语言课设心得分享(一)
			
今儿上完课设,老师果然讲的比较少,周四还不用去,看来还是学生自己折腾.我在做课设的过程中,攒了一些心得/体会,希望能和大家分享分享,也希望能一起探讨探讨.如果是我能回答的问题,我很乐意能够提供帮助. ...