当_WIN32_WINNT大于0x500时,ToolTip窗口不显示问题排查

目录

1、前言

2、回退代码后,ToolTip窗口不显示了

3、使用历史版本比对法找到ToolTip窗口何时开始不显示的

4、为了给字体设置ClearType属性,_WIN32_WINNT宏的值从0x500修改成0x501

5、将_WIN32_WINNT宏值由从0x500修改成0x501,导致系统的ToolTip窗口组件出问题

6、解决办法


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html

1、前言

       最近测试同事发现,在Slider滑块控件显示百分比的ToolTip时有问题,即在拖动滑块时ToolTip窗口没有显示实时的百分比,显示的还是之前的百分比,如下所示:

在排查这个问题的过程中遇到了一系列问题,在此做个总结,以供大家借鉴和参考。

2、回退代码后,ToolTip窗口不显示了

       这个问题是必现的。用老版本对比了一下,老版本中这个滑块控件的ToolTip显示是没有问题的。那说明这个问题肯定是修改代码,改出来的。于是,到svn上查看代码的修改记录,果然前段时间有人修改过相关代码。于是尝试将修改的代码还原,看看还没有问题,验证一下是不是这次修改引起的。

       将代码还原之后,重新编译执行,发现问题更严重了,ToolTip提示窗口直接不显示了这就有点奇怪了,老版本中是没问题的,估计更早的时候还有人修改过代码,导致ToolTip提示窗口不显示了。


         在这里,给大家重点推荐一下我的几个热门畅销专栏:

专栏1:(该专栏订阅量已达到400个,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据近几年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的实战问题分析实例,带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

专栏中的文章均是通过项目实战总结出来的(通过项目实战积累了大量的异常排查素材和案例),有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域的多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

开源组件及数据库技术icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html

以多年的开发实战为基础,分享一些开源组件及数据库技术!


3、使用历史版本比对法找到ToolTip窗口何时开始不显示的

       我们之前讲过可以使用历史版本比对法去排查一些问题,即安装不同时间点的版本,看看问题是从哪个时间点开始出现的。找到这个时间点后,大概就是前一天提交的代码或者底层发布过来的新dll库导致的。

       于是使用二分法安装不同时间点的版本,发现之前的版本长时间都有ToolTip不显示的问题,一直没有人反馈,所有界面的ToolTip都有问题。一直找到一年前的某个版本没有问题。于是继续使用二分法缩小范围,最终找到ToolTip窗口从那一天开始不显示的。

      于是到svn上查看前一天的代码修改记录,在directui库的Stdafx.h头文件中定义了_WIN32_WINNT宏,用该宏指定当前程序支持的最低操作系统版本,将该_WIN32_WINNT宏的值由0x500改成了0x501,事后证明正是这个修改导致的ToolTip窗口不显示的。

4、为了给字体设置ClearType属性,_WIN32_WINNT宏的值从0x500修改成0x501

       为什么要将_WIN32_WINNT宏的值从0x500修改成0x501呢?当时我们的软件在设置高DPI的电脑上运行,系统会对软件界面进行放大,放大后字体会比较模糊,为了将文字显示的更清晰一点,给绘制文字的字体设置ClearType属性,这样绘制出来的文字会更有棱角一点,会清晰一些。操作系统也支持设置ClearType选项,如下所示:

       如何给字体设置ClearType属性呢?我们在使用LOGFONT结构体创建字体时,直接给LOGFONT结构体中的lfQuality字段设置CLEARTYPE_QUALITY值即可,如下所示:

       跳转到CLEARTYPE_QUALITY宏的定义处:

可以看到只有当定义的NT版本达到0x501,才定义这个CLEARTYPE_QUALITY宏,但我们的directui工程中将_WIN32_WINNT宏值定义为0x500,于是为了支持CLEARTYPE_QUALITY宏,直接_WIN32_WINNT的值由0x500改成0x501。

5、将_WIN32_WINNT宏值由从0x500修改成0x501,导致系统的ToolTip窗口组件出问题

       开始出问题的时间点之前值只对这个_WIN32_WINNT宏值做了修改,难道是这个宏值对系统的ToolTip窗口也有影响?于是将_WIN32_WINNT宏的值重新改成0x500,将使用CLEARTYPE_QUALITY宏的代码注释掉,重新编译运行,ToolTip窗口就能正常显示了。果然与_WIN32_WINNT宏的值有关系!

        那将_WIN32_WINNT宏的值设置成为0x501,为啥会导致ToolTip窗口组件出问题呢?这个要看ToolTip窗口对应的结构体TOOLINFO,可以跳转到这个结构体的定义处,如下所示:

TOOLINFO最后一个字段是void *lpReserved,这个字段有点特殊,当NTDDI_VERSION大于NTDDI_WINXP时才会定义。

       我们可以go到这两个宏的定义处,NTDDI_WINXP宏的值为0x05010000

而NTDDI_VERSION是直接与_WIN32_WINNT相关的:

       本例中我们将_WIN32_WINNT宏改成0x501,所以#if (NTDDI_VERSION >= NTDDI_WINXP)条件为真,所以当前场景下,TOOLINFO最后一个字段void *lpReserved是有效的

       我们在定义TOOLINFO结构体对象时,给该结构体的cbSize字段赋值的是sizeof(TOOLINFO):

即当前TOOLINFO结构体的大小,包含void *lpReserved字段的。Windows系统默认加载的是comctl 5.82版本组件,这个版本中的ToolTip组件对应的TOOLINFO结构体中的size不包含void *lpReserved字段,而我们当前给cbSize字段设置的大小是包含void *lpReserved的,所以cbSize字段设大了,所以ToolTip组件内部出错了,导致ToolTip窗口显示不出来了。

       在Win32编程中使用ToolTip窗口组件时,先是调用CreateWindowEx创建一个TOOLTIPS_CLASS类窗口,然后给这个窗口发送TTM_ADDTOOL消息将定义的TOOLINFO结构体对象值(TOOLINFO结构体中存放的是给ToolTip窗口设置属性),如下所示:

应该是这一步设置进去后就出问题了。

6、解决办法

       在给TOOLINFO结构体对象的cbSize字段赋值时,不再赋sizeof(TOOLINFO),而是赋TTTOOLINFOA_V2_SIZE宏值

::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO));// 赋值为TTTOOLINFOA_V2_SIZE,不用sizeof(TOOLINFO)
m_ToolTip.cbSize = TTTOOLINFOA_V2_SIZE; 

