如果我切换到另一个应用程序我的MFC应用程序冻结

时间:2009-06-22 14:28:17

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

开始这个问题的V 2.0。

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

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

我的MFC应用程序的主对话框包含一个名为m_MainTabControl的CTabCtrl。此主选项卡控件保留在两个标签为“基本设计”和“高级设计”的选项卡上,每个选项卡都与其自己的对话框相关联。两个对话框都包含一个带有多个选项卡的CTabCtrl。因此,我的问题就此消失了。

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

当我第一次提出这个问题时,我就是这样做的。我认为我的程序运行得很好,直到我再玩了一遍,并注意到当它在它和另一个窗口或程序之间来回翻转时它不能很好地播放。更具体地说,一旦我的程序失去焦点,一些进程就会变得混乱并占用整个CPU的处理能力。

现在,我解释了我的小标签结构并将问题集中在如此精细的细节上:经过多次实验,我发现如果我在外部标签结构上注释掉第二个标签(即“高级设计”对话框和所有的小孩子标签)我的程序的剩余部分运行正常,我可以切换到另一个窗口,然后不打架。 “太棒了,”我想,“这只是我节目的90%左右,我只是为了让这个工作得以评论。让我们试着进一步削减它。”我重新插入了“高级设计”选项卡式对话框,但注释掉了“高级设计”中选项卡控件创建的选项卡式对话框,一切都运行正常。我一个接一个地放回属于“高级设计”选项卡控件的选项卡式对话框,每次重新引入错误时,无论我取消注释哪个选项卡。我还应该指出,我尝试重新插入的第一个选项卡之一(当然是单独的)是我的“边距”选项卡,这是一个MarginDlg类,我也可以使用我的“基本设计”TAB而不会有任何问题。这让我相信,当我在选项卡式对话框中创建选项卡式对话框时,我必须要做一些具体的事情,而这些对话框我没有做,或者做得不正确,有点像我不得不集中精力使它最初工作。< / p>

我非常感谢任何可以解决问题的光。我包括我认为相关的代码片段;一如既往,如果需要更多,请告诉我。

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

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

当我的应用程序启动时,它会创建我的主对话框,并调用OnInitDialog(): (TODO之上的一切:注释是处理对话愚蠢的默认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;
}

4 个答案:

答案 0 :(得分:4)

调试您的应用程序,使其进入此状态,然后进入Debug / Break All。在Threads窗口中找到主线程,然后查看调用堆栈。调用堆栈中的某个位置将是导致挂起的代码。

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

答案 1 :(得分:2)

当您运行某个进程而不允许消息泵运行时,通常会出现您描述的行为。

你的应用程序做了什么?我假设你启动它,然后点击一个按钮或选择一个菜单项来开始一些过程。

如果程序在你第一次启动它时(在你点击开始处理之前)表现正常,但是在开始这个过程后你的行为就像你描述的那样,那就是你的问题。

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

答案 2 :(得分:0)

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

  • 包含多个CPropertySheet s
  • CPropertyPage
  • CTabCtrl
  • 中的CPropertyPage
  • CDialog
  • 中有多个CTabCtrl
  • 在一个特定的CDialog中使用控件然后切换到另一个应用程序(例如,通过命中断点的Visual Studio)将停止我的应用程序(相应的CPU核心最终以100%负载结束)
  • 在切换到其他应用程序之前,使用同一特定CDialog 中的控件但切换到另一个CPropertyPage 不会导致任何问题

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

这会阻止能够标记到CDialog,但这个问题稍微不那么严重,我可能会在另一天再回来。

答案 3 :(得分:0)

我有一个类似的问题。我有多个嵌套窗口,有些具有WS_EX_CONTROLPARENT样式,有些则没有。关键是:所有内部窗口必须具有这种样式,或者根本不需要(至少很明显)。