Мое приложение MFC зависает, если я переключаюсь на другое приложение - PullRequest
2 голосов
/ 22 июня 2009

Начало V 2.0 этого вопроса.

Мое приложение VC ++ MFC компилируется и работает просто отлично. То есть, пока я не переключусь на другое окно. Как только моя программа теряет фокус, она зависает; Я не могу переключиться обратно на него, и если я переместлю окно, которое находится перед ним, мое окно приложения будет отображаться белым в пространстве, которое закрывало другое окно.

С тех пор, как я впервые опубликовал этот вопрос, я смог определить, какая часть моей программы вызывает такое поведение, но я до сих пор не знаю, какие строки кода неправильные или почему.

Основной диалог моего приложения MFC содержит CTabCtrl с именем m_MainTabControl. Этот основной элемент управления вкладками содержит две вкладки с надписью «Базовый дизайн» и «Расширенный дизайн», каждая из которых связана со своими собственными диалоговыми окнами. Оба диалоговых окна содержат CTabCtrl с несколькими вкладками. И таким образом моя проблема рождается.

Когда я впервые создал эту структуру вкладок внутри вкладок, у меня были проблемы с зависанием моей программы, когда я пытался переключаться между внутренними вкладками. Само собой разумеется, я был озадачен этим, пока не заметил, что, если я сначала нажму на элемент управления на одной из внутренних вкладок, то смогу переключать вкладки очень хорошо. Поэтому, не понимая полностью, в чем проблема, я установил фокус на первую внутреннюю вкладку первой внутренней вкладки при запуске программы. Проблема наполовину решена. Когда я запустил программу, я мог просто щелкнуть по первому набору внутренних вкладок; если бы я переключился на вторую внешнюю вкладку и попытался щелкнуть по ее внутренним вкладкам, не щелкая сначала по диалоговому элементу управления, он бы снова завис Поэтому я сделал функцию «Фокус», чтобы сфокусировать текущую выбранную внутреннюю вкладку выбранной в данный момент внешней вкладки, и вызвал ее в событии изменения вкладки (то, которое срабатывает при нажатии на другую вкладку).

Так я дошел до того момента, когда впервые задал этот вопрос. Я думал, что моя программа работает просто отлично, пока я не поиграл с ней немного больше и не заметил, что она играет не очень хорошо, когда я переключался между ним и другим окном или программой. Точнее говоря, как только моя программа выходит из фокуса, некоторые процессы теряют свою актуальность и занимают весь процессорный процесс.

А теперь, причина, по которой я так подробно объяснил свою маленькую структуру вкладок и проблемы с фокусировкой: после долгих экспериментов я обнаружил, что, если я закомментировал вторую вкладку в моей внешней структуре вкладок (то есть диалоговое окно «Расширенный дизайн» и все его дочерние вкладки) остальная часть моей программы работает нормально, и я могу переключиться на другое окно и вернуться без боя. «Отлично, - подумал я, - это всего лишь около 90% моей программы, которую я только что прокомментировал, чтобы это заработало. Давайте попробуем и дальше урезать». Я вернул диалоговое окно с вкладками «Advanced Design» обратно, но прокомментировал диалоги с вкладками, созданные элементом управления вкладками в «Advanced Design», и все по-прежнему работало нормально. Один за другим я возвращал диалоги с вкладками, принадлежащие элементу управления вкладками «Advanced Design», и каждый раз снова вводил «Ошибка» независимо от того, какую вкладку я раскомментировал. Я также должен отметить, что одной из первых вкладок, которые я попытался вернуть (конечно, в одиночку), была моя вкладка «Margins», которая является классом MarginDlg, который я также использую под вкладкой «Базовый дизайн» без проблем. Это заставляет меня верить, что есть что-то конкретное, что я должен делать при создании диалогов с вкладками в диалогах с вкладками, которые я не делаю или делаю неправильно, вроде того, как мне пришлось возиться с фокусировкой, чтобы сначала это работало.

Я был бы чрезвычайно благодарен за любой свет, который можно пролить на ситуацию. Я включаю то, что я считаю релевантными фрагментами кода; как всегда, дайте мне знать, если потребуется больше.

объявления переменных из файла .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);
    }
}

А Фокус () и изменение вкладки:

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 ]

4 голосов
/ 22 июня 2009

Отладьте ваше приложение, приведите его в это состояние, затем перейдите к Debug / Break All. Найдите основной поток в окне Threads, затем посмотрите на стек вызовов. Где-то в стеке вызовов будет ваш код, вызывающий зависание.

Обратите внимание, что для получения разумного стека вызовов вам необходимо указать Visual Studio на серверы символов Microsoft; см http://msdn.microsoft.com/en-us/library/b8ttk8zy.aspx

2 голосов
/ 22 июня 2009

Поведение, которое вы описываете, обычно происходит, когда вы запускаете какой-то процесс, не позволяя запустить насос сообщений.

Что делает ваше приложение? Я предполагаю, что вы запускаете его, а затем либо нажимаете кнопку, либо выбираете пункт меню, чтобы начать какой-то процесс.

Если программа ведет себя нормально при первом ее запуске (перед тем, как щелкнуть, чтобы начать обработку), но затем ведет себя так, как вы описали после запуска процесса, то это ваша проблема.

Вам придется переместить эту обработку в другой поток, чтобы графический интерфейс MFC оставался отзывчивым.

0 голосов
/ 19 марта 2019

У меня была похожая проблема. У меня было несколько вложенных окон, некоторые со стилем WS_EX_CONTROLPARENT, некоторые нет. Дело в том, что либо все внутренние окна должны иметь этот стиль, либо ни одного из них (по крайней мере, по-видимому).

0 голосов
/ 20 марта 2015

Я только что наткнулся на очень похожую ситуацию. Моя настройка:

  • A CPropertySheet, содержащий несколько CPropertyPage s
  • A CTabCtrl внутри одного CPropertyPage
  • Несколько CDialog с как часть CTabCtrl
  • Использование элемента управления в одном конкретном CDialog и последующее переключение на другое приложение (например, Visual Studio через точку останова попадания) остановит мое приложение (соответствующее ядро ​​ЦП будет загружено на 100%)
  • Использование элемента управления в том же конкретном CDialog , но затем переключение на другое CPropertyPage перед переключением на другое приложение не приведет к проблемам

После некоторого исследования я нашел эту статью в Базе знаний , которая подсказала мне решение. Конкретный CDialog, который вызвал остановку, имел строку EXSTYLE WS_EX_CONTROLPARENT в своем определении ресурса. Удаление линии решило проблему.

Это предотвращает возможность перехода на CDialog, но эта проблема немного менее серьезна, и я мог бы вернуться к ней в другой день.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...