翻译《The Old New Thing》- What does the CS_OWNDC class style do?


What does the CS_OWNDC class style do? - The Old New Thing (microsoft.com)icon-default.png?t=N7T8https://devblogs.microsoft.com/oldnewthing/20060601-06/?p=31003


Raymond Chen 2006年06月01日


 简要

本文讨论了CS_OWNDC窗口类样式的影响,它让窗口管理器为窗口创建一个永久的设备上下文(DC),并始终返回同一个DC。这会导致代码中假设每次调用GetDC会得到不同DC的逻辑出现问题,因为实际上多次调用可能返回相同的DC,从而破坏了依赖于此假设的绘图代码。

复制再试一次分享

正文

        回想一下,窗口DC(设备上下文)通常只是临时使用。如果你需要在窗口中绘图,你可以调用BeginPaint,或者在绘制周期之外调用GetDC,尽管通常应避免在绘制周期之外进行绘制。窗口管理器为窗口生成一个DC并返回它。你使用这个DC,然后将其恢复到原始状态,并用EndPaint(或ReleaseDC)将其返回给窗口管理器。在内部,窗口管理器保持一个小的DC缓存,当有人请求窗口DC时,它会从这个缓存中提取,当DC被返回时,它会重新进入缓存。由于窗口DC只是临时使用,通常未处理的DC数量不会超过几个,一个小的缓存就足以满足正常运行系统中DC的需求。

        如果你注册一个窗口类并在类样式中包含CS_OWNDC标志,那么窗口管理器会为窗口创建一个DC,并将其放入DC缓存中,并打上一个特殊标记,意味着“不要从DC缓存中清除这个DC,因为它是这个窗口的CS_OWNDC”。如果你调用BeginPaintGetDC来获取一个CS_OWNDC窗口的DC,那么那个DC将总是被找到并返回(因为它被标记为“永不清除”)。这样做的后果有好有坏,还有更坏的。

        好的方面是,由于DC是专门为窗口创建的,并且永远不会被清除,所以你不必担心在将其返回到缓存之前“清理DC”。无论你何时为一个CS_OWNDC窗口调用BeginPaintGetDC,你总是能得到那个特殊的DC回来。实际上,这就是CS_OWNDC窗口的全部意义所在:你可以创建一个CS_OWNDC窗口,获取它的DC,按照你喜欢的方式设置它(选择字体、设置颜色等),即使你释放了DC并在以后再次获取它,你将得到同一个DC回来,它将和你离开时一样。

        不好的方面是,你正在使用一个本应只临时使用的东西(一个窗口DC),并永久地使用它。早期版本的Windows对DC的数量有非常低的限制(大约八个左右),因此当DC不再需要时,尽快释放它们至关重要。尽管这个限制自那以后已经显著提高,但背后的原理仍然存在:不应轻率地分配DC。你可能已经注意到CS_OWNDC的实现仍然使用DC缓存;只是这些DC得到了一个特殊的标记,以便DC管理器知道要特别对待它们。这意味着大量的CS_OWNDC DC最终会“污染”DC缓存,减缓未来调用函数如BeginPaintReleaseDC的速度,这些函数需要搜索DC缓存。

        为什么DC管理器没有被优化以处理大量CS_OWNDC DC的情况?首先,正如我已经指出的,原始的DC管理器不需要担心大量DC的情况,因为系统根本就不可能首先创建那么多DC。其次,即使DC的数量限制提高后,重写DC管理器以优化CS_OWNDC DC的处理也没有太多意义,因为程序员已经被告知要节制使用CS_OWNDC。这是软件工程的一个实际问题:你能做的只有这么多。你决定做的每件事都是以牺牲其他事情为代价的。很难证明优化程序员被告知要避免并且事实上已经在避免的场景是合理的。你不会为某人滥用你的系统的情况优化。这就像花时间设计汽车发动机,以便在汽车没有油的情况下保持良好的燃油效率。

        更糟糕的是,大多数窗口框架库和几乎所有示例代码都假定你的窗口不是CS_OWNDC窗口。考虑以下代码,它使用两种字体绘制文本,使用第一种字体引导第二种字体中字符的位置。它看起来完全没问题,不是吗?

