Windows创建窗口涉及的概念以及相关函数

本文详细介绍了Windows窗口创建的过程,包括WNCLASS结构体、RegisterClass和CreateWindow函数的使用,以及展示、更新窗口的方法。同时讲解了MSG结构体、PeekMessage、TranslateMessage、DispatchMessage在消息处理中的角色,以及窗口关闭相关的PostQuitMessage和DestroyWindow函数。最后提到了默认消息处理函数DefWindowProc的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、窗口结构体WNCLASS

这个结构体存储的是一些关于窗口类信息的结构,

成员如下:

typedef struct tagWNDCLASSA {
  UINT      style;//窗口类型
  WNDPROC   lpfnWndProc;//窗口处理函数(回调函数)
  int       cbClsExtra;//窗口扩展(一般为0)
  int       cbWndExtra;//窗口实例扩展(一般为0)
  HINSTANCE hInstance;//实例句柄
  HICON     hIcon;//窗口的最小化图标
  HCURSOR   hCursor;//窗口鼠标光标
  HBRUSH    hbrBackground;//窗口背景颜色
  LPCSTR    lpszMenuName;//窗口菜单
  LPCSTR    lpszClassName;//窗口类名
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
窗口类型可选值
类风格含义
CS_VREDRAW移动或者调整窗口的高度(垂直方向)时,重绘整个窗口
CS_HREDRAW移动或者调整窗口的宽度(水平方向)时,重绘整个窗口
CS_DBLCLKS当用户光标在窗口内双击时,允许发送双击消息给窗口过程
CS_OWNDC给予每个窗口实例分配一个唯一的 DC(注意,尽管这样是很方便,但它必须慎重使用,因为每个 DC 大约要占 800 个字节的内存)
CS_CLASSDC该窗口类的所有窗口实例都共享一个窗口类 DC
CS_PARENTDC1. 将子窗口的裁剪区域设置到父窗口的 DC 中去,这样子窗口便可以在父窗口上绘制自身。(注意,这是子窗口还是从系统缓存中获取 DC,而不是使用父窗口的 DC。)
2. 指定该风格可以提高系统性能
CS_NOCLOSE禁止系统菜单的关闭选项
CS_SAVEBITS1. 以位图形式保存被该窗口遮挡的屏幕部分,这样当给窗口移动以后,系统便可以用该保存的位图恢复屏幕移动的相应部分,从而系统不用向被该窗口遮挡的窗口发送 WM_PAINT 消息
2. 该特性对于菜单类型的窗口比较合适,因为它通常是简短的显示一下之后便消失
3. 设置该特性将增加显示该窗口的时间,因为它通常要先分配保存位图的内存
CS_BYTEALIGNCLIENT在字节边界上(在 x 方向上)定位窗口的用户区域的位置
CS_BYTEALIGNWINDOW在字节边界上(在 x 方向上)定位窗口的位置
CS_GLOBALCLASS1. 当调用 CreateWindow 或 CreateWindowEx 函数来创建窗口时允许它的 hInstance 参数和注册窗口类时传递给 RegisterClass 的 hInstance 参数不同
2. 如果不指定该风格,则这两个 hInstance 必须相同

下面是我自己在程序中对窗口类信息结构体的填充,可以当做参考:

	// WNDCCASS :窗口类,用来处理窗口的某一类信息
	WNDCLASS wc;

	wc.style = CS_HREDRAW | CS_VREDRAW;
	// 回调函数
	wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	// 函数传进来的窗口的句柄
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(0, IDI_APPLICATION);
	wc.hCursor = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = 0;
	wc.lpszClassName = "DX9App";

二、对相关窗口进行注册

我们使用RegidsterClass函数对我们刚才填充的窗口结构进行注

	// 将窗口的信息填充完成之后,使用该函数,对窗口进行注册
	if (!RegisterClass(&wc))
	{
		::MessageBox(0, "RegisterClass() - FAILED", 0, 0);
		return false;
	}

三、注册完成之后,我们就要开始利用现有的属性进行窗口的创建,使用函数:CreateWindow

函数原型:

HWND CreateWindow(
LPCTSTR lpClassName,//窗口类的名称
LPCTSTR lpWindowName,//窗口的标题 
DWORD dwStyle,//窗口风格
int x,//初始x坐标
int y,//初始y坐标
int nWidth,//初始x方向的尺寸
int nHeight,//初始y方向的尺寸
HWND hWndParent,//父窗口句柄
HMENU hMenu,//窗口菜单句柄
HANDLE hlnstance,//程序实例句柄
LPVOID lpParam//创建参数
)

参数

含义

lpClassName

1. 窗口类名称,可以是一个指向 NULL 结束的字符串或一个整型数值
2. 如果是字符串,它指定了窗口的类名。这个类名可以是任何用函数 RegisterClass 注册的类名,或是任何预定义的控制类名
3. 如是一个整型量,它是由此前调用 theGlobalAddAtom 函数产生的全局量。这个小于 0xC000 的 16 位数必须是 lpClassName 参数字的低 16 位,该参数的高位必须是 0

lpWindowName

1. 窗口标题,一个指向 NULL 结束的字符串指针
2. 如果窗口风格指定了标题条,由 lpWindowName 指向的窗口标题将显示在标题条上
3. 当使用 Createwindow 函数来创建控制例如按钮,选择框和静态控制时,可使用 lpWindowName 来指定控制文本

dwStyle

指定创建窗口的风格(可选值见下一个表格)

x

1. 指定窗口的初始水平位置(x 坐标)
2. 对一个层叠或弹出式窗口,x 参数是屏幕坐标系的窗口的左上角的初始 x 坐标
3. 对于子窗口,x 是子窗口左上角相对父窗口客户区左上角的初始 x 坐标
4. 如果该参数被设为 CW_USEDEFAULT 则系统为窗口选择缺省的左上角坐标并忽略 y 参数,CW_USEDEFAULT 只对层叠窗口有效,如果为弹出式窗口或子窗口设定,则 x 和 y 参数被设为零。

y

1. 指定窗口的初始垂直位置(y 坐标)
2. 对一个层叠或弹出式窗口,y 参数是屏幕坐标系的窗口的左上角的初始 y 坐标
3. 对于子窗口,y 是子窗口左上角相对父窗口客户区左上角的初始 y 坐标
4. 对于列表框,y 是列表框客户区左上角相对父窗口客户区左上角的初始 y 坐标
5. 如果层叠窗口是使用 WS_VISIBLE 风格位创建的并且 x 参数被设为 CW_USEDEFAULT,则系统将忽略 y 参数

nWidth

1. 以设备单元指明窗口的宽度
2. 对于层叠窗口,nWidth 的值或是屏幕坐标的窗口宽度或是 CW_USEDEFAULT
3. 若 nWidth 是 CW_USEDEFAULT,则系统为窗口选择一个默认的高度和宽度(默认宽度为从初始 x 坐标开始到屏幕的右边界,缺省高度为从初始 y 坐标开始到目标区域的顶部。),CW_USEDEFAULT 只对层叠窗口有效,如果为弹出式窗口和子窗口设定 CW_USEDEFAULT 标志则 nWidth 和 nHeight 被设为零

nHeight

1. 以设备单元指明窗口的高度
2. 对于层叠窗口,nHeight 是屏幕坐标的窗口宽度
3. 若 nWidth 被设为 CW_USEDEFAULT,则系统忽略 nHeight 参数,自动为 nWidth 和 nHeight 设置默认参数

hWndParent

1. 指向被创建窗口的父窗口或所有者窗口的句柄
2. 若要创建一个子窗口或一个从属窗口,需提供一个有效的窗口句柄
3. 创建一个单纯的消息窗口,可以提供 HWND_MESSAGE 或提供一个己存在的消息窗口的句柄

hMenu

1. 指向窗口菜单句柄,或依据窗口风格指明一个子窗口标识
2. 对于层叠或弹出式窗口,hMenu 指定窗口使用的菜单:如果使用了菜单类,则 hMenu 可以为 NULL
3. 对于子窗口,hMenu 指定了该子窗口标识(一个整型量),一个对话框使用这个整型值将事件通知父类。应用程序确定子窗口标识,这个值对于相同父窗口的所有子窗口必须是唯一的

hInstance

与窗口相关联的模块实例的句柄

lpParam

1. 指向一个值的指针,该值传递给窗口 WM_CREATE 消息。该值通过在 IParam 参数中的 CREATESTRUCT 结构传递
2. 如果应用程序调用 CreateWindow 创建一个 MDI 客户窗口,则 lpParam 必须指向一个 CLIENTCREATESTRUCT 结构

dwStyle可选值:

窗口风格

含义

WS_BORDER

创建一个带边框的窗口

WS_CAPTION

创建一个有标题框的窗口(包含了 WS_BODER 风格)

WS_CHILD

创建一个子窗口,这个风格的窗口不能拥有菜单也不能与 WS_POPUP 风格合用

WS_CHILDWINDOW

与 WS_CHILD 相同

WS_CLIPCHILDREN

当在父窗口内绘图时,排除子窗口区域,在创建父窗口时使用这个风格

WS_CLIPSIBLINGS

1. 排除子窗口之间的相对区域,也就是,当一个特定的窗口接收到 WM_PAINT 消息时,WS_CLIPSIBLINGS 风格将所有层叠窗口排除在绘图之外,只重绘指定的子窗口
2. 如果未指定该风格,并且子窗口是层叠的,则在重绘子窗口的客户区时,就会重绘邻近的子窗口

WS_DISABLED

1. 创建一个初始状态为禁止的子窗口,一个禁止状态的窗口不能接受来自用户的输入信息
2. 在窗口创建之后,可以调用 EnableWindow 函数来启用该窗口

WS_DLGFRAME

创建一个带对话框边框风格的窗口,这种风格的窗口不能带标题条

WS_GROUP

1. 指定一组“控制窗口”的第一个“控制窗口”
2. 这个“控制窗口”组由第一个“控制窗口”和随后定义的“控制窗口”组成,自第二个“控制窗口”开始每个“控制窗口”具有 WS_GROUP 风格
3. 每个组的第一个“控制窗口”带有 WS_TABSTOP 风格,从而使用户可以在组间移动
4. 用户随后可以使用光标在组内的控制间改变键盘焦点

WS_HSCROLL

创建一个有水平滚动条的窗口

WS_ICONIC

创建一个初始状态为最小化状态的窗口,与 WS_MINIMIZE 风格相同

WS_MAXIMIZE

创建一个初始状态为最大化状态的窗口

WS_MAXIMIZEBOX

创建一个具有最大化按钮的窗口,该风格不能与 WS_EX_CONTEXTHELP 风格同时出现,同时必须指定 WS_SYSMENU 风格

WS_MINIMIZE

创建一个初始状态为最小化状态的窗口,与 WS_ICONIC 风格相同

WS_MINIMIZEBOX

创建一个具有最小化按钮的窗口,该风格不能与 WS_EX_CONTEXTHELP 风格同时出现,同时必须指定 WS_SYSMENU 风格

WS_OVERLAPPED

产生一个层叠的窗口,一个层叠的窗口有一个标题条和一个边框,与 WS_TILED 风格相同

WS_OVERLAPPEDWINDOW

相当于(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX),与 WS_TILEDWINDOW 风格相同

WS_POPUP

创建一个弹出式窗口,该风格不能与 WS_CHILD 风格同时使用。

WS_POPUPWINDOW

相当于(WS_POPUP | WS_BORDER | WS_SYSMENU),但 WS_CAPTION 和 WS_POPUPWINDOW 必须同时设定才能使窗口某单可见

WS_SIZEBOX

创建一个可调边框的窗口,与 WS_THICKFRAME 风格相同

WS_SYSMENU

创建一个在标题条上带有窗口菜单的窗口,必须同时设定 WS_CAPTION 风格

WS_TABSTOP

1. 创建一个“控制窗口”,在用户按下 Tab 键时可以获得键盘焦点。
2. 按下 Tab 键后使键盘焦点转移到下一具有 WS_TABSTOP 风格的“控制窗口”

WS_THICKFRAME

创建一个具有可调边框的窗口,与 WS_SIZEBOX 风格相同

WS_TILED

产生一个层叠的窗口,一个层叠的窗口有一个标题和一个边框,与 WS_OVERLAPPED 风格相同

WS_TILEDWINDOW

相当于(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX),与 WS_OVERLAPPEDWINDOW 风格相同

WS_VISIBLE

创建一个初始状态为可见的窗口

WS_VSCROLL

创建一个有垂直滚动条的窗口

该函数成功则返回新的窗口句柄,失败,则返回空。

实例:

hwnd = ::CreateWindow("DX9App", "DX9App",
		WS_EX_TOPMOST,
		0, 0, width, height,
		0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/);

// 判断一下这个创建是不是成功
	if (!hwnd)
	{
		::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
		return false;
	}

四、创建完成之后,就开始展示窗口,使用函数:ShowWindow

函数声明如下:

BOOL ShowWindow(HWND hWnd, int nCmdShow);

第一个参数是窗口的句柄,第二个参数是窗口展示的类型,可选值如下:

含义
SW_FORCEMINIMIZE强制窗口最小化,主要使用在非窗口主线程的其它线程来操作
SW_HIDE显示窗口为隐藏状态
SW_MAXIMIZE显示窗口为最大化
SW_MINIMIZE显示窗口为最小化
SW_RESTORE从任务里恢复窗口显示
SW_SHOW激活窗口为当前窗口,并且显示为当前的大小和位置
SW_SHOWDEFAULT创建进程时显示窗口的值
SW_SHOWMAXIMIZED激活窗口为当前窗口,并且显示最大化
SW_SHOWMINIMIZED激活窗口为当前窗口,并且显示最小化
SW_SHOWMINNOACTIVE显示窗口为最小化,但不激活它作为当前窗口v
SW_SHOWNA显示为当前的大小和位置,但不激活它作为当前窗口显示为当前的大小和位置,但不激活它作为当前窗口
SW_SHOWNOACTIVATE显示当前窗口,但不激活它作为当前窗口
SW_SHOWNORMAL显示当前窗口,但窗口是最小化或最大化时会恢复窗口为原来的大小和位

五、UpdateWindow函数,更新窗口

函数原型:

BOOL UpdateWindow(HWND hWnd // 窗口的句柄);

一般在创建窗口之后,调用ShowWindow,然后就会使用该函数进行窗口的更新。

PS:但是在我自己的程序中使用与否感觉差别不大,估计是没有搞清楚这个函数的实现机制到底是什么~

六、MSG结构体

typedef struct tagMSG{
    HWND hwnd;  //窗口句柄
    UINT message;  //消息类型
    WPARAM wParam;  //附加消息1
    LPARAM lParam;  //附加消息2
    DWORD time;  //消息被传递时候的时间
    POINT  pt;  //消息被传递时光标在屏幕上的位置
} MSG;

1) 最后两个字段 time 和 pt 一般由系统使用,我们很少用到。

2) message 为消息类型,也就是以 WM 开头的消息(WM 是 Window Message 的缩写 ),例如 WM_CREATE、WM_PAINT、WM_DESTROY、WM_COMMAND 等。

