Как опубликовать сообщение после того, как текущий экземпляр сообщения завершился в MF C? - PullRequest
1 голос
/ 30 мая 2020

Мне не хватает понимания сообщений в Windows приложениях.

У меня есть обработчик кнопок:

void CChristianLifeMinistryEditorDlg::OnFilePublicTalk()
{
    CWeekendMeetingDlg dlgPublicTalk(this);

    if (m_pEntry != nullptr)
    {
        dlgPublicTalk.SetPublicTalkInfo(m_pEntry->GetPublicTalkInfo());
        dlgPublicTalk.SetCircuitVisitMode(m_iIncludeMode == kIncludeServiceTalk); // AJT v17.0.7
        // AJT v20.0.1
        dlgPublicTalk.SetSongInfo(
            CChristianLifeMinistryUtils::UseSingOutJoyfullyToJehovah(m_datFirstMonday), m_eForeignLang);
        auto iResult = dlgPublicTalk.DoModal();
        if (iResult == IDOK || iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate) ||
                               iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
        {
            m_pEntry->SetPublicTalkInfo(dlgPublicTalk.GetPublicTalkInfo());

            SetModified(true);
            UpdatePreview(m_iDateIndex);
            m_pHtmlPreview->Refresh2(REFRESH_COMPLETELY); // Ensure it has refreshed

            // Padlock
            // Reminder
            // Disable controls

            if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate))
            {
                AfxMessageBox(_T("Move to previous week."));
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);

                // It can't process this message until the current instance one has finished

            }
            else if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
            {
                AfxMessageBox(_T("Move to next week."));
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);

                // It can't process this message until the current instance one has finished
            }
        }
    }
}

Я удалил код (заменен двумя AfxMessageBox calls), но в принципе я хочу перезапустить тот же обработчик событий, как если бы пользователь щелкнул его в меню «Файл». Можно ли использовать PostMessage, когда текущий экземпляр сообщения еще не завершен?


Я еще не пробовал предоставленный ответ, но я столкнулся с проблемой при вводе двух PostMessage звонки:

void CChristianLifeMinistryEditorDlg::OnFilePublicTalk()
{
    CWeekendMeetingDlg dlgPublicTalk(this);

    if (m_pEntry != nullptr)
    {
        dlgPublicTalk.SetPublicTalkInfo(m_pEntry->GetPublicTalkInfo());
        dlgPublicTalk.SetCircuitVisitMode(m_iIncludeMode == kIncludeServiceTalk); // AJT v17.0.7
        // AJT v20.0.1
        dlgPublicTalk.SetSongInfo(
            CChristianLifeMinistryUtils::UseSingOutJoyfullyToJehovah(m_datFirstMonday), m_eForeignLang);

        // AJT v20.1.8
        dlgPublicTalk.SetPreviousNextDateButtonStates(m_btnMovePrevious.IsWindowEnabled(),
                                                      m_btnMoveNext.IsWindowEnabled());
        auto iResult = dlgPublicTalk.DoModal();
        if (iResult == IDOK || iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate) ||
                               iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
        {
            m_pEntry->SetPublicTalkInfo(dlgPublicTalk.GetPublicTalkInfo());

            SetModified(true);
            UpdatePreview(m_iDateIndex);
            m_pHtmlPreview->Refresh2(REFRESH_COMPLETELY); // Ensure it has refreshed

            if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate))
            {
                PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_PREVIOUS_DATE, BN_CLICKED),
                    (LPARAM)m_btnMovePrevious.GetSafeHwnd());
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
            }
            else if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
            {
                PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_NEXT_DATE, BN_CLICKED),
                    (LPARAM)m_btnMoveNext.GetSafeHwnd());
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
            }
        }
    }
}

После выполнения действий и повторного отображения окна, я нажимаю «Отмена» и получаю:

enter image description here

Если я изменю его и напрямую вызову обработчики кнопок, например:

void CChristianLifeMinistryEditorDlg::OnFilePublicTalk()
{
    CWeekendMeetingDlg dlgPublicTalk(this);

    if (m_pEntry != nullptr)
    {
        dlgPublicTalk.SetPublicTalkInfo(m_pEntry->GetPublicTalkInfo());
        dlgPublicTalk.SetCircuitVisitMode(m_iIncludeMode == kIncludeServiceTalk); // AJT v17.0.7
        // AJT v20.0.1
        dlgPublicTalk.SetSongInfo(
            CChristianLifeMinistryUtils::UseSingOutJoyfullyToJehovah(m_datFirstMonday), m_eForeignLang);

        // AJT v20.1.8
        dlgPublicTalk.SetPreviousNextDateButtonStates(m_btnMovePrevious.IsWindowEnabled(),
                                                      m_btnMoveNext.IsWindowEnabled());
        auto iResult = dlgPublicTalk.DoModal();
        if (iResult == IDOK || iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate) ||
                               iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
        {
            m_pEntry->SetPublicTalkInfo(dlgPublicTalk.GetPublicTalkInfo());

            SetModified(true);
            UpdatePreview(m_iDateIndex);
            m_pHtmlPreview->Refresh2(REFRESH_COMPLETELY); // Ensure it has refreshed

            if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate))
            {
                //PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_PREVIOUS_DATE, BN_CLICKED),
                //  (LPARAM)m_btnMovePrevious.GetSafeHwnd());
                OnBnClickedMfcbuttonPreviousDate();
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
            }
            else if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
            {
                //PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_NEXT_DATE, BN_CLICKED),
                //  (LPARAM)m_btnMoveNext.GetSafeHwnd());
                OnBnClickedMfcbuttonNextDate();
                PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
            }
        }
    }
}

