在我正在处理的应用程序中,在某些情况下应用程序运行得非常慢,在这些情况下我发现我的移动鼠标,定时器/绘制消息没有被处理。如果我以慢圈移动鼠标,我可以无限期地阻止窗口被重新绘制!
我发现这是expected behaviour:
除了WM_PAINT消息,WM_TIMER消息和 在WM_QUIT消息中,系统总是在a结束时发布消息 消息队列。这可确保窗口接收其输入消息 在正确的先进先出(FIFO)序列中。 WM_PAINT 但是,消息,WM_TIMER消息和WM_QUIT消息是 保留在队列中并仅在转发到窗口过程时 队列不包含其他消息。另外,还有多个WM_PAINT 同一窗口的消息组合成一个WM_PAINT 消息,将客户区的所有无效部分合并到一个 单一区域。组合WM_PAINT消息减少了a的次数 窗口必须重绘其客户区的内容。
但是,我该怎么办呢?有时直接响应鼠标移动,我需要尽快重新绘画。
我在CWnd派生类中通过这样的方法捕获消息:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
答案 0 :(得分:3)
正如您在文档中所读到的那样,WM_PAINT
和WM_TIMER
与其他消息不同。
实际差异在于它们是最低优先级消息。也就是说,如果队列中还有其他消息,则不会处理它们。
那就是说,你正在移动鼠标,因此发布了很多消息,但是这些消息的频率通常很低(几秒钟几十),所以你的程序应该大部分都是空闲的,但它不是,可能是因为这些消息中的一些消耗的时间远远超过预期,并且正在阻塞队列。你只需要检测哪一个以及为什么。
一些鼠标相关的消息,我不在乎:
WM_MOUSEMOVE
WM_NCMOUSEMOVE
WM_SETCURSOR
WM_NCHITTEST
通过网络搜索:
WM_MOUSEOVER
WM_MOUSELEAVE
WM_NCMOUSEOVER
WM_NCMOUSELEAVE
无论如何,如果你想立即重新粉刷,而不是等待WM_PAINT
,你应该致电UpdateWindow()
。此函数强制立即处理WM_PAINT
(如果有任何无效的东西),并阻塞直到它完成,绕过该消息的低优先级问题。
更新:根据您的评论中的情况,我认为您的最佳解决方案可能就是这样:
WM_MOUSEMOVE
中将光标位置保存在成员变量中并设置一个标志,表示鼠标已移动。OnIdle()
处理程序。如果不移动,什么也不做。如果被移动,则进行昂贵的计算。UpdateWindow()
调用OnIdle()
或不调用OnIdle()
的情况下尝试使用,并查看哪个更好。是的,在慢速计算机中它仍会感觉不稳定,但由于WM_TIMER
的优先级甚至低于WM_PAINT
和{{1}},因此这些消息不会无限期降级。更重要的是,您不会将多次调用排入昂贵的函数。
答案 1 :(得分:1)
几乎在所有情况下,正确的解决方案是当您更新应用程序状态以响应WM_MOUSEMOVE时需要重新绘制,您应该调用UpdateWindow()。这就对了。你不应该使用PeekMessage(),GetCursorPos,OnIdle()等。
如果你调用UpdateWindow(),那么将强制WM_PAINT消息,你的窗口将被更新,用户将看到他们的鼠标移动的反应,你的应用程序将感觉到响应。这很好。事实上,这是理想的。
如果不调用UpdateWindow(),则在传递WM_PAINT消息之前可能会出现另一条鼠标消息。大多数鼠标可以以128 / s的速度传送WM_MOUSEMOVE消息,触摸屏似乎以200 / s的速度运行,游戏鼠标可能更快。因此,如果用户快速移动鼠标,则WM_PAINT消息可能永远不会被强制传递。这使您处于每秒处理100多条WM_MOUSEMOVE消息但从不绘制窗口的情况。这意味着您的内部帧速率为100 fps,可见帧速率为0 fps。碎。
因此,再一次,答案是在需要绘制的WM_MOUSEMOVE处理之后调用UpdateWindow()。您的内部帧率可能较低(因为现在您为每条消息执行了更多工作),但您的可见帧率现在将与您的内部帧率相匹配,并且您的可见帧率很重要。
如http://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
所述,将悄悄地丢弃额外的WM_MOUSEMOVE消息我已经为许多应用程序做了这个修复,结果总是非常壮观。这种单行修复经常使应用程序的响应速度提高10倍。