新增Win方向键支持工作记录
本文记录新增Win方向键支持工作全过程中的点点滴滴。
问题描述
测试反馈,竞品软件支持Win+方向键改变窗口位置、支持将软件拖动到屏幕四周时出现放大预览边框,自身不支持,要求支持
问题调研
经过多方查找资料(查阅官方文档、询问ChatGPT/Kimi等),得知上述描述的是 Aero Snap
功能。
Aero Snap基本原理
Aero Snap 是 Windows 系统提供的一种窗口管理功能,它利用了 Windows 的窗口消息处理机制和桌面窗口管理器(DWM)来实现窗口的自动布局和调整。
当用户将窗口拖动到屏幕边缘时,系统会发送特定的消息给窗口,通知窗口调整其大小和位置。
窗口的边框在这个过程中起到了关键的作用,因为它是系统识别窗口边界和触发 Aero Snap 功能的依据。
系统识别
Windows 系统通过窗口的消息循环和事件处理机制来识别 Aero Snap 边框。
当用户将窗口拖动到屏幕边缘时,系统会检测到鼠标事件,并根据窗口的位置和屏幕边界来判断是否触发 Aero Snap 功能。
系统会根据窗口的样式和边框定义来调整窗口的大小和位置。
窗口的边框样式可以通过窗口类的风格(如 WS_THICKFRAME)来定义,这告诉系统该窗口支持大小调整和边框拖动操作。
//以上总结来自 Kimi,提问词为:"系统识别 Aero Snap 边框"
问题解决过程
在了解该功能后,得知本软件不支持该功能的根本原因是缺少 WM_THICKFRAME 属性,增加上应该就行了。如果这样简单解决了,就没有后话了。
增加该属性,的确可以使能Aero Snap
功能,但增加后,软件边框表现不正常,而且顶部还有白色边框,如下图所示:
上图中间的白色额外边框,以及软件边框上面的渐变阴影效果。
上图是软件左侧边框之外的渐变阴影效果。
通过 Spy++
软件以及调试,发现这个渐变阴影效果会影响窗口边框的绘制。比方说,边框宽度是10,按照10来绘制,实际上只显示了5,另外的5被渐变阴影边框占据,这部分的绘制,找了许多方法和材料,都无法实现。还有顶部6px空白边框,也无法绘制,问题就停在这里了。
遇到难题,在钻研无果时,先放个几天,稍后再处理。
几天之后
再次看这个问题,目的是支持Win键移动和鼠标拖动放大效果,方法是增加WM_THICHFRAME
,但增加后出现了边框绘制问题,暂时没好方法解决,那么想一想,有没有不添加 WM_THICHFRAME
属性,但也支持Win键移动和鼠标拖动放大效果呢?
继续拆分问题:
- 添加
WM_THICHFRAME
属性,但解决边框显示问题 <-- 暂时没方法 - 不添加
WM_THICHFRAME
属性,支持Win键移动 - 不添加
WM_THICHFRAME
属性,支持鼠标拖动
不就是响应Win+方向键移动嘛,那可不可以自己实现这种效果?解决不了所有问题,能解决其中一个子问题,也是进步。
想到这里,大方向定了,接下来就是分析功能点,逐个功能点实现就行。
-
使用已有软件,执行Win+方向键,梳理在单屏幕、双屏幕下的操作逻辑,整理操作记录,总结出操作规律,为后续开发做指导。
-
截取Win键自定义。查阅资料后得知Win键是特殊功能键,无法在
PreTranslateMessage
中截获,经过一番探索,采用自定义钩子截获Win键,在这过程中又遇到了 钩子被系统释放、维护窗口状态、获取屏幕工作区大小、双屏支持等问题,遇到问题,关注问题本身并着力解决就行。
经过上述实现,在不添加 WM_THICHFRAME
属性,能够支持Win键移动,虽说在双屏幕中间更改时还有些问题,不影响该功能总体表现,就先这样提交吧。
几个月之后
产品陆续接到若干反馈,说不支持鼠标拖动到屏幕边沿自动更改大小的问题。埋下的雷,几个月后终究还是爆发了。
再回顾这个问题,决定从头分析。从一个最简单的demo工程入手,一步一步增加属性,使其与最终软件一样,自绘四周边框,发现能够在具有 WM_THICKFRAME
属性的情况下,完美绘制四周边框。有了这一点,就为后续解决这个问题提供指引了,这个问题是可以解决的,边框绘制不对是自己的问题。
在后续解决中,在最简单demo上的参数验证以及向ChatGPT进行精准地提问,寻求各种不同的解决思路,并不断尝试,终于被我找到了一个解决方案。
这个方案提供了三种方法,按照第一种方法,关闭非客户区渲染(含阴影)
// 禁用非客户区渲染(含阴影)
DWMNCRENDERINGPOLICY policy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(GetSafeHwnd(), DWMWA_NCRENDERING_POLICY, &policy, sizeof(policy));
使用这种方法,能够在具备 WM_THICHFRAME
属性的基础上,窗口边框外观自绘正常,bingo!
收尾工作
在找到解决方法之后,也要看下这个的副作用。毕竟是调用陌生的函数,这个接口最低支持Windows Vista
,因此要做好相关入口屏蔽工作。
精简修复代码,只添加与其紧密相关的代码。
修复完这个历时多月的问题,心中仿佛一块重石落地,晚上睡觉都更安稳了。
小结
工作中遇到难题,不用慌。将难题分解成几个小难题,可以先从小来解决。
构建最小demo,一步一步重现并解决问题。
利用好Kimi、ChatGPT等AI工具,在全面了解问题的基础上,提炼切中要害的问题并提问,并且继续追问,立即验证。
解决完问题后,做好总结记录。