【发布时间】:2014-09-22 11:50:15
【问题描述】:
截至 2015 年 3 月 26 日,问题已解决,请参阅本页底部,这是一个非常肮脏的把戏。
截至 2014 年 8 月 18 日,部分解决:DWM 是罪魁祸首(见最后一条评论)。
我已经使用 win32 API 构建了自己的窗口样式。在 Windows XP 和 Windows 7 下一切正常。然而,在 Windows 8 下奇怪的事情发生了:拖动窗口的左边框会导致右侧极度抖动,而它根本不应该移动。 看看this,你就会明白我的意思了。
当我拖动右边框时,左侧没有按应有的方式移动。诚然,有一些闪烁,但这是可以接受的。见this
我用几个标志尝试了SetWindowPos() 和(begin/End)DeferWindowPos,但无济于事。即使使用SWP_NOREDRAW 并阻止所有其他绘画也无济于事。不管有没有CS_HREDRAW 和CS_VREDRAW。
当然,我使用的是双缓冲。然而,要摆脱那种讨厌的不安似乎是不可能的。我还尝试了 Intel HD 4000 图形引擎的另一个驱动程序,但仍然无济于事。我忽略了什么吗?是 Windows 8 的 bug 吗?
顺便说一句,当我打开“拖动时显示窗口内容”选项(在高级系统设置菜单中)时,所有其他应用程序都会显示相同的行为。
任何帮助将不胜感激。
干杯,埃德蒙。
PS:禁用“在拖动时显示窗口内容”不是选项,因为它是我的应用程序的内置功能。
Edit1:d7samurai 似乎正在努力解决同样的问题。
跟进:我已经尝试了很多方法来消除抖动,但没有任何帮助。问题是 W8 根本没有做它应该做的事情。以SWP_NOREDRAW 标志为例。正如所料,已移动的窗口侧的重新粉刷被抑制,到目前为止一切顺利。但是....(窗户的)另一边,仍然有效,这个被重新粉刷了!它不仅完全没有必要和无用,而且还用抖动重新粉刷过!此外,与 W7 和 XP 相比,绘画速度要慢两倍。在这个问题上花了整整一周之后,我完全完成了 W8!这真的是一个 POC,负责这项糟糕工作的人精神错乱。真心希望W9能做得更好。阿门。
你应该颤抖:
上述异常似乎不仅限于 W8。当 W7 设置为“最佳外观”或至少设置为“启用桌面合成”以及“在窗口和按钮上使用视觉样式”时,W7 也显示出相同的行为。然后我认为可能完全不使用 SetWindowPos 函数来欺骗窗口,而是通过发送ShowWindow( hWnd, SW_MAXIMIZE ) 命令并拦截我指定所需大小和位置的WM_GETMINMAXINFO msg。你猜怎么了?我还是很紧张。啊!!!
接下来要做什么?是否有可能在较低/较深的级别(钩子?)拦截绘画以便以体面的方式重绘窗口?
更新 dd 03-26-2015,一个非常肮脏的把戏:
Eureka!我终于找到了解决问题的方法。但首先,让我解释一下发生了什么以及为什么拖动会伴随着抖动。假设您要扩展窗口的左侧。与扩展右侧相反,这是通过两个步骤完成的。首先,将具有原始大小的窗口内容向左移动。完成此操作后,内容被扩展至新的右侧与先前的右侧重合的程度。简而言之,它是“移动”和“调整大小”的组合,这就是导致丑陋抖动的原因。当您拖动窗口顶部时,也会发生类似的事情。在 Windows 8.1 或 10 中,您无能为力。遗憾的是,我还尝试了新的 DWM 功能(BufferedPaintInit、BeginBufferedPaint、EndBufferedPaint、BufferedPaintUnInit),但无济于事。 SetWindowPlacement() 也会产生抖动。显然,所有这些功能都会导致破坏调整大小的同一个罪魁祸首。
然后我推断,当您创建一个新窗口并以新大小显示它时,它根本不会抖动。我会说,相当明显。因此,反复创建一个新窗口并随后销毁前一个窗口可能是一种解决方案,但这当然不是很有效。在玩这个想法时,我偶然发现用新的/另一个大小使隐藏的窗口可见,然后隐藏前一个窗口,也不会显示抖动。此过程比创建/销毁序列更有效。所以在我的程序开始时,我只创建了两个窗口,第一个隐藏,第二个可见。下面的代码 sn-p 应该更详细地解释无抖动调整大小的过程:
........
i = prm->hpi; // get current index
h1 = prm->parent[i]; // get current handle
flags = SWP_NOCOPYBITS;
flags |= SWP_NOSENDCHANGING;
// if DWM tries to make a mess of it:
// DWM enabled top border right border
if( prm->parent[1] && ( rgn == TBE || rgn == LBE ) )
{
i = i + 1 & 1; // get new index
prm->hpi = i; // save new index
h2 = prm->parent[i]; // get new handle
prm->hParent = h2; // set as current one
flags |= SWP_NOREDRAW; // bypass message pump
flags |= SWP_SHOWWINDOW; // make h2 visible
SetWindowPos( h2, HWND_TOP, px, py, cx, cy, flags );
PaintParent( h2 ); // paint it now
DwmFlush(); // wait till finished
ShowWindow( h1, SW_HIDE ); // make h1 invisible
}
// DWM disabled or dragging the right or bottom border:
else SetWindowPos( h1, HWND_TOP, px, py, cx, cy, flags );
还有一点:使用DwmFlush()确保新窗口的所有绘制都完成,然后隐藏另一个窗口,否则会出现一些闪烁。当然,上述程序比普通程序要慢一些。在我的系统上(使用 i5-3570K 处理器)3..26ms,取决于窗口的大小及其内容。但至少那种可怕的不安已经消失了。
我确实意识到这是一个非常肮脏的把戏,所以如果有人知道更简洁的解决方案,请告诉我们。
【问题讨论】:
-
您使用什么代码来调整窗口大小?我的第一印象是您在设置 RECT 时存在错误,因为视频中有时会裁剪右上角的按钮。这可能是 Win8 唯一的错误,但是,我会先查看现有代码。
-
@Scott,代码太长,放在这里。所以我只放了几个相关的部分:`GetWindowRect(hwnd, &wrc);``GetCursorPos(&pt);``py = wrc.top;``cy = wrc.bottom - wrc.top;``px = pt。 x - 1 // 定义新位置` ` cx = wrc.right - px; // 定义新宽度` ` flags = SWP_NOCOPYBITS; // | SWP_NOREDRAW;` ` SetWindowPos( hwnd, HWND_TOP, px, py, cx, cy, flags );` >" 右上角的按钮被裁剪了" 其余的也被裁剪了。请仔细看看。此外,其他应用程序,甚至是资源管理器,表现也一样糟糕。
-
对不起,上面的混乱。我无法以体面的方式删除代码,因为“backtics”拒绝履行承诺。
-
我不确定你为什么要从 x 坐标中减去一个像素。然后你用它来调整矩形的宽度。这似乎有问题。
-
@Ben:“你认为视频很好,如果我是用户,我根本不会担心”。嗯,我儿子也是这么说的,但我是一个完美主义者,这些“小细节”真的很困扰我。此外,我的程序本身具有“完美主义的自负”(它会以极高的精度进行一些专门的计算)。出于这个原因,看起来邋遢的 GUI 不会与程序的其余部分保持一致。由于外人看不到引擎盖,他们会根据 GUI 的外观来判断。如果它看起来很草率,他们可能会认为其余部分也会很草率。