由WS_OVERLAPPDWINDOW窗口的风格联想起

本文详细介绍Windows环境下窗口编程的基础概念和技术,包括窗口类的注册、窗口的创建与显示、消息循环等核心环节。通过实例讲解如何使用API函数如RegisterClass、CreateWindow、ShowWindow等进行窗口界面的设计与实现。

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

原文:http://blog.sina.com.cn/s/blog_413115f7010006fi.html

以上的标识符可以在WINUSER.H中找到。函数返回代表该图标对象的句柄。

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

LoadCursor()函数加载一个预先定义的光标,并返回该项光标的句柄。该项句柄被赋给WNDCLASS结构的hCursor域。当鼠标经过"基于此窗口类创建的窗口"时,它变成一个小箭头。不过因为之前的光标也是小箭头,所以我们看不出光标的变化。下面是预定义的鼠标指针标识符:

IDC_APPSTARTING 标准箭头及小沙漏
IDC_ARROW 标准箭头
IDC_CROSS 十字交叉
IDC_HAND (Windows2000)手形
IDC_HELP 箭头和问号
IDC_IBEAM 文本I形
IDC_ICON 空图标
IDC_NO 斜杠圈
IDC_SIZE 四向箭头
IDC_SIZEALL 四向箭头
IDC_SIZENESW 指向东北和西南的双向箭头
IDC_SIZENS 指向南北的双向箭头
IDC_SIZENWSE 指向西北和东南的双向箭头
IDC_SIZEWE 指向东西的双向箭头
IDC_UPARROW 垂直箭头
IDC_WAIT 沙漏

您可以把语句中的IDC_ARROW换成别的,然后再编译运行一次。当您把鼠标移到窗口上面时会看到不同的结果。试一试啊!!你可以先试一试IDC_CROSS。

wndclass.hCursor = LoadCursor(NULL, IDC_CROSS);

然后编译运行一次。把鼠标移到窗口对象上面,看看鼠标有什么变化?鼠标是不是变成十字形了?之前的LoadIcon()也是同样道理,您可以试着换一下第二个参数。看看有什么变化。

wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);

设定基于此窗口类创建的窗口对象的背景颜色。hbr代表“handle to a brush(刷子句柄)”。刷子是图形学上的术语。指用来填充一个区域的着色像素模式。Windows有几个标准刷子,也称为备用(stock)刷子。上面所示的GetStockObject()调用将返回一个白色刷子的句柄。窗口客户区将完全为白色。这是一种及其普遍的做法。

wndclass.lpszMenuName = NULL;

指定窗口类菜单。HelloWin没有菜单,所以这项为NULL。

wndclass.lpszClassName = szAppName;

给这个窗口类取名字。以后就用这个名字认它了。

if (!RegisterClass (&wndclass))//为程序窗口注册窗口类
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

创建一个窗口首先需要注册一个窗口类。RegisterClass()的功能是注册窗口类。但是注册窗口类并不是每次都能成功。所以应该有一个查错机制。

RegisterClass()注册失败后会返回0。所以当窗口类注册失败后if为真,接着执行MessageBox()。执行MessageBox()的结果是显示一个消息框,并显示"This program requires Windows NT!"。然后退出整个程序。这段英文的大概意思是说"本程序需要Windows NT的支持!"。如果您使用的是Windows98,当您运行附书光盘中的HelloWin.exe时可能有机会看到这个消息框。我的是Win2k,所以看不到啊。出现这个消息框表明注册窗口类失败。

窗口类定义了窗口的一般特征,可以使用同一窗口类创建许多不同的窗口。调用CreateWindow()创建窗口时指定有关窗口的更详细的信息。

为什么一个窗口的所有特征不能被一步到位指定呢?实际上以这种方式分开这些风格信息是非常方便的。例如,所有的按钮窗口都可以基于同样的窗口类来创建。与这个窗口类相关的窗口过程位于Windows内部。所有的按钮都是以同样的方式工作的。但是每一个按钮都有不同的尺寸,不同的屏幕位置,以及不同的文本串。这些不同的特征是CreateWindow()定义的一部分,而不是窗口类定义的。

hwnd = CreateWindow (szAppName, // 指定一个窗口类,基于该窗口类创建窗口
TEXT ("The Hello Program"), // 这个字符串会出现在标题栏中
WS_OVERLAPPEDWINDOW, // 本窗口风格
CW_USEDEFAULT, // 窗口的X坐标,更准确地说是窗口左上角的X坐标
CW_USEDEFAULT, // 窗口的Y坐标
CW_USEDEFAULT, // 窗口的宽度
CW_USEDEFAULT, // 窗口的高度
NULL, // 窗口对象的父窗口句柄
NULL, // 窗口对象的菜单句柄或者子窗口编号
hInstance, // 当前进程的实例句柄
NULL) ; // 窗口对象的参数指针句柄(我也不知道这是什么意思)

