窗口类 WNDCLASS 总结

总结为下面的几个问题:
. 什么是窗口类
. 窗口类的三种类型
. 窗口类各字段含义
. 窗口类的注册和注销
. 如何使用窗口类,子类化、超类化是什么 下面分别描述: . 什么是窗口类?
窗口类定义了一系列属性,系统使用这些属性作为模板来创建出一个或多个 window (窗口)。
每个窗口类都关联了一个窗口过程函数(window procedure), 由窗口类创建出的所有窗口(window), 都共享同一个窗口过程函数。
在进程中创建窗口之前必须先注册窗口类(RegisterClassEx)。注册窗口类时,会将窗口类与一个窗口类名(class name)关联起来。当创建窗口时,只要传入窗口类名,系统就能帮你找到对应的窗口类,从而创建出窗口。 由窗口类创建出来的窗口具备什么功能、如何显示,这些绝大部分取决于窗口过程函数怎么写。 窗口类和窗口的关系,可以类比为类和对象的关系。窗口类就像类定义,而窗口就像对象。
窗口过程函数就像类的成员函数,窗口过程函数中第一个参数就是窗口的句柄,类似于类的成员函数默认有一个 this 指针。
C++ 中的类定义会由链接器帮忙找到,Win32 没有这回事儿,所以需要进行注册窗口类的操作,这样才能根据类名找到类定义。 问题:自定义了一个窗口类,比如自己实现了一个 listbox 控件,怎样为这个控件添加外部接口呢? 类似于 listbox.SetListItem() 这样的接口怎么实现?此处存疑待考 . 窗口类的三种类型
有三种类型的窗口类,它们在作用范围、注册和销毁时机上有所差别:
. 系统窗口类(System Classes)
系统注册的窗口类,程序无需注册就能使用。
系统会在程序第一次调用 GDI 函数时为其注册系统窗口类, 每个程序都会得到一份系统窗口类的拷贝。 作用域: 系统内所有 Win32 进程
销毁时机: 无法销毁 以下是可以被程序直接使用的一些 System Classes:
Button
ComboBox
Edit
ListBox
MDIClient
ScrollBar
Static
有些 System Classes 是系统内部使用的:
ComboLBox
DDEMLEvent
Message
#
#
#
#
# . 程序全局窗口类(Application Global Classes)
程序自行注册的窗口类, WNDCLASS 的 style 指定为 CS_GLOBALCLASS, 注册后当前进程内全部模块都可以使用。
例如,在 a.dll 的 DllMain 里注册一个全局窗口类,如果 b.exe 加载了 a.dll, 那么就可以在 b.exe 里使用那个窗口类。全局指的就是这个意思。 作用域: 某进程的全部模块(exe, dll)
销毁时机: 自行 UnRegisterClass 作为这个特性的扩展, win32 有一项技术,允许一个第三方控件在 dll 里实现,然后把这个 dll 载入到每个 win32 进程地址空间里。这样所有进程都可以使用这个控件,这项技术的细节是,在 dll 的 DllMain 里注册第三方控件,然后把 dll 的名字写入注册表里:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\APPINIT_DLLS
这样当任意一个 win32 进程载入时,系统也同时会把这个 dll 载入这个程序的进程地址空间(这样做可能有点儿奢侈,并不是每个进程都需要这个第三方控件)。这样窗口类就可以在任意一个 win32 进程里直接使用了。 . 程序局部窗口类(Application Local Classes)
程序自行注册的窗口类, WNDCLASS 的 style 不指定 CS_GLOBALCLASS, 注册后仅注册模块可以使用。
例如,在 a.dll 里注册的局部窗口类,只能在 a.dll 里使用,即使 b.exe 加载了 a.dll, 也不能使用。 作用域: 仅注册窗口类的那个模块可以使用
销毁时机: 自行 UnRegisterClass 程序局部窗口类是使用最频繁的类(大多数情况下窗口类不需要给别的模块使用) 问题:在 dll 里注册的全局窗口类或局部窗口类, dll 被卸载时,会自动销毁吗?如果已经创建出了窗口,窗口会自动销毁吗?此处存疑待考。 . 窗口类各字段含义
窗口类用 WNDCLASS 结构体表示, WNDCLASSEX 是扩展版本,多了一个小图标 hIconSm 成员。
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:
窗口类名,标识一个窗口类,相当于窗口类的 ID;
在同一个模块内,不能注册同名的局部窗口类;在同一进程内,不能注册同名的全局窗口类;但是,可以注册跟全局窗口类或系统窗口类同名的局部窗口类,这是由系统查找窗口类的顺序决定的。根据窗口类名创建窗口时,系统按照如下顺序定位到窗口类:
. 根据窗口类名从局部窗口类列表中查找 hInstance 与当前模块的 hInstance 一致的窗口类(程序的不同模块可以用相同的窗口类名来注册局部窗口类);
. 如果局部窗口类列表里没有要查找的窗口类名,从全局窗口类列表里查找;
. 如果全局窗口类列表里没有要查找的窗口类名,从系统窗口类列表里查找;
按照这个顺序,即使局部窗口类名跟全局窗口类或系统窗口类同名,系统依然能正确定位到局部窗口类。例如,可以注册一个 "Edit" 的局部窗口类,在模块内创建 "Edit" 窗口时,会使用局部窗口类的定义,而不是系统窗口类。 hInstance:
实例句柄,标识窗口类所在的模块,可以是进程的 hInstance, dll 的 hModule, 不能为 NULL; lpfnWndProc:
窗口过程函数地址,窗口的功能由这个函数实现。 对于从该类创建的窗口,系统会将所有消息交给此窗口过程函数处理。程序可以使用 SetWindowLong 来改变窗口类的过程函数,这个操作叫做 “子类化(SubClassing)”, 稍后将具体讲述这一操作。 style:
style 成员决定了从该类创建出来的窗口的风格,可以使用下列值的组合:
CS_OWNDC,CS_CLASSDC,CS_PARENTDC:
这几个标志决定窗口的默认 DC(device context):
. 如果使用 CS_OWNDC 标志,属于此窗口类的所有窗口实例都由自己的 DC(称为私有 DC), 所以程序只需要调用一次 GetDC 或者 BeginPaint 获取 DC, 系统就为窗口初始化一个 DC, 并且保存程序对其进行的改变。 ReleaseDC 和 EndPaint 函数不再需要了。当选择了CS_OWNDC,程序改变影射模式(Mapping Mode)的时候必须小心,当由系统擦除窗口的背景时,系统假定和默认其影射模式是 MM_TEXT. 如果私有DC的影射模式不一样,窗口被擦除的地方将不再可见。
. 如果使用CS_CLASSDC标志,所有属于该类的窗口实例共享相同的 DC(称为类DC). 类 DC 有一些私有 DC 的优点,而更加节约内存(因为不需要为每个窗口实例都分配800字节的DC空间了)。每个窗口实例都通过 GetDC 或 BeginPaint 得到设备上下文(DC)句柄,如果没有别的窗口需要该DC,不需要调用 ReleaseDC 或 EndPaint 释放 DC. 在一个窗口实例上通过 GetWindowDC, GetDC, GetDCEx, BeginPaint 获得 DC,并对其中的一些参数进行更改的话,所进行的更改除了剪切区域和设备本身属性(Device origin)之外对所有其他窗口实例都是有效的。和 CS_OWNDC 相同的是,必须确保影射模式也是 MM_TEXT, 否则,被系统擦除的背景将不再可见。为NT编写的程序最好不要使用这个标志,因为“节约内存”的好处根本不明显。
. 如果使用 CS_PARENTDC 标志, 属于这个类的窗口都使用它的父窗口的句柄。和 CS_CLASSDC 相似的是,多个窗口共享一个 DC, 不同的是,这多个窗口(虽然有父子关系并且共享 DC )并不要求都属于同一个窗口类。注意如果程序需要改变各子窗口的影射模式,那么最好不要用 CS_PARENTDC 标志,否则将很容易引起各子窗口影射模式的混乱,因为所有的子窗口都使用同一个 DC.
. 如果不指定 CS_OWNDC, CS_CLASSDC, CS_PARENTDC 这几个标志,此类的窗口使用一个通用 DC, 并置于 DC 缓冲里以供使用。通用 DC 在使用前获取,使用后释放,在 DC 获取的时候, DC 里的上下文按默认值初始化,除非当时该 DC 已经在窗口的 DC 缓冲里(比如没有调用 ReleaseDC 或 EndPaint 释放 DC ),这样的话 DC 的剪切边界和设备属性都不需要被重新初试化,可以节约一些时间。 问题:这几个字段涉及到尚不了解的 DC 和映射模式等概念,存疑待考。
CS_GLOBALCLASS:
CS_GLOBALCLASS 是唯一一个针对类本身起作用而不是对某个窗口起作用的标志。系统将包含这种标志的窗口类作为应用程序全局类保存。 CS_BYTEALIGNCLIENT, CS_BYTEALIGNWINDOW:
字节对齐标志,据说没什么用了。 CS_HREDRAW, CS_VREDRAW:
CS_HREDRAW 标志表示当窗口的水平尺寸(宽度)改变的时候,重画整个窗口。 CS_VREDRAW 则是在窗口垂直尺寸(高度)改变时重画整个窗口。按钮和滚动条都有这两种风格。 问题:什么情况下需要指定这两个标志?存疑待考。
CS_NOCLOSE:
如果指定了这个标志,则窗口上的关闭按钮和系统菜单上的关闭命令失效。 CS_DBLCLKS:
CS_DBLCLKS 标志使窗口可以检测到双击事件。窗口响应双击的细节如下:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDOWN
WM_LBUTTONUP
其实就是两个单击,而如果指定了 CS_DBLCLKS 标志,则系统想窗口依次发送如下消息:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
第二次的 WM_LBUTTONDOWN 被替换成了 WM_LBUTTONDBLCLK
注意,在上述序列中间可能会插入其他的一条或一些消息,所以这两个消息序列不一定是完全连续的。 其实,在没有指定 CS_DBLCLKS 标志时,程序本身也可以检测到双击事件的。参见 MSDN 里 Dr.GUT 的 "Simulating Mouse Button Clicks" 文章,不过要有一些技巧. 一般的情况下,如果没有指定 CS_DBLCLKS, 在窗口的消息循环里将不会得到 WM_LBUTTONDBLCLK 消息。
所有的标准窗口控件,对话框,桌面窗口类都默认拥有CS_DBLCLKS标志。第三方控件最好也加上此风格,以使其在对话框编辑器里可以正常工作。 CS_SAVEBITS:
菜单,对话框,下拉框都拥有 CS_SAVEBITS 标志。当窗口使用这个标志时,系统用位图形式保存一份被窗口遮盖(或灰隐)的屏幕图象。首先,系统要求显示驱动保存图象位数据,如果显示驱动本身的存储空间足够,保存操作成功, window 系统也可以使用这些保存的位数据。如果不够,系统将位数据在全局内存里以位图的方式保存,并且在 USE 模块局部堆里为每个窗口分配空间,保存一些事务数据(比如位图数据缓冲的大小)的结构。当程序使遮盖屏幕的窗口消失时,系统可以很快的从内存里恢复屏幕图象。
CS_SAVEBITS 的效率本身是很难度量的。 CS_SAVEBITS 提高了“临时”窗口比如菜单,对话框,下拉框的性能。但是,存贮位信息的开销也是很明显的,尤其由系统代替显示驱动存储位信息的时候,系统承担了速度和存储开销。 使用 CS_SAVEBITS 的好处其实依赖于窗口遮盖的区域发生了什么事情,如果该区域相当复杂,需要重画很多的效果,那么,存储该区域可能比重画该区域要来的轻松,如果反之,该区域可以相当快速的重画,或者在被遮盖的时候还经常发生变化并且变化很显著,保存的方案反而影响了整体性能。 问题:并不理解这部分的内容,可以结合 MSDN 来学习。
以上 style 标记都是可选字段,视用途决定要使用哪些。如果窗口大小不改变、不响应双击操作、不被其他模块使用的话,设 style == 也是可以的。 hIcon 和 hIconSm:
窗口图标, hIcon 是大图标,展示在任务切换窗口(Alt+Tab), 大图标模式任务栏, explorer 中, hIconSm 是小图标,展示在窗口标题栏,小图标模式任务栏。
窗口图标的尺寸必须符合一定大小,大小可以通过 GetSystemMetrics 函数指定 SM_CXICON, SM_CYICON, SM_CXSMICON, SM_CYSMICON 来获取大小图标的标准尺寸。
可以通过 WM_SETICON 消息来修改图标,通过 WM_GETICON 消息来获取图标。 hCursor:
窗口默认鼠标指针。当设置了该值,当鼠标移入窗口区域时,系统将指针由系统默认形状变成所设置的指针形状。程序可以使用 LoadCursor 函数从标准系统指针库(比如 IDC_ARROW)或用户指定指针资源中获取指针句柄。程序可以通过 SetCursor 函数随时改变指针。如果 hCursor 的值未设置(设置为 NULL), 程序必须在鼠标指针移入窗口时进行设置,否则将使用系统默认的鼠标指针形状。 lpszMenuName:
窗口默认主菜单。如果创建窗口时没有显式指定菜单资源,将使用在这里指定的默认菜单。
可以使用 MAKEINTRESOURCE 宏将资源里菜单的 ID 号转换为连续字符串赋值给该成员。 hbrBackground:
窗口背景颜色,类型为 HBRUSH, 可以将其赋值为一个画刷句柄,或者颜色值。如果是颜色值的话,必须使用下列标准系统颜色之一:
COLOR_ACTIVEBORDER
COLOR_HIGHLIGHTTEXT
COLOR_ACTIVECAPTION
COLOR_INACTIVEBORDER
COLOR_APPWORKSPACE
COLOR_INACTIVECAPTION
COLOR_BACKGROUND
COLOR_INACTIVECAPTIONTEXT
COLOR_BTNFACE
COLOR_MENU
COLOR_BTNSHADOW
COLOR_MENUTEXT
COLOR_BTNTEXT
COLOR_SCROLLBAR
COLOR_CAPTIONTEXT
COLOR_WINDOW
COLOR_GRAYTEXT
COLOR_WINDOWFRAME
COLOR_HIGHLIGHT
COLOR_WINDOWTEXT
使用颜色赋值时,必须加 并强制转换为 HBRUSH 类型: wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + );
如果 hbrBackground 成员设置为 NULL, 程序必须在响应 WM_PAINT 的时候负责画背景。程序也可以响应 WM_ERASEBKGND 消息,或根据调用 BeginPaint 函数时填充的 PAINTSTRUCT 结构里的成员 fErase 的值类判断是否需要重画背景。 cbClsExtra, cbWndExtra:
cbClsExtra 指窗口类额外内存大小,这部分内存被该类创建的所有窗口共享;
cbWndExtra 指窗口额外内存大小,该类创建的每个窗口各自拥有一段额外内存;
额外内存的大小最多只能有 个字节。如果不需要的话,必须设为 , 以防止系统错误分配内存。
如果用类声明并注册一个对话框类型的窗口, cbWndExtra 的值必须设置为 DLGWINDOWEXTRA, 系统对话框管理器需要这么多的额外数据对对话框进行管理。 . 窗口类的注册和注销
注册窗口类使用 RegisterClass 或 RegisterClassEx 函数;
参数: WNDCLASS 或 WNDCLASSEX 结构体;
返回值:ATOM 类型的原子值,据说这个值是跟每个窗口类一一对应的。
注意: RegisterClass 和 RegisterClassEx 都有 ANSI 和 Unicode 两种版本。如果使用 ANSI 版本如 RegisterClassA, 消息中的文本信息将是 ANSI 格式;如果使用 Unicode 版本如 RegisterClassExW, 消息中的文本信息将是 Unicode 格式。 注销窗口类使用 UnregisterClass 函数;
参数: lpszClassName, hInstance
返回值:如果定位不到窗口类,或者仍然有窗口没有被销毁,将失败并返回非 值。
注意: 调用 UnRegisterClass 之前,必须先销毁由该类创建出的所有窗口。
dll 被卸载后, 这个 dll 所注册过的窗口类并不会被注销。 . 如何使用窗口类,子类化、超类化是什么
一旦注册了一个类,一般来说除了使用该类创建窗口之外就没有什么需要做的事情了。当然,如果需要访问该类信息,子类化,或者超类化该类,介绍一些方法就是有用的。
类访问函数:
GetClassInfoEx 获取窗口类信息,输入 hInstance 和 lpszClassName, 返回窗口类对应的 WNDCLASSEX 结构体。 也可以输入 atom
GetClassLong 从 WNDCLASSEX 结构体中获取一个 long 类型数值, 输入 hWnd, nIndex, 返回窗口对应窗口类的 WNDCLASSEX 结构体中的某个字段的信息。 比如 GetClassLong(hWnd, GCL_STYLE) 可以获取窗口类的 styles
GetClassLongPtr 跟上面那个函数的功能类似,只是以指针方式返回结果。比如 GetClassLongPtr(hWnd, GCLP_WNDPROC) 可以获取窗口类的过程函数地址
GetClassName 获取某窗口所属窗口类的窗口类名, 输入 hWnd, 返回 lpClassName
GetWindowLong 获取某个窗口的指定信息,以 long 类型返回,输入 hWnd, nIndex, 返回窗口的某个字段的信息,比如 GetWindowLong(hWnd, GWL_STYLE) 可以获取窗口的 styles
GetWindowLongPtr 跟上面那个函数的功能类似,只是以指针方式返回结果。比如 GetWindowLongPtr(hWnd, GWLP_WNDPROC) 可以获取窗口对应窗口类的窗口过程函数
SetClassLongPtr 替换窗口类 WNDCLASSEX 的某个字段
SetClassWord 跟上面函数功能类似,不过貌似是给 位系统用的
SetWindowLong 替换窗口的某个字段,输入 hWnd, nIndex, dwNewLong 可以替换指定字段
SetWindowLongPtr 跟上面函数功能类似 子类化:
术语"子类化"(subclassing)描述的是用一个新的窗口过程代替原窗口过程。
术语"实例子类化"(即子类化单个窗口)是指使用 SetWindowLong 函数改变某一个窗口实例的窗口过程。
术语"全局子类化"(子类化整个窗口类)则是指使用SetClassLong改变整个类的默认窗口过程函数。
在 位 windows 系统里,可能难于子类化另一个进程里的窗口或窗口类,一般来说,子类化都是发生于同一个进程里的("打破进程边界"的相关的主题本文没有涉及). 超类化:
术语"超类化"(Superclassing)指创建一个新的类,该类使用某个现存类的窗口过程,继承该类的基本功能,并可以在此基础上进行扩展。 关于子类化和超类化的具体描述,请参阅 MSDN 里 "safe subclasing in Win32" 一文
http://jdearden.gotdns.org/programming_windows_notebook/safe_subclassing_in_win32.html 参考链接:
https://msdn.microsoft.com/en-us/library/ms633574(v=vs.85).aspx
http://blog.csdn.net/vcbear/article/details/5988

