拖动窗口的左边框时无法摆脱抖动

时间:2014-07-30 17:48:13

标签: c windows winapi

截至2015年3月26日,问题已解决,请参阅本页底部,这是一个非常肮脏的伎俩。

截至2014年8月18日,部分解决了:DWM是罪魁祸首(见最后评论)。

我使用win32 API构建了自己的窗口样式。在Windows XP和Windows 7下,一切都很顺利。然而,在Windows 8下发生了奇怪的事情:拖动窗口的左边界会导致右侧出现极端抖动,而它根本不应该移动。 看一下this,你会理解我的意思。

当我拖动右边框时,左侧不会按原样移动。不可否认,有一些闪烁,但这是可以接受的。见this

我已尝试过SetWindowPos()(begin/End)DeferWindowPos多个标志,但无济于事。即使使用SWP_NOREDRAW并阻止所有其他绘画也无济于事。无论是否有CS_HREDRAWCS_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功能(BufferedPaintInitBeginBufferedPaintEndBufferedPaintBufferedPaintUnInit,但无济于事。 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,具体取决于窗口的大小及其内容。但至少可怕的抖动消失了。

我确实意识到这是一个非常肮脏的伎俩,所以如果有人知道更简洁的解决方案,请告诉我们。

3 个答案:

答案 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_NOREDRAWSWP_NOCOPYBITS之类的所有常见嫌疑人一团糟,您为什么仍然看到这种抖动。

我遇到了同样的抖动问题,发现由于Windows 8/10 Aero下的应用程序不会直接绘制到屏幕上,而是绘制到屏幕外缓冲区,然后由邪恶的DWM.exe窗口管理器合成,因此事实证明,DWM实际上在现有的旧版XP / Vista / 7 BitBlt行为的基础上又添加了BitBlt型行为的另一层。

DWM的blit行为更加疯狂,因为它们不仅复制客户区,而且实际上在旧客户区的边缘复制像素以制作新客户区。不幸的是,使DWM不做主宰比仅传递一些额外的标志要困难得多。

您的两窗口技巧似乎使DWM没做额外的处理,但是我正在寻找一种方法来用一个窗口完成操作。

我没有100%的解决方案,但我希望上面的信息会有所帮助,并且我确实有实现Ben的好主意的代码,以尽量减少DWM盲目地进行攻击的机会:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

享受!