这个宏值就是将void *lpReserved字段去除后的TOOLINFO结构体大小。可以查看TTTOOLINFOA_V2_SIZE宏的定义看出来:

#define TTTOOLINFOA_V1_SIZE CCSIZEOF_STRUCT(TTTOOLINFOA, lpszText)
#define TTTOOLINFOW_V1_SIZE CCSIZEOF_STRUCT(TTTOOLINFOW, lpszText)// 1、TTTOOLINFOW_V2_SIZE宏的定义
#define TTTOOLINFOA_V2_SIZE CCSIZEOF_STRUCT(TTTOOLINFOA, lParam)
#define TTTOOLINFOW_V2_SIZE CCSIZEOF_STRUCT(TTTOOLINFOW, lParam)
#define TTTOOLINFOA_V3_SIZE CCSIZEOF_STRUCT(TTTOOLINFOA, lpReserved)
#define TTTOOLINFOW_V3_SIZE CCSIZEOF_STRUCT(TTTOOLINFOW, lpReserved)// 2、CCSIZEOF_STRUCT宏的定义
#ifndef CCSIZEOF_STRUCT
#define CCSIZEOF_STRUCT(structname, member)  (((int)((LPBYTE)(&((structname*)0)->member) - ((LPBYTE)((structname*)0)))) + sizeof(((structname*)0)->member))
#endif// 3、TTTOOLINFO宏的定义
typedef struct tagTOOLINFOW {UINT cbSize;UINT uFlags;HWND hwnd;UINT_PTR uId;RECT rect;HINSTANCE hinst;LPWSTR lpszText;LPARAM lParam;
#if (NTDDI_VERSION >= NTDDI_WINXP)void *lpReserved;
#endif
} TTTOOLINFOW, NEAR *PTOOLINFOW, *LPTTTOOLINFOW;

根据TTTOOLINFOA_V2_SIZE的定义,TTTOOLINFOA_V2_SIZE就是只计算到TOOLINFO倒数第二个参数lParam的长度,包含lParam成员长度,正好将最后一个字段void *lpReserved给跳过去了,这样ToolTip就能正常显示了。

       也可以使用#pragma comment预编译指令,将comctrl指定为使用6.0版本

#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'\"")

对应范例如下:

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>#pragma comment(lib, "user32.lib")
#pragma comment(lib, "comctl32.Lib")
#pragma comment(lib, "gdi32.Lib")#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'\"")

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

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

相关文章