Window 窗口类的更多相关文章

  1. [Android FrameWork 6.0源码学习] Window窗口类分析

    了解这一章节,需要先了解LayoutInflater这个工具类,我以前分析过:http://www.cnblogs.com/kezhuang/p/6978783.html Window是Activit ...

  2. 窗口类(Window Class)概述

    windows窗口编程(通常意义上的win32)有几个比较核心的概念:入口函数WinMain.窗口类Window Class.窗口过程.消息处理机制.通用控件.本文主要介绍窗口类的相关概念,包括: 窗 ...

  3. WPF 之 创建继承自Window 基类的自定义窗口基类

    开发项目时,按照美工的设计其外边框(包括最大化,最小化,关闭等按钮)自然不同于 Window 自身的,但窗口的外边框及窗口移动.最小化等标题栏操作基本都是一样的.所以通过查看资料,可按如下方法创建继承 ...

  4. Win32编程:窗口类样式+窗口外观样式+窗口显示样式

    1.窗口类样式WNDCLASS.style CS_VREDRAW 提供窗口位置变化事件和高度变化事件的处理程序,功能是重绘窗口 CS_HREDRAW 提供窗口位置变化事件和宽度变化事件的处理程序,功能 ...

  5. 探索Win32系统之窗口类(转载)

    Window Classes in Win32 摘要 本文主要介绍win32系统里窗口类的运做和使用机制,探索一些细节问题,使win32窗口类的信息更加明朗化. 在本文中,"类", ...

  6. win32键盘记录 -- 自定义窗口类

    最近学了些关于window api编程的知识,于是琢磨编写一些键盘记录器,能够把输入的按键输出到窗口内,并且实现窗口自动滚动. 封装窗口类使用了GWL_USERDATA字段来保存this指针,比较容易 ...

  7. duilib底层机制剖析:窗口类与窗口句柄的关联

    转载请说明原出处.谢谢~~ 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术. 这些技术都是为了解决c++封装的窗口类与窗口句柄的关联问题. 这里是三篇关于thunk技 ...

  8. MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转

    MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决   在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...

  9. WIN32窗口类风格和窗口风格(备查询)

    一.WNDCLASS typedef struct { UINT cbSize //这个结构体的长度,一般用sizeof(WNDCLASSEX)设置 UINT style //窗口式样 WNDPROC ...

随机推荐

  1. android 启动socket 失败:socket(af_inet sock_stream 0) 返回-1

    Android 启动socket 失败:socket(af_inet sock_stream 0) 返回-1 原因权限问题, 应该添加如下权限: <uses-permission android ...

  2. python中常用的推导(字典推导和列表推导)

    在python开发中经常需要书写这样的代码 result = list() for data in datas: if data not in ['a', 'b']: result.append(da ...

  3. JVM基础(1)——内存模型

    转载:http://blog.csdn.net/weitry/article/details/53264262 系列文章规划: JVM基础(1)——内存模型 JVM基础(2)——内存管理 JVM基础( ...

  4. eclipse缺省的Server没有weblogic

    转自:http://www.javakfz.com/index.php/08/06/471.html eclipse缺省的Server没有weblogic,因此要下载个weblogic的插件.这个过程 ...

  5. DNS域名解析服务

    一.DNS的体系结构: DNS:域名解析系统 DNS由根域.顶级域和子域构成.根域主要负责管理顶级域,顶级域主要负责管理其下面子域. .代表DNS的根域. .com..edu等代表顶级域. shou. ...

  6. 无废话MVC入门教程一[概述、环境安装、创建项目]

    (转载) 本文目标 1.对MVC有初步的了解 2.能够在VS2010的基础之上安装MVC3的开发和运行环境 3.对MVC框架有概括性的认识 本文目录 1.什么是MVC 2.VS2010安装MVC3 3 ...

  7. 算法笔记_107:蓝桥杯练习 算法提高 学霸的迷宫(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗.但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要 ...

  8. 算法笔记_073:哈密顿回路问题(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 什么是哈密顿回路? 引用自百度百科: 哈密顿图(哈密尔顿图)(英语:Hamiltonian path,或Traceable path)是一个无向图, ...

  9. vue 不能检测数组长度 值变化原因解析

    1.vue不能检测数组长度或者值的变化 (1)数组长度变化 未检测到 <!DOCTYPE html> <html lang="en"> <head&g ...

  10. 修改HTML5 input placeholder默认颜色

    WebKit和Blink(Safari,Google Chrome, Opera15+)使用伪元素 ::-webkit-input-placeholder Mozilla Firefox 4-18使用 ...