windows - 为什么我的 MFC 应用程序在与两个滚动条交互后挂起?

标签 windows debugging mfc

我正在开发一个 MFC 应用程序(在 Win10 下运行),其中包含一个图形 CAD 样式编辑器窗口。编辑器窗口包含用户可以重新定位和配置的图标。

在包含许多元素的布局中,我们发现编辑器窗口可能会挂起长达 30 秒。当我们的大多数用户使用 Windows 7 时,报告此问题;看来Windows 10开始出现这种情况,但我还没有回到Win7确认。

触发挂起的确切操作顺序是:

  1. 水平滚动(使用水平滚动条)
  2. 垂直滚动(使用垂直滚动条)
  3. 应用程序挂起大约 20-30 秒,然后恢复

上述步骤的微小变化也会触发临时挂起;例如,使用鼠标按钮垂直滚动,然后使用滚动条水平滚动,也会触发挂起。

挂起或锁定总是会恢复。我还注意到 Windows 中的其他应用程序会暂时“卡住”(例如:我无法拖动窗口,并且 UI 更新变得非常慢,对于发生此挂起时运行的所有应用程序) 。

我不确定从哪里开始调试,因为我有一种感觉,这发生在操作系统级别;我的第一个猜测是开始分析不同的代码行,以针对导致延迟的确切操作系统调用,但我不确定如果导致挂起的元素不是我的函数调用中的函数调用,那么这种方法的效果如何。代码;例如,操作系统中的某些队列可能几乎已满,导致消息泵变慢,而没有任何特定的操作系统调用看起来很慢。

我的问题是:

  1. Windows 10 是否有任何变化? CWnd 计数较大,这可能会与滚动交互导致挂起?
  2. Windows 提供了哪些工具来调试这种情况?我应该看看 WinDbg 吗?我应该专注于分析问题而不使用任何专用调试工具吗?

我将从内部应用程序的角度来解决这个问题(以排除我们的代码直接导致挂起的可能性),但是如果我们能够提供有关调试此问题的最佳方法的任何指导(假设我们我们的代码中没有像 30 秒函数调用这样明显的东西。


感谢您的评论。以下是一些新信息:

  • 此事件期间 CPU 使用率较低;徘徊在 1-3% 左右,所以我的代码没有受到 CPU 的瓶颈。
  • 我在入口点和导出点向 HSCROLL 和 VSCROLL 处理程序添加了 TRACE 语句。挂起似乎发生在进入我的 VSCROLL 处理程序之前,即用鼠标左键单击滚动条之后。
  • 该代码有一个 LButtonDown 处理程序,但单击滚动条时它似乎没有被点击
  • 该应用程序有 208 个 GDI 对象和 66 个 User 对象,因此我认为我们远远低于限制
  • 在所有经过测试的 Win10 电脑上都观察到此问题,并非单台机器所独有

现在要尝试 Spy++。

我没有看到处理程序有任何明显的问题,我的调试输出似乎排除了它们是罪魁祸首,但为了完整性,它们在这里:

void CDrawing60View::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    TRACE("OnHScroll:Begin\r\n");

    int i = 1;
    switch ( nSBCode )
    {
    case SB_LEFT :  //   Scroll to far left.
        i = 2 ;
        break ;
    case SB_ENDSCROLL : //   End scroll.
        i = 3 ;
        break ;

    case SB_LINELEFT :  //   Scroll left.  left arrow on left side of scroll bar
        i = 4 ;
        break ;

    case SB_LINERIGHT : //   Scroll right.  right arrow on right side of scroll bar
        i = 5 ;
        break ;

    case SB_PAGELEFT :  //   Scroll one page left.
        i = 6 ;
        break ;

    case SB_PAGERIGHT : //   Scroll one page right.
        i = 7 ;
        break ;

    case SB_RIGHT : //   Scroll to far right.
        i = 8 ;
        break ;

    case SB_THUMBPOSITION : //   Scroll to absolute position. The current position is specified by the nPos parameter.
        i = 9 ;
        break ;

    case SB_THUMBTRACK :    //   Drag scroll box to specified position. 
        i = 10;
        break ;
    }

    CFormView::OnHScroll(nSBCode, nPos, pScrollBar);

    CPoint p = GetScrollPosition(); // p = how much we have scrolled in the horizontal/vertical directions

    SNAP_TO_8_PIXELS (p.x);
    SNAP_TO_8_PIXELS  ( p.y)
    ScrollToPosition ( p ) ;

    MoveDrawing . LastScrollPositionX = p . x ; // used when saving the drawing
    MoveDrawing . LastScrollPositionY = p . y ;

    TRACE("OnHScroll:End\r\n");
}

