【问题标题】:My MFC Application Freezes if I Switch to Another App如果我切换到另一个应用程序,我的 MFC 应用程序会冻结
【发布时间】:2010-11-04 21:04:33
【问题描述】:

开始这个问题的 V 2.0。

我的 VC++ MFC 应用程序编译并运行良好。也就是说,直到我切换到另一个窗口。一旦我的程序失去焦点,它就会冻结;我无法切换回它,如果我移动它前面的窗口,我的应用程序窗口在另一个窗口覆盖的空间中显示为白色。

自从我第一次发布这个问题以来,我已经能够查明我的程序的哪个部分导致了这种行为,但我仍然不知道哪些代码行是错误的或为什么。

我的 MFC 应用程序的主对话框包含一个名为 m_MainTabControl 的 CTabCtrl。这个主选项卡控件包含两个标记为“基本设计”和“高级设计”的选项卡,每个选项卡都与它们自己的对话框相关联。两个对话框都包含一个带有多个选项卡的 CTabCtrl。于是我的问题就诞生了。

当我第一次创建这个 tab-within-tab 结构时,当我尝试在内部选项卡之间切换时,我遇到了我的程序冻结的问题。不用说我对此感到困惑,直到我注意到如果我首先单击一个内部选项卡上的控件,我就可以很好地切换选项卡。因此,在没有完全理解问题所在的情况下,我将焦点设置在程序启动时第一个内部选项卡的第一个内部选项卡上。问题解决了一半。当我运行程序时,我可以点击第一组内部选项卡。如果我切换到第二个外部选项卡并尝试单击其内部选项卡而不首先单击对话框控件,它将再次冻结。所以我做了一个 Focus 函数来聚焦当前选定的外部选项卡的当前选定的内部选项卡,并在我的选项卡更改事件中调用它(当您单击另一个选项卡时会关闭)。

这就是我第一次提出这个问题时的目的。我以为我的程序运行得很好,直到我多玩了一会儿,发现当我在它和另一个窗口或程序之间来回切换时它玩得不好。更具体地说,一旦我的程序失去焦点,某些进程就会失控并占用整个 CPU 的处理价值。

现在,我如此详细地解释了我的小标签结构和焦点问题的原因:经过大量实验,我发现如果我注释掉外部标签结构上的第二个标签(即“高级设计”对话框和它所有的小子标签)我的程序的其余部分运行良好,我可以毫不费力地切换到另一个窗口并返回。 “太好了,”我想,“这只是我刚刚注释掉的程序的大约 90% 以使其正常工作。让我们尝试进一步削减它。”我将“高级设计”选项卡式对话框放回原处,但注释掉了由“高级设计”中的选项卡控件创建的选项卡式对话框,一切仍然正常。我一一放回属于“高级设计”选项卡控件的选项卡式对话框,每次我重新引入错误时,无论我取消注释哪个选项卡。我还应该指出,我尝试放回的第一个选项卡(当然是单独的)是我的“边距”选项卡,它是一个 MarginDlg 类,我也可以在我的“基本设计”选项卡下毫无问题地使用它。这让我相信,在选项卡式对话框中创建选项卡式对话框时,我必须做一些我没有做或做错的事情,就像我一开始必须搞乱焦点以使其工作一样。

我将非常感谢任何可以阐明这种情况的信息。我包括了我认为相关的sn-ps代码;与往常一样,如果需要更多信息,请告诉我。

来自主对话框的 .h 文件的变量声明:

//tab stuff
CTabCtrl m_MainTabControl;
vector<CDialog*> m_tabPages;
SimpDesDlg* simpDesDlg;
AdvDesDlg* advDesDlg;

当我的应用程序启动时,它会创建我的主对话框,并调用 OnInitDialog(): (TODO 上面的所有内容:comment 是默认的 VS 处理愚蠢的对话框的东西)

BOOL CspAceDlg::OnInitDialog()
{
    CDialog::OnInitDialog();


    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here
    ///////////////////////////////////////

    DrawResultsArea();
    DrawTabs();
    theApp.Calculate();
    Focus();
    //simpDesDlg->Focus();
    //DrawToolbar();

    return FALSE;  // return TRUE  unless you set the focus to a control
}

DrawTabs() 是我为外部选项卡控件创建选项卡的地方:

