一旦消息的当前实例在 MFC 中完成,如何 post 一条消息?
How to post a message once the current instance of the message has finished in MFC?
我对 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
调用)但原则上我想重新启动同一个事件处理程序,就好像用户在文件菜单上单击它一样。在消息的当前实例尚未终止时使用 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);
}
}
}
}
执行完操作后,它会重新显示 window,然后我单击“取消”,我得到:
如果我更改它并直接调用按钮处理程序,如下所示:
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 文件并在 Web 浏览器控件(实际上在父对话框中)中重新绘制它。
像这样直接调用那些按钮处理程序是否可以接受?
使用提供的答案(谢谢)效果很好:
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
来模拟按钮点击。我认为这是可以接受的?
正如所写,这是安全的。 PostMessage
生成一个 queued message 直到当前线程再次调用消息检索函数(如 GetMessage
)后才会被观察到。然而,消息循环被阻塞,等待当前消息处理程序 return 控制权返回给它。
不幸的是,MFC 提供的(主)消息循环并不是可能调度消息的唯一代码。例如,对话框(如 MessageBox
es)启动它们自己的消息循环。与菜单或 window 调整大小的实现一样。有很多机会恢复到您试图防止的重入状态。
更强大的解决方案可以将 PostMessage
调用推迟到函数 return 之前。 C++ 提供了实现这一点所需的所有工具:Destructors!
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.
}
我对 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
调用)但原则上我想重新启动同一个事件处理程序,就好像用户在文件菜单上单击它一样。在消息的当前实例尚未终止时使用 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);
}
}
}
}
执行完操作后,它会重新显示 window,然后我单击“取消”,我得到:
如果我更改它并直接调用按钮处理程序,如下所示:
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 文件并在 Web 浏览器控件(实际上在父对话框中)中重新绘制它。
像这样直接调用那些按钮处理程序是否可以接受?
使用提供的答案(谢谢)效果很好:
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
来模拟按钮点击。我认为这是可以接受的?
正如所写,这是安全的。 PostMessage
生成一个 queued message 直到当前线程再次调用消息检索函数(如 GetMessage
)后才会被观察到。然而,消息循环被阻塞,等待当前消息处理程序 return 控制权返回给它。
不幸的是,MFC 提供的(主)消息循环并不是可能调度消息的唯一代码。例如,对话框(如 MessageBox
es)启动它们自己的消息循环。与菜单或 window 调整大小的实现一样。有很多机会恢复到您试图防止的重入状态。
更强大的解决方案可以将 PostMessage
调用推迟到函数 return 之前。 C++ 提供了实现这一点所需的所有工具:Destructors!
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.
}