Последний подход работает. Я должен указать, что эти два обработчика кнопок обновляют файл HTML и перерисовывают его в элементе управления веб-браузера (который фактически находится в родительском диалоговом окне).

Допустимо ли напрямую вызывать такие обработчики кнопок, как этот ?


Используя предоставленный ответ (спасибо), это работает хорошо:

void CChristianLifeMinistryEditorDlg::OnFilePublicTalk()
{
    delay_post_msg dpm{ this };
    CWeekendMeetingDlg dlgPublicTalk(this);

    if (m_pEntry != nullptr)
    {
        dlgPublicTalk.SetPublicTalkInfo(m_pEntry->GetPublicTalkInfo());
        dlgPublicTalk.SetCircuitVisitMode(m_iIncludeMode == kIncludeServiceTalk); // AJT v17.0.7
        // AJT v20.0.1
        dlgPublicTalk.SetSongInfo(
            CChristianLifeMinistryUtils::UseSingOutJoyfullyToJehovah(m_datFirstMonday), m_eForeignLang);

        // AJT v20.1.8
        dlgPublicTalk.SetPreviousNextDateButtonStates(m_btnMovePrevious.IsWindowEnabled(),
                                                      m_btnMoveNext.IsWindowEnabled());
        auto iResult = dlgPublicTalk.DoModal();
        if (iResult == IDOK || iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate) ||
                               iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
        {
            m_pEntry->SetPublicTalkInfo(dlgPublicTalk.GetPublicTalkInfo());

            SetModified(true);
            UpdatePreview(m_iDateIndex);
            m_pHtmlPreview->Refresh2(REFRESH_COMPLETELY); // Ensure it has refreshed

            if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::PreviousDate))
            {
                //PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_PREVIOUS_DATE, BN_CLICKED),
                //  (LPARAM)m_btnMovePrevious.GetSafeHwnd());
                OnBnClickedMfcbuttonPreviousDate();
                //PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
                dpm.set_active();
            }
            else if (iResult == to_underlying(CWeekendMeetingDlg::EndResult::NextDate))
            {
                //PostMessage(WM_COMMAND, MAKEWPARAM(IDC_MFCBUTTON_NEXT_DATE, BN_CLICKED),
                //  (LPARAM)m_btnMoveNext.GetSafeHwnd());
                OnBnClickedMfcbuttonNextDate();
                //PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
                dpm.set_active();
            }
        }
    }
}

Мне пришлось использовать два обработчика кнопок OnBnClickedMfcbuttonPreviousDate / OnBnClickedMfcbuttonNextDate напрямую, без использования PostMessage для имитации нажатия кнопок. Полагаю, это приемлемо?

1 Ответ

2 голосов
/ 30 мая 2020

Как написано, это безопасно. PostMessage генерирует сообщение в очереди , которое не отслеживается, пока текущий поток не дойдет до повторного вызова функции извлечения сообщений (например, GetMessage). Однако сообщение l oop заблокировано, ожидая, пока текущий обработчик сообщений вернет ему управление.

К сожалению, (основное) сообщение l oop, предоставленное MF C, не является t единственный код, который потенциально может отправлять сообщения. Например, диалоги (например, MessageBox es) запускают собственное сообщение l oop. Как и меню, или реализация изменения размера окна. Множество возможностей вернуться к повторному входу, от которого вы пытались защититься.

Более надежное решение могло бы отложить вызов PostMessage до того, как функция вернется. C ++ предоставляет все инструменты, необходимые для реализации этого: Деструкторы!

struct delay_post_msg {
    delay_post_msg(CWnd* w) : w_{ w }, active_{ false } {}
    void set_active() { active_ = true; }
    ~delay_post_msg()
    {
        if (active_) w_->PostMessage(WM_COMMAND, ID_FILE_PUBLIC_TALK, 0);
    }
private:
    CWnd* w_;
    bool active_;
};

Вы бы использовали это примерно так:

void CChristianLifeMinistryEditorDlg::OnFilePublicTalk()
{
    // Objects are destroyed in the opposite order they are created.
    // If this object's d'tor needs to run last, it has to be created first.
    delay_post_msg dpm{ this };

    CWeekendMeetingDlg dlgPublicTalk(this);

    // ...

    if (some_condition)
    {
        dpm.set_active();
    }

    // ...

    // If active, the d'tor of dpm posts the message just before leaving this function.
}
...