void CspAceDlg::DrawTabs()
{
    simpDesDlg = new SimpDesDlg;
    m_tabPages.push_back(simpDesDlg);
    advDesDlg = new AdvDesDlg;
    m_tabPages.push_back(advDesDlg);

    // create a tcItem to hold the name of each tab during creation
    // and then get inserted, and arrays holding the tab names and IDs of
    // the dialogs they refer to
    TC_ITEM tcItem;
    PSTR pszTabNames[] = {"Basic Design", "Advanced Design"};
    UINT pszTabItems[] = {IDD_SIMPLEDESIGNTAB, IDD_ADVANCEDDESIGNTAB};

    //every member of m_tabPages[] will become a tab
    for (int i = 0; i < int(m_tabPages.size()); i++)
    {
        //set up the tab name
        tcItem.mask = TCIF_TEXT;
        tcItem.pszText = pszTabNames[i];
        tcItem.cchTextMax = int(strlen(pszTabNames[i]));
        //insert the new tab into the tab control and create the dialog window
        m_MainTabControl.InsertItem(i, &tcItem);
        m_tabPages[i]->Create(pszTabItems[i], &m_MainTabControl);
    }

    //redraw so that the dialogs don't appear in upper left corner and cover the tabs
    CRect tabRect, itemRect;
    int nX, nY, nXc, nYc;

    m_MainTabControl.GetClientRect(&tabRect);
    m_MainTabControl.GetItemRect(0, &itemRect);

    nX=itemRect.left;
    nY=itemRect.bottom+1;
    nXc=tabRect.right-itemRect.left-1;
    nYc=tabRect.bottom-nY-1;

    m_tabPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);
    for(int nCount=1; nCount < int(m_tabPages.size()); nCount++){
        m_tabPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);
    }
}

主对话框的 Focus() 方法:

void CspAceDlg::Focus()
{
    int curSel = m_MainTabControl.GetCurSel();
    m_tabPages[curSel]->SetFocus();

    //if it's the basic design, we need to focus its first tab
    if (curSel == 0)
    {
        simpDesDlg->Focus();
    }
    //if it's the advanced design, we need to focus its first tab
    else if (curSel == 1)
    {
        advDesDlg->Focus();
    }
}

m_MainTabControl 的选项卡选择更改事件代码:

void CspAceDlg::OnTcnSelchangeBuildtabs(NMHDR *pNMHDR, LRESULT *pResult)
{
    for (int i = 0; i < int(m_tabPages.size()); i++)
    {
        m_tabPages[i]->ShowWindow(m_MainTabControl.GetCurSel() == i ? SW_SHOW :
                                                   SW_HIDE);
    }

    Focus();

    *pResult = 0;
}

SimpDesDlg 和 AdvDesDlg 的标签使用相同的代码,除了标签初始化(只是为每个标签创建不同的标签),所以这里是 AdvDesDlg 的代码:

OnInitDialog():

BOOL AdvDesDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    DrawTabs();

    return false;
}

在选项卡式对话框中添加:

