新增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功能,但增加后,软件边框表现不正常,而且顶部还有白色边框,如下图所示:

image.png

上图中间的白色额外边框,以及软件边框上面的渐变阴影效果。

image.png

上图是软件左侧边框之外的渐变阴影效果。

通过 Spy++ 软件以及调试,发现这个渐变阴影效果会影响窗口边框的绘制。比方说,边框宽度是10,按照10来绘制,实际上只显示了5,另外的5被渐变阴影边框占据,这部分的绘制,找了许多方法和材料,都无法实现。还有顶部6px空白边框,也无法绘制,问题就停在这里了。

遇到难题,在钻研无果时,先放个几天,稍后再处理。

几天之后

再次看这个问题,目的是支持Win键移动和鼠标拖动放大效果,方法是增加WM_THICHFRAME,但增加后出现了边框绘制问题,暂时没好方法解决,那么想一想,有没有不添加 WM_THICHFRAME 属性,但也支持Win键移动和鼠标拖动放大效果呢?

继续拆分问题:

  1. 添加 WM_THICHFRAME 属性,但解决边框显示问题 <-- 暂时没方法
  2. 不添加 WM_THICHFRAME 属性,支持Win键移动
  3. 不添加 WM_THICHFRAME 属性,支持鼠标拖动

不就是响应Win+方向键移动嘛,那可不可以自己实现这种效果?解决不了所有问题,能解决其中一个子问题,也是进步。

想到这里,大方向定了,接下来就是分析功能点,逐个功能点实现就行。

  1. 使用已有软件,执行Win+方向键,梳理在单屏幕、双屏幕下的操作逻辑,整理操作记录,总结出操作规律,为后续开发做指导。

  2. 截取Win键自定义。查阅资料后得知Win键是特殊功能键,无法在 PreTranslateMessage 中截获,经过一番探索,采用自定义钩子截获Win键,在这过程中又遇到了 钩子被系统释放、维护窗口状态、获取屏幕工作区大小、双屏支持等问题,遇到问题,关注问题本身并着力解决就行。

经过上述实现,在不添加 WM_THICHFRAME 属性,能够支持Win键移动,虽说在双屏幕中间更改时还有些问题,不影响该功能总体表现,就先这样提交吧。

几个月之后

产品陆续接到若干反馈,说不支持鼠标拖动到屏幕边沿自动更改大小的问题。埋下的雷,几个月后终究还是爆发了。

再回顾这个问题,决定从头分析。从一个最简单的demo工程入手,一步一步增加属性,使其与最终软件一样,自绘四周边框,发现能够在具有 WM_THICKFRAME 属性的情况下,完美绘制四周边框。有了这一点,就为后续解决这个问题提供指引了,这个问题是可以解决的,边框绘制不对是自己的问题。

在后续解决中,在最简单demo上的参数验证以及向ChatGPT进行精准地提问,寻求各种不同的解决思路,并不断尝试,终于被我找到了一个解决方案。

image.png

这个方案提供了三种方法,按照第一种方法,关闭非客户区渲染(含阴影)

    // 禁用非客户区渲染(含阴影)
    DWMNCRENDERINGPOLICY policy = DWMNCRP_DISABLED;
    DwmSetWindowAttribute(GetSafeHwnd(), DWMWA_NCRENDERING_POLICY, &policy, sizeof(policy));

使用这种方法,能够在具备 WM_THICHFRAME 属性的基础上,窗口边框外观自绘正常,bingo!

收尾工作

在找到解决方法之后,也要看下这个的副作用。毕竟是调用陌生的函数,这个接口最低支持Windows Vista,因此要做好相关入口屏蔽工作。

精简修复代码,只添加与其紧密相关的代码。

修复完这个历时多月的问题,心中仿佛一块重石落地,晚上睡觉都更安稳了。

小结

工作中遇到难题,不用慌。将难题分解成几个小难题,可以先从小来解决。

构建最小demo,一步一步重现并解决问题。

利用好Kimi、ChatGPT等AI工具,在全面了解问题的基础上,提炼切中要害的问题并提问,并且继续追问,立即验证。

解决完问题后,做好总结记录。

posted @ 2025-05-21 10:56  浩天之家  阅读(23)  评论(0)    收藏  举报