3)至于wParam和lParam,MSDN上给的解释,两个参数是完全一样的

查了一些资料,我的理解是如果是自己定义的message的类型,那么怎么填写wParam和lParam就怎么取message,如果使用的是系统message类型,那么每一个系统的message类型都会有自己对应的wParam和lParam,此时找出来,填充即可,不过我感觉大多数系统的message类型的已经够用。

七、PeekMessage、TranslateMessage、DispatchMessage

BOOL PeekMessage(      

    LPMSG lpMsg,//

    HWND hWnd,//

    UINT wMsgFilterMin,//

    UINT wMsgFilterMax,//

    UINT wRemoveMsg//

);

参数说明:

lpMsg:指向一个MSG结构的指针,该结构中保存着从线程消息队列中取得的消息信息。

hWnd:被测试消息的相关窗口句柄。该窗口必须属于正在调用的线程。

如果hWnd值为NULL:PeekMessage检索任何属于调用线程窗口的消息。如果hWnd的值为INVALID_HANDLE_VALUE,PeekMessage会检索那些hWnd值为NULL的消息,并作为由PostThreadMessage投递的消息来对待。

wMsgFilterMin:指定被检索的最小消息值的整数。使用WM_KEYFIRST指定第一个键盘消息或WM_MOUSEFIRST指定第一个鼠标消息。