void AdvDesDlg::DrawTabs()
{
    //Make the dialogs for the tabs
    antennaDlg = new AntennaDlg;
    commSEDlg = new CommSEDlg;
    encryptorDlg = new EncryptorDlg;
    marginDlg = new MarginDlg;
    miscDlg = new MiscDlg;
    transRecDlg = new TransRecDlg;

    //add them all to the tabPages vector
    m_tabPages.push_back(antennaDlg);
    m_tabPages.push_back(commSEDlg);
    m_tabPages.push_back(encryptorDlg);
    m_tabPages.push_back(marginDlg);
    m_tabPages.push_back(miscDlg);
    m_tabPages.push_back(transRecDlg);

    //m_tabPages[0] = new AntennaDlg;
    //m_tabPages[1] = new CommSEDlg;
    //m_tabPages[2] = new EncryptorDlg;
    //m_tabPages[3] = new MarginDlg;
    //m_tabPages[4] = new MiscDlg;
    //m_tabPages[5] = new TransRecDlg;

    //antennaDlg = (AntennaDlg*) m_tabPages[0];
    //commSEDlg = (CommSEDlg*) m_tabPages[1];
    //encryptorDlg = (EncryptorDlg*) m_tabPages[2];
    //marginDlg = (MarginDlg*) m_tabPages[3];
    //miscDlg = (MiscDlg*) m_tabPages[4];
    //transRecDlg = (TransRecDlg*) m_tabPages[5];

    // create a tcItem to hold the name of each tab during creation
    // and then get inserted, and arrays holding the tab names and IDs of
    // the dialogs they refer to
    TC_ITEM tcItem;
    PSTR pszTabNames[] = {"Antenna", "Comm Support", "Encryptor", "Margins", "Misc", "Trasmitter/Receiver"};
    UINT pszTabItems[] = {IDD_ANTENNATAB, IDD_COMMSETAB, IDD_ENCRYPTORTAB, IDD_MARGINTAB, IDD_MISCTAB, IDD_TRANSRECTAB};

    //every member of m_tabPages[] will become a tab
    for (int i = 0; i < int(m_tabPages.size())/*(sizeof(m_tabPages)/sizeof(m_tabPages[0]))*/; i++)
    {
        //set up the tab name
        tcItem.mask = TCIF_TEXT;
        tcItem.pszText = pszTabNames[i];
        tcItem.cchTextMax = int(strlen(pszTabNames[i]));
        //insert the new tab into the tab control and create the dialog window
        advTab.InsertItem(i, &tcItem);
        m_tabPages[i]->Create(pszTabItems[i], &advTab);
    }

    //redraw so that the dialogs don't appear in upper left corner and cover the tabs
    CRect tabRect, itemRect;
    int nX, nY, nXc, nYc;

    advTab.GetClientRect(&tabRect);
    advTab.GetItemRect(0, &itemRect);

    nX=itemRect.left;
    nY=itemRect.bottom+1;
    nXc=tabRect.right-itemRect.left-1;
    nYc=tabRect.bottom-nY-1;

    //m_tabPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);
    for(int nCount=/*1*/0; nCount < int(m_tabPages.size())/*(sizeof(m_tabPages)/sizeof(m_tabPages[0]))*/; nCount++){
        m_tabPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);
    }
}

以及 Focus() 和选项卡的变化:

void AdvDesDlg::Focus()
{
    this->SetFocus();
    for (int i = 0; i < int(m_tabPages.size())/*(sizeof(m_tabPages)/sizeof(m_tabPages[0]))*/; i++)
    {
        m_tabPages[i]->ShowWindow(advTab.GetCurSel() == i ? SW_SHOW :
                                                   SW_HIDE);
    }

    int curSel = advTab.GetCurSel();
    m_tabPages[curSel]->SetFocus();
}

void AdvDesDlg::OnTcnSelchangeAdvDesign(NMHDR *pNMHDR, LRESULT *pResult)
{
    for (int i = 0; i < int(m_tabPages.size())/*(sizeof(m_tabPages)/sizeof(m_tabPages[0]))*/; i++)
    {
        m_tabPages[i]->ShowWindow(advTab.GetCurSel() == i ? SW_SHOW :
                                                   SW_HIDE);
    }

    int curSel = advTab.GetCurSel();
    m_tabPages[curSel]->SetFocus();

    *pResult = 0;
}

【问题讨论】:

  • 当你启动你的程序时,在你做任何事情之前,它是否会占用 CPU?在我看来,您有一个失控的进程,阻止了消息的处理。在此调用堆栈和您在下面的评论(关于 OnInitDialog 中的焦点)之间,这绝对是对话框上所有控件之间的交互。如果您不知道哪些可能有隐藏的处理循环,请开始将它们注释掉,直到行为消失。当我在应该在线程中的主窗口循环中进行处理时,我肯定会得到你描述的情况(没有油漆,没有分钟)。
  • 对不起,但我不明白你所说的“在主窗口循环中处理应该在一个线程中”是什么意思。我正在上大学,所以还有一些东西我还没有学到,线程就是其中之一。我需要了解多少才能了解如何线程化一个进程,在这种情况下,我假设它与我的对话框上的控件有关?抱歉这里一无所知...
  • 另外,我只是在任务管理器打开的情况下运行我的程序,这样我就可以观察我的进程。当我第一次打开程序时,它占用了大约 5400K 的内存和 00 个 CPU。由于我只是单击浏览器窗口输入这些数字,因此程序进入冻结状态并增加了 25 个 CPU(周期?)。我不知道你认为什么会占用 CPU,但我猜它开始占用 0 的事实意味着它最初并没有占用它。
  • 最后,我会尝试注释掉控件。我想我应该一次只注释掉一个控件,然后看看当我移动到另一个窗口时程序是否仍然冻结,但是如果我可以隔离哪些控件正在造成混乱,你对我有什么建议吗?应该对它/他们做些什么来让它/他们表现出来,还是你需要看代码才能说?
  • 我实际上完全被你的描述弄糊涂了(什么都不做,当你点击离开时,它们会跳跃 25%)。一些控制变得疯狂。