void FunnyDraw(HWND hwnd, HFONT hf1, HFONT hf2) {HDC hdc1 = GetDC(hwnd);HFONT hfPrev1 = SelectFont(hdc1, hf1);UINT taPrev1 = SetTextAlign(hdc1, TA_UPDATECP);MoveToEx(hdc1, 0, 0, NULL);HDC hdc2 = GetDC(hwnd);HFONT hfPrev2 = SelectFont(hdc2, hf2);for (LPTSTR psz = TEXT("Hello"); *psz; psz++) {POINT pt;GetCurrentPositionEx(hdc1, &pt);TextOut(hdc2, pt.x, pt.y + 30, psz, 1);TextOut(hdc1, 0, 0, psz, 1);}SelectFont(hdc1, hfPrev1);SelectFont(hdc2, hfPrev2);SetTextAlign(hdc1, taPrev1);ReleaseDC(hwnd, hdc1);ReleaseDC(hwnd, hdc2);
}

        我们为窗口获取了两个DC。在第一个中我们选择了我们的第一个字体;在第二个中,我们选择了第二个。在第一个DC中,我们还设置了文本对齐为TA_UPDATECP,这意味着传递给TextOut函数的坐标将被忽略。相反,文本将从“当前位置”开始绘制,并且“当前位置”将更新为字符串的末尾,这样下一次调用TextOut将从上一次结束的地方继续。 一旦两个DC设置好,我们就逐个字符绘制我们的字符串。我们查询第一个DC以获取当前位置,并在第二个字体中绘制相同x坐标(但稍低一些)的字符,然后我们用第一个字体绘制字符(这也推进了当前位置)。 完成文本绘制循环后,我们作为标准记账的一部分恢复两个DC的状态。

        函数的意图是绘制类似这样的东西,其中第一个字体比第二个大。

        如果窗口不是CS_OWNDC,那就是你得到的。你可以通过从我们的临时程序中调用它来尝试它:

HFONT g_hfBig;
BOOL
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs) {LOGFONT lf;GetObject(GetStockFont(ANSI_VAR_FONT),sizeof(lf),&lf);lf.lfHeight *= 2;g_hfBig = CreateFontIndirect(&lf);return g_hfBig != NULL;
}
void
OnDestroy(HWND hwnd) {if (g_hfBig) DeleteObject(g_hfBig);PostQuitMessage(0);
}
void
PaintContent(HWND hwnd, PAINTSTRUCT *pps) {FunnyDraw(hwnd, g_hfBig,GetStockFont(ANSI_VAR_FONT));
}

        但如果窗口是CS_OWNDC,那么坏事就发生了。你自己试试,将这一行wc.style = 0;改为wc.style = CS_OWNDC;你会得到以下意想不到的输出:

        当然,如果你了解CS_OWNDC的工作原理,这一点也不意外。理解的关键是要记住,当窗口是CS_OWNDC时,无论调用多少次GetDCGetDC都只是返回同一个DC。现在你只需要记住FunnyDraw函数,hdc1hdc2实际上是同一件事