wMsgFilterMax:指定被检索的最大消息值的整数。使用WM_KEYLAST指定最后的键盘消息或WM_MOUSEFIRST指定最后的鼠标消息。

wRemoveMsg:指定消息的处理方式。该参数的值可以为下列的值之一。

含义
PM_NOREMOVE消息经PeekMessage处理后并不从消息队列中移除。
PM_REMOVE消息经PeekMessage处理后从消息队列中移除
PM_QS_INPUTWindows 98/Me,Windows 2000/XP: 处理鼠标和键盘消息。
PM_QS_PAINTWindows 98/Me,Windows 2000/XP:处理绘图消息。
PM_QS_POSTMESSAGEWindows 98/Me,Windows 2000/XP:处理所有投递消息,包括定时器和热键消息
PM_QS_SENDMESSAGEWindows 98/Me,Windows 2000/XP: 处理所有发送消息。

返回值:

如果有可用的消息,则返回值为非0;

如果没有可用的消息,则返回值为0;

BOOL TranslateMessage( CONST MSG*lpMsg );

参数:lpMsg是检查需要转换的消息。

返回值:

如果消息被转换(即,字符消息被送到线程的消息队列中),返回非零值。

如果消息是 WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, 或 WM_SYSKEYUP,返回非零值,不考虑转换。