标签: c++ visual-studio visual-studio-2008 visual-c++ mfc


【解决方案1】:

调试您的应用程序,使其进入此状态,然后进行调试/全部中断。在 Threads 窗口中找到主线程,然后查看调用堆栈。调用堆栈中的某处将是导致挂起的代码。

请注意,为了获得合理的调用堆栈,您需要将 Visual Studio 指向 Microsoft 的符号服务器;见http://msdn.microsoft.com/en-us/library/b8ttk8zy.aspx

【讨论】:

  • 有趣的是你无法调试这种情况。一旦断点命中,应用程序就会失去焦点并停止。如果您继续调试,您(可能)以CWnd::RunModalLoop() 结尾
【解决方案2】:

您描述的行为通常发生在您运行某个进程而不允许消息泵运行时。

你的应用是做什么的?我假设你启动它,然后点击一个按钮或选择一个菜单项来启动某个进程。

如果程序在您第一次启动时运行正常(在您单击开始处理之前),但在启动进程后又按照您描述的方式运行,那么这就是您的问题。

您必须将该处理移至另一个线程以允许 MFC GUI 保持响应。

【讨论】:

  • 当我运行我的应用程序时,我可以点击应用程序内的任何控件,运行计算等,一切都很好。一旦我尝试在我的应用程序之外做一些事情,例如打开一个新的 Windows 窗口或只是切换到现有的 Windows 窗口、运行另一个程序等,我的应用程序就会冻结。
  • 如果您启动您的应用程序,您能否在点击控件或菜单​​之前将其最小化?您是否在视图或文档的构造函数或 OnCreate 覆盖中开始任何处理?
  • 在单击控件/菜单之前或之后,我无法最小化我的应用程序(或调出另一个应用程序/窗口)。我没有覆盖任何 OnCreate(),但我确实使用 OnInitDialog() 来设置很多控件的初始数据。可能相关:在我的主对话框的 OnInitDialog() 中,我有一个选项卡控件,其中包含两个选项卡,每个选项卡中都有自己的选项卡控件。当我尝试在首先单击另一个控件或外部选项卡之前切换内部选项卡时,我的应用程序冻结问题,直到我发现我需要将焦点设置到我的默认内部选项卡。这要求我...
  • 这要求我在主对话框的 OnInitDialog() 上返回 FALSE 而不是 TRUE(VisualStudio 的默认 cmets: // 返回 TRUE,除非您将焦点设置为控件)。我尝试将其改回以返回 TRUE,但它重新引入了我之前遇到的问题并且没有解决我当前的问题。
【解决方案3】:

我偶然发现了一个非常相似的情况。我的设置:

  • 一个CPropertySheet包含几个CPropertyPages
  • CTabCtrl 内一个 CPropertyPage
  • 几个CDialogs 作为CTabCtrl 的一部分
  • 在一个特定的 CDialog 中使用控件,然后切换到另一个应用程序(例如,通过命中断点的 Visual Studio)将停止我的应用程序(相应的 CPU 内核最终处于 100% 负载)
  • 在同一特定CDialog 中使用控件但在切换到另一个应用程序之前切换到另一个CPropertyPage不会导致任何问题

经过一番研究,我找到了this Knowledge Base article,这提示我找到了解决方案。触发停顿的特定CDialog 在其资源定义中具有EXSTYLE WS_EX_CONTROLPARENT 行。删除线解决了问题。

这会阻止进入CDialog 的选项卡,但这个问题稍微不那么严重,我可能改天再来。

【讨论】:

    【解决方案4】:

    我遇到了类似的问题。我有多个嵌套窗口,一些具有 WS_EX_CONTROLPARENT 样式,一些没有。问题是:所有内部窗口都必须具有这种样式,或者它们都没有(至少显然如此)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-07
      相关资源
      最近更新 更多