void FunnyDraw(HWND hwnd, HFONT hf1, HFONT hf2) {HDC hdc1 = GetDC(hwnd);HFONT hfPrev1 = SelectFont(hdc1, hf1);UINT taPrev1 = SetTextAlign(hdc1, TA_UPDATECP);MoveToEx(hdc1, 0, 0, NULL);

        到目前为止,函数的执行相当正常。

    HDC hdc2 = GetDC(hwnd);

        由于窗口是一个CS_OWNDC窗口,返回给hdc2的DC与hdc1返回的是同一个。换句话说,hdc1 == hdc2!现在事情变得有趣了。

    HFONT hfPrev2 = SelectFont(hdc2, hf2);

        由于hdc1 == hdc2,这实际上做的是从DC中取消选择字体hf1,而选择字体hf2

    for (LPTSTR psz = TEXT("Hello"); *psz; psz++) {POINT pt;GetCurrentPositionEx(hdc1, &pt);TextOut(hdc2, pt.x, pt.y + 30, psz, 1);TextOut(hdc1, 0, 0, psz, 1);}

        现在这个循环完全崩溃了。在第一次迭代中,我们从DC获取当前位置,返回(0, 0),因为我们还没有移动它。然后我们在第二个DC中的位置(0, 30)绘制字母“H”。但由于第二个DC与第一个相同,实际上我们是在TA_UPDATECP模式的DC中调用TextOut。因此,坐标被忽略,字母“H”被显示出来(在第二种字体中),当前位置被更新为“H”之后。最后,我们绘制第一个DC中的“H”(它与第二个相同)。我们以为我们用第一个字体绘制,但实际上我们用第二个字体绘制。我们以为我们在(0, 0)绘制,但实际上我们在(x, 0)绘制,其中_x_是字母“H”的宽度,因为对TextOut(hdc2, ...)的调用更新了当前位置。

        因此,每次循环,字符串中的下一个字符都会以第二种字体显示两次。

        但等等,灾难还没有结束。看看我们的清理代码: 

    SelectFont(hdc1, hfPrev1);

        这将DC中的原始字体恢复。

    SelectFont(hdc1, hfPrev2);

        这将重新选择第一个字体!我们未能将 DC 恢复到其原始状态,结果把一个 "损坏的 "DC 放进了缓存。

        这就是我将 CS_OWNDC 描述为 "更糟糕 "的原因。它采用了曾经有效的代码,并违反了大多数人(通常没有意识到)对 DC 的假设,从而破坏了代码。

        你可能以为 CS_OWNDC 很糟糕。下次我将谈谈 CS_CLASSDC ,那更是场灾难。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/838002.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

品牌银饰售卖|基于SSM+vue的品牌银饰售卖平台的设计与实现(源码+数据库+文档)

品牌银饰售卖平台 目录 基于SSM+vue的品牌银饰售卖平台的设计与实现 一、前言 二、系统设计 三、系统功能设计 1前台功能模块 2后台功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题…

Redission分布式锁 - 抢课系统

使用Redission分布式锁与Kafka消息队列,实现学生抢课系统(高并发秒杀场景)。 目录 一、思路1.为频繁访问的信息设置缓存(1)登陆(2)课程任务信息(3)用户抢课记录 2.消息队…

知识图谱开发日志

应用于应用环境的配置.测试.发布 假如你写了一个web,并且测试调试都没有问题 并且,你想发给你的朋友,导师,或者部署到远程云服务器上 那么,你需要配置相同的软件,比如数据库,web服务器,必要的插件,库,etc…但这并不一定能保证软件的正常运行,因为别人可能使用完全不同的操作系统…

在VMware安装Androidx86_64系统要点

上篇使用VirtualBox安装过Androidx86_64系统,尝试了没有蓝牙共享的好方法。本篇记录下使用Vmware虚机安装改系统,并使用蓝牙共享功能。 1.准备材料 本篇安装环境是安装Window10_64位系统。需要下载好Vmware安装包,VMWare版本:VMw…

python:rename函数用法

在Pandas库中,rename函数是一个非常实用的方法,用于重命名DataFrame或Series的轴标签(如列名或索引)。以下是rename函数的基本用法、参数以及一些示例。 1.rename基本语法 DataFrame.rename(mapperNone, indexNone, columnsNone…

【以规划为导向的自动驾驶】Planning-oriented Autonomous Driving

ABSTRACT 研究背景: 现代自动驾驶系统是顺序化地排列多个任务模块, 近期的主流方法: ①为单个任务部署独立模型 ②设计具有分离式头部的多任务(multi-task)范式。 但是,这些方法会累积误差或任务间协同不足而不利于自动驾驶。 作者认为重…

【devops】Linux 日常磁盘清理 ubuntu 清理大文件 docker 镜像清理

日常磁盘清理 1、查找大文件 find / -type f -size 1G2、清理docker无用镜像(drone产生的残余镜像文件) docker system prune -a一、清理服务器磁盘 1、查找大文件 在Ubuntu系统中,你可以使用find命令来查找大文件。find命令是一个强大的…

从离线到实时:无锡锡商银行基于 Apache Doris 的数据仓库演进实践

作者:武基鹏,无锡锡商银行 大数据技术经理 编辑整理:SelectDB 技术团队 导读:为实现数据资产的价值转化以及全面数字化、智能化的风险管理,无锡锡商银行大数据平台经历从 Hive 离线数据仓库到 Apache Doris 实时数据仓…

5G技术相关部分图解

1、面向5G商用网络的全系列解决方案 面向5G商用网络的全系列解决方案涵盖了从核心网到接入网的各个方面,确保网络的高性能、高可靠性和高安全性 2、2\3\4\5G带宽图解 G带宽的提升将推动许多新型应用的发展,并提供更快速、更可靠的移动通信体验。然而…

Springboot3 链接Redis遇到的报错(本文仅记录保存,优质文章移步springboot专栏)

出现的报错: cannot connect to Redisedis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password is setredis wrong number of arguments for ‘auth’ command 其实上面的三个报错是不同界面显示的,后面两个是通过Ide…

git入门操作

一、介绍 Git是一个开源的分布式版本控制系统,由Linus Torvalds创建,用于有效、高速地处理从小到大的项目版本管理。 二、注册Git代码托管平台账号 以下几个平台可供选择: Gitee: https://gitee.com/(国内) Gitee(码云&…

【源码】2024全新多语言区块链交易所源码/期权交易/申购/币币秒合约交易所

全新ui,更新很多内容,具体看图,全部开源 全新多语言区块链交易所源码/期权交易/申购/币币秒合约交易所 - 吾爱资源网

03-数据结构(一)

链接:C# 数据结构_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1a541147Nk/?spm_id_from333.337.search-card.all.click&vd_source6eb7d966aa03ff5cb02b63725f651e68 链接:使用 C#.Net 学习掌握数据结构 (更新中)_哔哩哔哩_bilibili 一…

CheckStyle静态样式之道

优质博文:IT-BLOG-CN 在标准化的统一样式检查规范里,最为常用的统一样式工具是checkstyle插件,而不是国内阿里的代码规约插件。 【1】下载插件 【2】配置生效 配置生效及告警设置 【3】配置checkstyle.xml 官网地址 官网最新Releases 下面…

模拟量电机控制器PWM 输出隔离转换器4-20mA/0-5V/0-10V转50Hz/100Hz/1KHz/10KHz/100KHz

主要特性: 精度、线性度误差等级: 0.1、0.2、0.5 级4-20mA/0-5V/0-10V 等标准信号输入可选择RS485 通讯输入,支持 Modbus 协议PWM 信号输出,PWM 频率可选PWM 输出驱动能力可达 5A信号输入/信号输出 3000VDC 隔离可选择一进一出,一…

OpenAI 震撼发布:GPT-4o免费,实时语音视频交互开启新纪元

OpenAI 震撼发布:GPT-4o免费,实时语音视频交互开启新纪元 在仅仅问世17个月后,OpenAI 研制出了仿佛科幻片中登场的超级人工智能——GPT-4o,而且所有人都可以完全免费使用,让这个科技界的巨浪让人震撼无比!…

真JAVA代码审计之XSS漏洞

Part1 漏洞案例demo&#xff1a; 没有java代码审计XSS漏洞拿赏金的案例。 所以将就看看demo吧 漏洞原理&#xff1a;关于XSS漏洞的漏洞原理核心其实没啥好说的&#xff0c;网上一查一大堆。 反射性XSS漏洞 <% page language"java" contentType"text/ht…

图搜索算法-最短路径算法-贝尔曼-福特算法

相关文章&#xff1a; 数据结构–图的概念 图搜索算法 - 深度优先搜索法&#xff08;DFS&#xff09; 图搜索算法 - 广度优先搜索法&#xff08;BFS&#xff09; 图搜索算法 - 拓扑排序 图搜索算法-最短路径算法-戴克斯特拉算法 贝尔曼-福特算法&#xff08;Bellman-Ford&#…

克鲁斯CLOOS机器人维修知识分享

克鲁斯工业机器人是一种高度精密的自动化设备&#xff0c;广泛应用于制造业、物流等领域。为了确保机器人的正常运行&#xff0c;了解一些基本的CLOOS工业机械手维修知识是必不可少的。 【常见CLOOS机械臂故障及解决方法】 1. 机器人无法启动&#xff1a;检查电源是否正常&…

Milvus 安装与配置

一、环境准备 在安装 Milvus 之前&#xff0c;确保你的系统满足以下要求&#xff1a; 操作系统&#xff1a;Milvus 支持 Linux 操作系统&#xff0c;如 Ubuntu、CentOS 等。硬件资源&#xff1a;推荐使用具有足够 CPU、内存和 SSD 存储的机器。对于大规模数据集&#xff0c;高…