如何在另一个线程中关闭 MFC 模态对话框并获取对话框 return 值?
How do I close a MFC Modal Dialog in another thread and can get the dialog return value?
我有一个 MFC 对话框项目。在主对话框中有一个下载按钮,点击它会提示一个进度条并开始下载。下载完成后,我想让它自动关闭。
void CProgressBarTest::DoDataExchange(CDataExchange* pDX)
{
static auto funDownload = [&]() {
m_downloadRetValue = ::SomeDownloadAPI(funDownloadCallback);
//When download finished, close the current dialog (progress bar). Here are two options:
//EndDialog(IDYES); // causes crash
//::PostMessage(this->GetSafeHwnd(), WM_CLOSE, 0, 0);// doesn't crash, but without return valud IDYES.
};
m_thDownload = std::thread(funDownload);
}
关闭进度条的方法有两种:
EndDialog(IDYES)
: 导致崩溃。
::PostMessage(this->GetSafeHwnd(), WM_CLOSE, 0, 0)
:它可以关闭 window 而不会崩溃,但也没有 return 值(IDYES)。
我想在外面做这样的检查,
void CGUIThreadTestDlg::OnBnClickedButton3()
{
CProgressBarTest dlg(this);
INT_PTR nRet = dlg.DoModal();
switch (nRet)
{
case -1:
AfxMessageBox(_T("Dialog box could not be created!"));
break;
case IDYES:
AfxMessageBox(_T("Yes!"));
break;
case IDOK:
// Do something
break;
case IDCANCEL:
AfxMessageBox(_T("IDCANCEL!"));
break;
default:
// Do something
break;
}
}
也许这是一种解决方法。
Can I return a custom value from a dialog box's DoModal function?
不要使用 DoModal 的 return 值。只需为 CProgressBarTest
.
添加一个成员函数
Post 从下载线程到主 GUI 线程的 application-defined message 像这样:
BOOL CProgressBarTest::OnInitDialog()
{
CDialog::OnInitDialog();
auto funDownload = []( HWND hwnd ){
auto const downloadRetValue = ::SomeDownloadAPI(funDownloadCallback);
::PostMessage( hwnd, WM_APP_DOWNLOAD_FINISHED, static_cast<WPARAM>( downloadRetValue ), 0 );
};
m_thDownload = std::thread(funDownload, GetSafeHwnd());
return TRUE;
}
注意 1: 我使用 OnInitDialog()
来启动线程,因为 DoDataExchange()
是一个非常糟糕的选择,因为它会被调用多次。 OnInitDialog()
只会调用一次。
注 2: 无捕获 lambda 用于更好地解耦下载线程与 GUI 线程。将 this
从 GUI 对话框传递到工作线程是灾难的根源,因为它很容易只写入 GUI 线程变量,而忽略所需的同步。除此之外,更少的耦合伴随着更少的依赖性,这总是一件好事。
什么是WM_APP_DOWNLOAD_FINISHED
?这是我的 application-defined message ID,我通常这样定义:
enum {
WM_APP_0 = WM_APP,
WM_APP_DOWNLOAD_FINISHED
// for future extension...
};
添加消息映射条目:
BEGIN_MESSAGE_MAP(CProgressBarTest, CDialog)
ON_MESSAGE( WM_APP_DOWNLOAD_FINISHED, &CProgressBarTest::OnDownloadFinished )
END_MESSAGE_MAP()
并将消息处理程序定义为 return 来自取决于下载结果的对话框的值:
LRESULT CProgressBarTest::OnDownloadFinished( WPARAM wp, LPARAM lp )
{
m_thDownload.join();
auto const downloadRetValue = wp;
EndDialog( downloadRetValue == ERROR_SUCCESS ? IDYES : IDCANCEL );
return 0;
}
确保像我上面那样 join()
线程以避免 std::thread
析构函数崩溃,这需要线程已加入。
我有一个 MFC 对话框项目。在主对话框中有一个下载按钮,点击它会提示一个进度条并开始下载。下载完成后,我想让它自动关闭。
void CProgressBarTest::DoDataExchange(CDataExchange* pDX)
{
static auto funDownload = [&]() {
m_downloadRetValue = ::SomeDownloadAPI(funDownloadCallback);
//When download finished, close the current dialog (progress bar). Here are two options:
//EndDialog(IDYES); // causes crash
//::PostMessage(this->GetSafeHwnd(), WM_CLOSE, 0, 0);// doesn't crash, but without return valud IDYES.
};
m_thDownload = std::thread(funDownload);
}
关闭进度条的方法有两种:
EndDialog(IDYES)
: 导致崩溃。
::PostMessage(this->GetSafeHwnd(), WM_CLOSE, 0, 0)
:它可以关闭 window 而不会崩溃,但也没有 return 值(IDYES)。
我想在外面做这样的检查,
void CGUIThreadTestDlg::OnBnClickedButton3()
{
CProgressBarTest dlg(this);
INT_PTR nRet = dlg.DoModal();
switch (nRet)
{
case -1:
AfxMessageBox(_T("Dialog box could not be created!"));
break;
case IDYES:
AfxMessageBox(_T("Yes!"));
break;
case IDOK:
// Do something
break;
case IDCANCEL:
AfxMessageBox(_T("IDCANCEL!"));
break;
default:
// Do something
break;
}
}
也许这是一种解决方法。
Can I return a custom value from a dialog box's DoModal function?
不要使用 DoModal 的 return 值。只需为 CProgressBarTest
.
Post 从下载线程到主 GUI 线程的 application-defined message 像这样:
BOOL CProgressBarTest::OnInitDialog()
{
CDialog::OnInitDialog();
auto funDownload = []( HWND hwnd ){
auto const downloadRetValue = ::SomeDownloadAPI(funDownloadCallback);
::PostMessage( hwnd, WM_APP_DOWNLOAD_FINISHED, static_cast<WPARAM>( downloadRetValue ), 0 );
};
m_thDownload = std::thread(funDownload, GetSafeHwnd());
return TRUE;
}
注意 1: 我使用 OnInitDialog()
来启动线程,因为 DoDataExchange()
是一个非常糟糕的选择,因为它会被调用多次。 OnInitDialog()
只会调用一次。
注 2: 无捕获 lambda 用于更好地解耦下载线程与 GUI 线程。将 this
从 GUI 对话框传递到工作线程是灾难的根源,因为它很容易只写入 GUI 线程变量,而忽略所需的同步。除此之外,更少的耦合伴随着更少的依赖性,这总是一件好事。
什么是WM_APP_DOWNLOAD_FINISHED
?这是我的 application-defined message ID,我通常这样定义:
enum {
WM_APP_0 = WM_APP,
WM_APP_DOWNLOAD_FINISHED
// for future extension...
};
添加消息映射条目:
BEGIN_MESSAGE_MAP(CProgressBarTest, CDialog)
ON_MESSAGE( WM_APP_DOWNLOAD_FINISHED, &CProgressBarTest::OnDownloadFinished )
END_MESSAGE_MAP()
并将消息处理程序定义为 return 来自取决于下载结果的对话框的值:
LRESULT CProgressBarTest::OnDownloadFinished( WPARAM wp, LPARAM lp )
{
m_thDownload.join();
auto const downloadRetValue = wp;
EndDialog( downloadRetValue == ERROR_SUCCESS ? IDYES : IDCANCEL );
return 0;
}
确保像我上面那样 join()
线程以避免 std::thread
析构函数崩溃,这需要线程已加入。