OpenCV开发笔记(七十六):相机标定(一):识别棋盘并绘制角点

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/136535848 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子(红模仿…

C++ 网络编程学习五

C网络编程学习五 网络结构的更新单例模式懒汉单例模式饿汉单例模式懒汉式指针智能指针设计单例类 服务器优雅退出asio的多线程模型IOServiceasio多线程IOThreadPoolepoll 和 iocp的一些知识点 网络结构的更新 asio网络层&#xff0c;会使用io_context进行数据封装&#xff0c;…

SenseNova 商汤日日新大模型 Function Call(函数调用)功能讲解和应用示例

考虑到使用 magic 申请 OpenAPI 的账号挺麻烦的&#xff0c;这里以商汤日日新大模型 SenseNova 介绍 Function Call 的功能。 官方链接&#xff1a;日日新开放平台 一、Function Call 是个啥&#xff1f; 在 LLM&#xff08;Large Language Model&#xff09; 语言大模型时代&…

Redhat Linux(RHEL) - Primavera P6 EPPM 安装及分享

引言 继上一期发布的Oracle Linux版环境发布之后&#xff0c;近日我又制作了基于Redhat Linux 的P6虚拟机环境&#xff0c;同样里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机&#xff0c;请先与Oracle Primavera销售代表…

Unity之PUN实现多人联机射击游戏的优化

目录 &#x1f3ae;一、 跳跃&#xff0c;加速跑 &#x1f3ae;二、玩家自定义输入昵称 &#x1f345;2.1 给昵称赋值 &#x1f345;2.2 实现 &#x1f3ae;三、玩家昵称同步到房间列表 &#x1f345;3.1 获取全部玩家 &#x1f345;3.2 自定义Player中的字段 &#…

如何利用WebRTC构建点对点的即时通讯工具

在当今竞争激烈的商业环境中&#xff0c;企业越来越需要构建自己的即时通讯工具来提升内部沟通效率和信息安全&#xff0c;减少第三方工具依赖带来的潜在风险&#xff0c;并能与自身的行业业务深入融合。 拥有专用的通讯平台能够加快信息的流动&#xff0c;提升工作协同和任务执…

通过网口或串口走Modbus协议,读写数据库中的数据

智能网关IGT-DSER方便实现多台PLC与数据库之间的数据通讯&#xff0c;既可以读取PLC的数据上报到数据库&#xff0c;也可以从数据库查询数据后写入到PLC的寄存器&#xff0c;还可以将数据库的数据转为Modbus服务端/从站&#xff0c;实现数据库内的数据也可以走Modbus协议通过网…

springmvc学习笔记2

springmvc学习笔记2 springmvc响应数据页面跳转控制开发模式介绍快速返回逻辑视图jsp页面创建配置jsp视图解析器mvc初始化handler返回视图 转发和重定向实现返回json数据&#xff08;重点静态资源处理 RestFull风格设计和实战风格介绍实战 扩展知识全局异常处理拦截器拦截器声明…

什么是字节码?采用字节码的好处是什么?

在 Java 中&#xff0c;JVM 可以理解的代码就叫做字节码&#xff08;即扩展名为 .class 的文件&#xff09;&#xff0c;字节码是一种中间代码&#xff0c;它是由源代码经过编译生成的一种二进制表示形式。字节码通常不针对特定的硬件平台&#xff0c;而是针对虚拟机设计的&…

面试题系列一之-css画三角形(原理解析)

用html写一个三角形的图标算是一个比较简单的,但是工作中用的还是比较多的&#xff0c;面试也可能会问&#xff0c;但了解背后的原理才能熟练使用 我们首先写一个div,设置边框 <body><div class"border"></div> </body> <style> .bo…

MySQL 篇-深入了解事务四大特性及原理

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 事务的概述 2.0 事务的特性 2.1 原子性 2.2 一致性 2.3 持久性 2.4 隔离性 2.4.1 脏读问题 2.4.2 不可重复读问题 2.4.3 幻读问题 3.0 事务的四个隔离级别 3.1…

【LeetCode】升级打怪之路 Day 17:二叉树题型 —— 二叉树的序列化与反序列化

今日题目&#xff1a; 297. 二叉树的序列化与反序列化652. 寻找重复的子树 目录 LC 297. 二叉树的序列化与反序列化 【classic】 ⭐⭐⭐⭐⭐1&#xff09;序列化逻辑2&#xff09;反序列化逻辑 LC 652. 寻找重复的子树 【稍有难度】 今天主要学习了二叉树的序列化和反序列化相关…