如果消息没有转换(即,字符消息没被送到线程的消息队列中),返回值是零。

TranslateMessage是用来把虚拟键消息转换为字符消息。

由于Windows对所有键盘编码都是采用虚拟键的定义,这样当按键按下时,并不得字符消息,需要键盘映射转换为字符的消息。

LONG DispatchMessage(CONST MSG*lpmsg);

参数:lpmsg:指向含有消息的MSG结构的指针。

返回值:返回值是窗口程序返回的值。尽管返回值的含义依赖于被调度的消息,但返回值通常被忽略。

总结:TranslateMessage函数将键盘消息转化,DispatchMessage函数将消息传给窗体函数去处理.

处理代码像下面这样子:

		if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}

八、PostQuitMessage

当你点击窗口右上角的关闭时,Windows就会把窗口从系统里删除,这时就会发出消息WM_DESTROY给窗口消息处理函数WindowProc,WindowProc收到这条消息后,最需要做的一件事情就是调用PostQuitMessage发出退出消息,让消息循环结束。

函数PostQuitMessage声明如下:

VOID PostQuitMessage( int nExitCode); 

nExitCode是退出标识码,它被放到WM_QUIT消息的参数wParam里。

九、DestroyWindow

调用DestroyWindow函数后,操作系统就会进行一系列的删除动作,先发送WM_DESTROY消息,接着发送WM_NCDESTROY消息。如果这个窗口还有子窗口或者是其它窗口的所有者,就需要给所有子窗口发送删除消息。

函数DestroyWindow声明如下:

BOOL DestroyWindow( HWND hWnd);

hWnd是要删除的窗口句柄。

十、DefWindowProc

在Windows操作系统里,当窗口显示之后,它就可以接收到系统源源不断地发过来的消息,然后窗口就需要处理这些消息,因此就需要一个函数来处理这些消息。在API里定义了一个函数为回调函数,当系统需要向窗口发送消息时,就会调用窗口给出的回调函数WindowProc,如果WindowProc函数不处理这个消息,就可以把它转向DefWindowProc函数来处理,这是系统的默认消息处理函数。当你按下菜单,或者点击窗口时,窗口需要运行这个消息处理函数。

函数DefWindowProc声明如下:

LRESULT DefWindowProc(         

    HWND hWnd,

    UINT Msg,

    WPARAM wParam,

    LPARAM lParam

);

hwnd是当前窗口的句柄。

uMsg是系统发过来的消息。

wParam是消息参数。

lParam是消息参数。

 

以上就是创建窗口涉及到的一些函数和概念,以后有在接触到,还会在这里补充~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值