使用 Microsoft 登录
登录或创建帐户。
你好,
使用其他帐户。
你有多个帐户
选择要登录的帐户。

症状

即使释放鼠标左键,滚动条也会连续滚动。 滚动条的类型与此问题无关,也就是说,无论滚动条是窗口的一部分还是滚动条控件,都将出现相同的问题。

原因

如果在接收某个滚动条通知消息时执行的操作结果是执行的,则通常会出现此问题。滚动时,将在 Windows 中启动内部消息检索循环。 此邮件循环的任务是跟踪滚动并发送相应的滚动条通知消息,WM_HSCROLL 和 WM_VSCROLL。 WM_LBUTTONUP 收到后,滚动将终止。 如果在滚动过程中启动另一个消息循环,则该消息循环会检索 WM_LBUTTONUP,并且由于应用程序无法访问滚动条的内部消息检索循环,因此无法正确调度 WM_LBUTTONUP。 因此,内部消息检索器从不接收 WM_LBUTTONUP,并且滚动永不结束。滚动的应用程序不必显式检索消息即可导致此问题。 在滚动时,调用以下任何函数或处理具有消息检索循环的任何消息都可能会导致 WM_LBUTTONUP 丢失。 下面列出的函数分为以下类别:

DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()

解决方案

滚动时,不应通过任何消息检索循环(而不是滚动条的内部消息)从队列中检索 WM_LBUTTONUP 消息。应用程序可能会遇到此问题,如下所示:

  • 应用程序实现邮件检索循环以实现后台处理,例如后台处理,同时执行耗时的画图。

  • 应用程序实现消息检索循环以实现与另一个应用程序或 DLL 的通信。 例如,为了滚动,应用程序需要从其他位置接收数据。

可能的解决方法

下面列出了两种可能的解决方法。 第一个解决方法由许多现有应用程序和 Windows 使用;但是,在极少数情况下,第一种解决方法可能不可行。 在这种情况下,可以使用第二种解决方法。 但是,如果可能,请尽量避免在滚动时完全实现邮件检索。

  • 使用计时器-基于消息的处理。 将复杂的处理分解为较小的任务,并跟踪每个任务开始和结束的位置,然后根据计时器消息执行每个任务。 处理完所有组件后,终止计时器。 有关此解决方法的示例,请参阅下面的示例。

  • 实现邮件检索循环,但请确保它不会检索 WM_LBUTTONUP。 这可以通过使用筛选器来实现。 有关此解决方法的一些示例,请参阅下文。

演示解决方法1的示例

应用程序具有复杂的画图过程。 调用 ScrollWindow ()以滚动,将生成 paint 消息。 在绘制时进行后台处理。

  1. 收到 WM_PAINT 邮件时,请执行下列操作:

    1. 拨打 BeginPaint ()。

    2. 将无效的 rect 复制到要在步骤2中使用的全局 rect 变量(例如 grcPaint)。 全局矩形 grcPaint 将是先前获取的 rect (grcPaint)和新的无效矩形(ps rcPaint)的联合。 此操作的代码将类似于以下内容:

               RECT grcPaint;    // Should be initialized before getting the                           // first paint message.            :            :         UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
    3. 用 ValidateRect ()拨打 ps rcPaint。

    4. 拨打 EndPaint ()。

    5. 设置计时器。

    这样,不再生成 WM_PAINT 消息,因为没有无效区域,并且设置了计时器,这将生成 WM_TIMER 消息。

  2. 收到 WM_TIMER 消息时,请检查全局 rect 变量;如果不为空,请获取一个分区并进行绘制。 然后调整全局 rect 变量,使其不再包含绘制的区域。

  3. 全局 rect 变量为空后,将终止计时器。

演示解决方法2的示例

应用程序需要通过 DDE 或其他应用程序中的其他机制获取一些数据,然后在窗口中显示这些数据。 为了滚动,应用程序需要请求,然后从服务器应用程序获取数据。有三种不同的筛选器可用于设置 PeekMessage ()和获取信息。 可以使用 PeekMessage ()的 uFilterFirst 和 uFilterLast 参数设置筛选器。 uFilterFirst 指定要检查的范围内的 fist 消息,uFilterLast 指定要检查的范围中的最后一条消息。

  1. 仅检查和检索用于获取所需数据的相关消息。

  2. 检查 WM_LBUTTONUP 但不将其从队列中删除;如果它在队列中,请断开。 否则,检索并发送所有消息。

  3. 检索小于 WM_LBUTTONUP 和大于 WM_LBUTTONUP 的所有消息,但不检索 WM_LBUTTONUP。

更多信息

重现问题的步骤

下面是导致 WM_LBUTTONUP 消息丢失的事件序列:

  1. 使用鼠标单击滚动条。

  2. 步骤1生成 WM_NCLBUTTONDOWN 消息。

  3. 步骤2导致启动 Windows 内部消息循环。 此消息循环查找与滚动条相关的消息。 此消息循环的目的是生成合适的 WM_HSCROLL 或 WM_VSCROLL 消息。 消息循环和滚动将在收到 WM_LBUTTONUP 后终止。

  4. 当接收 WM_HSCROLL 或 WM_VSCROLL 消息时,应用程序要么直接获取消息检索循环,要么调用导致消息检索的函数。

  5. 在步骤4中提及的消息循环中,将从队列中删除 WM_LBUTTONUP。 然后调度 WM_LBUTTONUP。

  6. 由于步骤 5 WM_LBUTTONUP 消息在其他地方被调度,而内部邮件检索循环在步骤3中提及,而不会收到。 步骤3中的消息循环查找 WM_LBUTTONUP 停止滚动。 由于未收到,滚动条将继续滚动。

需要更多帮助?

需要更多选项?

了解订阅权益、浏览培训课程、了解如何保护设备等。

社区可帮助你提出和回答问题、提供反馈,并听取经验丰富专家的意见。

此信息是否有帮助?

你对语言质量的满意程度如何?
哪些因素影响了你的体验?
按“提交”即表示你的反馈将用于改进 Microsoft 产品和服务。 你的 IT 管理员将能够收集此数据。 隐私声明。

谢谢您的反馈!

×