截至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
。
当然我使用双缓冲。然而似乎不可能摆脱那种讨厌的抖动。我还尝试了另一款用于英特尔HD 4000图形引擎的驱动程序,但无济于事。我忽略了什么吗?这是Windows 8的错误吗?
顺便说一句,当我拖动"显示窗口内容时,拖动"选项(在高级系统设置菜单中),所有其他应用程序显示相同的行为。
非常感谢任何帮助。
干杯,爱德蒙。PS:禁用"拖动时显示窗口内容"是不可取的,因为它内置了我的应用程序的功能。
Edit1:似乎d7samurai正在努力解决同样的问题。
跟进:我已经尝试了很多东西来摆脱抖动,但没有任何帮助。问题是W8根本不做它应该做的事情。以SWP_NOREDRAW
标志为例。正如预期的那样,已经移动的窗口侧的重新绘制被抑制,到目前为止一直很好。但是......(窗口的另一面),仍然有效,这个重新粉刷!它不仅是完全不必要和无用的,而是重新涂上了抖动!而且,与W7和XP相比,绘画速度要慢两倍。花了整整一个星期来解决这个问题后,我完全通过了W8!它真的是一个POC,负责这项工作的人都精神错乱。我真的希望W9会做得更好。阿门。
你沉默不语:
似乎上述异常不仅限于W8。当W7设置为"最佳外观时,W7也表现出相同的行为。或至少#34;启用桌面组合"与"在窗口和按钮上使用视觉样式"。然后我认为可以通过不使用SetWindowPos函数来欺骗窗口,而是通过发送ShowWindow( hWnd, SW_MAXIMIZE )
命令并拦截WM_GETMINMAXINFO
msg来指定所需的大小和位置。你猜怎么着?我还有抖动。 Arghhh !!!
下一步该怎么做?是否有可能在更低/更深的层次上拦截绘画(挂钩?)以便以合适的方式重绘窗户?
更新dd 03-26-2015,这是一个非常肮脏的伎俩:
Eureka!我终于找到了问题的解决方案。但首先,让我解释一下发生了什么以及为什么拖动伴随着抖动。假设您要展开窗口的左侧。与扩展右侧相反,这是在两个步骤中完成的。首先,将具有原始大小的窗口内容移动到左侧。完成此操作后,内容将扩展到新右侧与前一个右侧重合的程度。简而言之,它是"移动"的组合。然后是"调整大小"这就是导致丑陋抖动的原因。拖动窗口顶部时会发生类似的情况。在Windows 8.1或10中,您无法对其进行任何操作。我还尝试过新的DWM功能(BufferedPaintInit
,BeginBufferedPaint
,EndBufferedPaint
,BufferedPaintUnInit
,但无济于事。 SetWindowPlacement()
也会产生抖动。显然,所有这些功能都会导致破坏调整大小的罪魁祸首。
然后我推断当你创建一个新窗口并以新的大小显示它时,它根本不会抖动。我会说,相当明显。因此,重复创建一个新窗口并随后销毁前一个窗口可能是一个解决方案,但当然这并不是非常有效。在玩这个想法的同时,我偶然发现用一个新的/另一个大小显示隐藏的窗口,然后隐藏前一个窗口,也不会显示抖动。此过程比创建/销毁序列更有效。所以在我的程序开始时我只创建了两个窗口,第一个隐藏,第二个窗口可见。下面的代码片段应该更详细地解释无抖动调整大小的过程:
........
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,具体取决于窗口的大小及其内容。但至少可怕的抖动消失了。
我确实意识到这是一个非常肮脏的伎俩,所以如果有人知道更简洁的解决方案,请告诉我们。
答案 0 :(得分:1)
在Windows 8.1及更高版本中,您必须与DWM合作而不是绕过它。
这意味着您必须通过DwmGetCompositionTimingInfo
找出合成发生的时间表,并确保在适当的时刻将渲染事件插入队列。您可能还发现需要在WM_SIZING事件之后重新启动并重新绘制窗口,或者甚至使用自己的计时器绘制比发送WM_SIZING事件更频繁的时间。例如。如果DWM以50 fps合成,但你每秒只获得WM_Sizing事件,那么如果你使用自己的计时器GetWindowPos
,你可能比你依赖WM_SIZING更频繁地绘制。
您可能会发现在绘画后明智地使用DwmFlush
也可以帮助减少这种抖动。
答案 1 :(得分:0)
我找到了解决问题的方法。请查看原始邮件的最新更新。
答案 2 :(得分:0)
两个窗口的解决方案非常有创意!我认为我无法应用该解决方案,因为我的窗口是状态繁重的OpenGL窗口,必须在各个窗口实例之间进行维护/复制。
我想我可以为您提供更多的见解,说明尽管与SWP_NOREDRAW
和SWP_NOCOPYBITS
之类的所有常见嫌疑人一团糟,您为什么仍然看到这种抖动。
我遇到了同样的抖动问题,发现由于Windows 8/10 Aero下的应用程序不会直接绘制到屏幕上,而是绘制到屏幕外缓冲区,然后由邪恶的DWM.exe窗口管理器合成,因此事实证明,DWM实际上在现有的旧版XP / Vista / 7 BitBlt
行为的基础上又添加了BitBlt
型行为的另一层。
DWM的blit行为更加疯狂,因为它们不仅复制客户区,而且实际上在旧客户区的边缘复制像素以制作新客户区。不幸的是,使DWM不做主宰比仅传递一些额外的标志要困难得多。
您的两窗口技巧似乎使DWM没做额外的处理,但是我正在寻找一种方法来用一个窗口完成操作。
我没有100%的解决方案,但我希望上面的信息会有所帮助,并且我确实有实现Ben的好主意的代码,以尽量减少DWM盲目地进行攻击的机会:
享受!