我们要关注的是第三个参数WS_OVERLAPPEDWINDOW

WINUSER.H中对WS_OVERLAPPDWINDOW定义如下。

#define WS_OVERLAPPDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)

您想搞清楚这一长串到底是什么东东吗?很…很…很Easy!这是定义该窗口对象的风格。从上面的语句里可以看出第三行的真正的内容是:

WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

问题是书上没有说明上面六个WS_XXXX都是干什么用的,所以只能由我们自己想办法。

请将第三个参数WS_OVERLAPPDWINDOW换成:

WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

接下来要做的就是把六个WS_XXXX一个一个地去掉,每去掉一次就重新编译一次,看看窗口会变成什么样子。首先去掉WS_SYSMENU,也就是说第三个参数变成了:

WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

然后编译一次,您会Lucky地看到您的HelloWin.exe已经是面目全非了,窗口右上角的三个按钮不见了,左上角的小图标也不见了,用鼠标右击标题栏时不会出现系统菜单。由此我们已经知道WS_SYSMENU的含义了。接下来让我们看看WS_MINIMIZEBOX,WS_MAXIMIZEBOX是干什么用的,改成如下所示后,再编译

WS_OVERLAPPED |WS_CAPTION | WS_SYSMENU | WS_THICKFRAME

您会High地看到右上角只有一个按钮了,最大化,最小化两个按钮消失于时间和空间的尽头……以此类推,您可以一个一个的试。

第四,第五个参数分别是窗口对象在屏幕上的x,y坐标,准确地说是窗口对象的左上角的坐标。

第六,第七个参数分别是窗口对象的宽度和高度。

第八个参数父窗口句柄。您不知道父窗口是什么东东?好,请打开记事本程序,按CTRL+O,会发现出现一个窗口,这个窗口总是在记事本上面,这个窗口是记事本的子窗口,而记事本就是父窗口。OK?还不知道?………………HelloWin没有父窗口,也就没有子窗口。所以这一项选的是NULL。

HelloWin没有菜单,所以第九项也是NULL。

第十项,书上写的是,hInstance是程序的实例句柄,也就是说这个句柄代表的是程序自身。同时它也是WinMain()的参数之一。

第十一项,窗口对象的参数指针句柄。看到这么长的名字是不是头晕了?我不知道具体含义sorry......

CreateWindow()返回被创建的窗口的句柄,该句柄被存放在变量hwnd中。Windows中的每个窗口都有一个句柄,程序用句柄来引用窗口。许多Windows函数都需要使用hwnd作为参数,这样,Windows才能知道函数是针对哪个窗口的。如果一个程序创建了许多窗口,则每个窗口都有一个句柄。窗口句柄是Windows程序处理的最重要的句柄之一。

就这样,CreateWindows()调用返回后窗口已经创建完毕。Windows已经分配了一块内存,用来保存在创建窗口过程中设置的全部信息。

但是我们仍然无法在屏幕上看到窗口。还需要两个函数显示窗口。一个是:

ShowWindow(hwnd, iCmdShow);

hwnd是刚刚用CreateWindow()创建的窗口对象的句柄。我们要好好看一看第二个参数。这也是WinMain()的第四个参数,int类型。那么,这是干什么用的呢?Come on Baby!

第二个参数有三个常量可供选择。

SW_SHOWNORMAL SW_SHOWMAXIMIZED SW_SHOWMINNOACTIVE

这些都是干什么用的???跟上次一样,改代码!请把

ShowWindow(hwnd, iCmdShow);

改成

ShowWindow(hwnd, SW_SHOWMAXIMIZED) 或是 ShowWindow(hwnd, SW_SHOWMINNOACTIVE)

运行一次您就Know了。虽然名字是ShowWindow(),但是我们还是不能Show到HelloWin生成的窗口。因为我们需要重新画屏幕,也就是刷新。

UpdateWindow(hwnd);

该函数的功能是刷新窗口。现在终于可以看见窗口了。

while (GetMessage (&msg, NULL, 0, 0)) //从消息队列中获取消息
{
TranslateMessage (&msg) ; //转换某些键盘消息
DispatchMessage (&msg) ; //将消息发送给窗口过程
}

Windows为当前运行的每个程序维护一个消息队列。这一点很重要。当您点击鼠标左右键的时候会发生消息,当您点了某个按钮的时候也会发生消息。总之,不管您干什么,都会产生消息。这些消息会进入消息队列中。GetMessage()负责从消息队列中取出消息。