void CDrawing60View::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    TRACE("OnVScroll:Begin\r\n");

    int i = 0;
    switch ( nSBCode )
    {
    case SB_BOTTOM :    //   Scroll to bottom.
        i = 2 ;
        break ;
    case SB_ENDSCROLL : //   End scroll.
        break ;
    case SB_LINEDOWN :  //   Scroll one line down.
        i = 2 ;
        break ;
    case SB_LINEUP :    //   Scroll one line up.
        i = 2 ;
        break ;
    case SB_PAGEDOWN :  //   Scroll one page down.
        i = 2 ;
        break ;
    case SB_PAGEUP :    //   Scroll one page up.
        i = 2 ;
        break ;
    case SB_THUMBPOSITION : //   Scroll to the absolute position. The current position is provided in nPos.
        i = 2 ;
        break ;
    case SB_THUMBTRACK :    //   Drag scroll box to specified position. The current position is provided in nPos.
        i = 2 ;
        break ;
    case SB_TOP :   //   Scroll to top. 
        i = 2 ;
        break ;
    }

    CFormView::OnVScroll(nSBCode, nPos, pScrollBar);
    CPoint p = GetScrollPosition(); // p = how much we have scrolled in the horizontal/vertical directions

    SNAP_TO_8_PIXELS (p.x);
    SNAP_TO_8_PIXELS ( p.y)
    ScrollToPosition ( p ) ;

    MoveDrawing . LastScrollPositionX = p . x ; // used when saving the drawing
    MoveDrawing . LastScrollPositionY = p . y ;

    TRACE("OnVScroll:End\r\n");
}

好吧,Spy++ 已经产生了一些有趣的结果。当我运行 Spy++ 时,我无法重现此问题!。我想知道这是否表明我的处理程序中存在竞争条件,因为我可以想象 Spy++ 的唯一效果是减慢速度。

最佳答案

这原来是 Windows 操作系统问题。 Microsoft 支持提供了高级解释:较新的 Windows 功能正在干扰我的应用程序。

看来我的调查指向了正确的方向: ntdll.dlldwm.exe 是罪魁祸首,而不是我的可执行文件。

Spy++ 解决了我的问题这一事实很能说明问题。这表明我们没有遇到严格的性能限制,并且还表明这不是我的应用程序停滞;而是我的应用程序停滞了。操作系统级别的某些东西正在干扰自身。

Microsoft 支持代表指示我使用 Windows ADK 中应用程序兼容性工具下包含的兼容性管理工具。使用此向导,我生成了一个填充数据库 (.sdb),其中包含 ScrollWindowsExFlags 的修复程序。

然后,我在提升的命令行中使用以下命令来安装 shim 数据库:

sdbinst -u <path to the sdb file>

来自 Microsoft 支持:

Having many child windows to scroll is the cause. When they are scrolled, DWM has to update the internal data for each window that is moved. Having too many of them overwhelms DWM.

关于windows - 为什么我的 MFC 应用程序在与两个滚动条交互后挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59655147/

相关文章:

java - 在java代码中将Windows风格的路径转换为unix路径

c# - log4net条目可以查询吗?

c++ - Eclipse 调试错误 : No such file or directory\n

c++ - MFC 应用程序中 Delphi 7 和 Delphi XE4 之间的 ActiveX 差异

c++ - 如何使用 boost thread win32-mt-s 构建 boost(与 mingw 交叉编译)

c++ - 如何确定 Windows 10 中是否打开了高对比度主题?

c++ - 使用 Visual Studio 编译 hunspell

c++ - 处理多个 WM_USER 消息(或实现相同的结果)

c++ - 在 C/C++ 中创建 Windows 服务的任何示例? (无图形用户界面)

c - Valgrind 的输出缺少基本值