TranslateMessage()在按键时系统产生虚拟键消息(VK_TAB等等)。在接收虚拟键代码时该函数将相应的WM_CHAR代码发送到应用程序消息队列中。具体的意思嘛我不懂啊。不过这个东东暂时好像不是很重要。
DispatchMessage()从应用程序的消息循环中发送消息至相应的窗口过程。

消息有入队与不入队之分。入队消息按照顺序一个一个送到窗口过程,就像排队买票一样。不入队消息的优先级是比较高的,可以直接发送给窗口过程。就好像领导不用排队买电影票一样。while循环处理的都是入队的消息。

<think>好的,用户问的是关于WS_POPUP窗口的介绍,我需要详细解释这个概念。首先,我得确定用户对Windows编程的基础了解程度,可能需要从基本概念讲起。WS_POPUP是窗口样式的一种,用于创建弹出窗口。但用户可能不知道这些细节,所以得先说明它属于窗口样式,属于Win32 API的一部分。 接下来,我需要解释WS_POPUP的具体用途,比如常见的弹出对话框、菜单、工具提示等。这时候要举例子,比如MessageBox或者自定义对话框,这样用户更容易理解。同时,要对比WS_OVERLAPPED或WS_CHILD,说明WS_POPUP的不同之处,比如没有边框和标题栏,通常置顶显示。 然后,用户可能想知道如何实际使用这个样式。这时候需要给出代码示例,用CreateWindow或CreateWindowEx函数,并指出在dwStyle参数中指定WS_POPUP。但要注意,代码中的参数可能需要其他样式组合,比如WS_VISIBLE,或者调整窗口的位置和大小,否则窗口可能不可见或位置不对。 另外,必须提到与WS_CHILD的区别,强调WS_POPUP不能作为子窗口,而WS_CHILD必须要有父窗口。这点很重要,避免用户混淆两种样式。 还有,注意事项部分,用户可能遇到的问题需要提前告知。比如,使用WS_POPUP后要自己处理绘制和非客户区,可能需要处理消息循环,或者结合其他样式如WS_CAPTION来添加标题栏。这些实际开发中的常见问题,给出提示能帮助用户避免踩坑。 最后,总结一下WS_POPUP的适用场景和特点,让用户有个清晰的回顾。要确保语言简洁,结构清晰,分点说明,方便用户逐步理解。同时检查是否需要补充其他相关样式或高级用法,但根据问题范围,可能不需要展开,保持回答的针对性。 整个过程中,要确保技术细节准确,比如API函数名、样式名称的正确写法,以及代码示例的正确性。同时用中文口语化表达,避免过于生硬的技术术语,让不同水平的用户都能理解。可能还需要考虑用户的实际需求,比如他们是否在开发中遇到具体问题,或者只是概念上的了解,因此回答要兼顾理论和实践,提供足够的信息帮助解决问题。</think>关于WS_POPUP窗口的逐步解释: 1. **基本定义** - WS_POPUP是Windows API中定义的一种窗口样式(Window Style),用于创建没有父窗口的顶级弹出式窗口。 - 宏定义值:`#define WS_POPUP 0x80000000L` - 必须通过`CreateWindowEx`函数的`dwStyle`参数指定。 2. **典型特征** ```c HWND hwnd = CreateWindowEx( 0, // 扩展样式 CLASS_NAME, // 窗口类 L"弹出窗口", // 标题 WS_POPUP | WS_VISIBLE, // 关键样式组合 100, 100, // 位置(X,Y) 300, 200, // 尺寸(宽,高) NULL, // 无父窗口 NULL, // 无菜单 hInstance, NULL ); ``` - ✅ 没有边框和标题栏(需配合其他样式才能添加) - ✅ 默认出现在所有窗口最上层 - ❌ 不能与WS_CHILD样式同时使用 3. **常见应用场景** - 自定义对话框(例如游戏中的设置面板) - 浮动工具栏(如Photoshop的色板窗口) - 通知气泡(类似QQ的消息提醒) - 屏幕覆盖层(如录屏软件的选区工具) 4. **与WS_OVERLAPPED的区别** | 特性 | WS_POPUP | WS_OVERLAPPED | |---------------------|----------------|-------------------| | 默认尺寸 | 需要显式指定 | 包含系统装饰尺寸 | | 任务栏显示 | 可选 | 默认显示 | | 最小化/最大化按钮 | 需手动添加 | 默认包含 | 5. **注意事项** - **Z序问题**:需要通过`SetWindowPos`调整窗口层级 - **关闭处理**:必须自行处理WM_CLOSE消息 - **DPI适配**:建议调用`EnableNonClientDpiScaling` - **样式组合**:常配合以下样式使用: ```c WS_POPUP | WS_CLIPCHILDREN | WS_SYSMENU ``` 实际开发中,现代UI框架(如WPF/WinUI3)会封装这些底层细节,但理解WS_POPUP机制对调试窗口层级问题仍有重要意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值