在std :: thread中打开MFC对话框

3

我想展示一个对话框来告知用户应用程序正在忙碌中。为了避免阻塞主线程,我考虑使用std::thread来展示这个对话框。请看下面的代码:

InProcDlg inProcess;
std::thread t([ &inProcess ] {      
    inProcess.DoModal();
    delete inProcess;
});
// wait till process has finished 
::PostMessage(inProcess.m_hWnd, WM_USER + 1, 0, 0);
if (t.joinable()){
    t.join();
}

InProcDlg.cpp

BEGIN_MESSAGE_MAP(InProcDlg, CDialogEx)
  ...
  ON_MESSAGE(WM_USER + 1, &InProcDlg::close)
END_MESSAGE_MAP()

LRESULT InProcDlg::close(WPARAM wParam, LPARAM lParam)
{
  UNREFERENCED_PARAMETER(wParam, lParam);
  EndDialog(1);
  return 0;
}

运行这段代码时,对话框能够正常显示。虽然对话框也被关闭了,但主对话框没有显示出来,应用程序卡在了CreateRunDlgIndirect()。尝试设置一些断点,逐步执行,主对话框可以正常地显示回来。非常奇怪。请给我一些建议,告诉我需要深入研究哪些问题。

接下来,我还想向用户展示进程流程,通过发送一个表示当前进程状态的整数。

int *percent;
::PostMessage(inProcess.m_hWnd, WM_USER + 2, 0, reinterpret_cast<LPARAM>(percent));

我怎样才能在发送或发布消息之前获取对话已经存在的证据呢? 我正在使用Visual Studio 2013。


1
请查看:https://dev59.com/yUrSa4cB1Zd3GeqPTh3X - dwo
delete inProcess; 这段代码可以编译吗?无论如何,都不要使用它 - 它没有意义。 - Igor Tandetnik
1
最好的做法,通常也更容易的方法是反过来 - 在主 UI 线程上显示对话框,在工作线程上运行繁忙处理。 - Igor Tandetnik
1
你不能安全地将窗口层次结构跨多个线程扩展(请参见是否可以拥有跨进程的父/子或所有者/拥有的窗口关系?)。抛开这个问题,WM_USER 属于窗口类实现。在这种特殊情况下,是一些对话框窗口类。如果你想发送应用程序私有消息,需要使用 WM_APP + x 消息范围。 - IInspectable
1
@IInspectable:该链接特别警告跨进程层次结构,并指出跨线程窗口层次结构更简单,因为跨线程变量共享更容易,而且没有消息被过滤。 - MSalters
显示剩余2条评论
1个回答

2
我能想到两种方法来做到这一点:
无模式对话框

https://www.codeproject.com/Articles/1651/Tutorial-Modeless-Dialogs-with-MFC

用户线程(UI线程)。
使用CWinThread创建主UI线程(CWinApp)的一个兄弟线程。最重要的是将CWinThread::m_pMainWnd成员指定为指向对话框的指针。如果对话框是模态的,请在调用DoModal后返回FALSE,如果对话框是非模态的,则返回TRUE。
class CMainFrame : public CFrameWnd {
    // pointer to thread
    CWinThread* m_pUserThread;
}

开始线程。
m_pUserThread = AfxBeginThread(RUNTIME_CLASS(CUserThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED );
m_pUserThread->m_bAutoDelete = TRUE;
m_pUserThread->ResumeThread();

"头文件"
class CUserThread : public CWinThread
{
    DECLARE_DYNCREATE(CUserThread)
public:
    CUserThread();

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CUserThread)
    public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
    //}}AFX_VIRTUAL

protected:
    virtual ~CUserThread();
    // Generated message map functions
    //{{AFX_MSG(CUserThread)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
private:
    CUserMsg* m_pDlg;
}

源文件
#define DO_MODAL
BOOL CUserThread::InitInstance()
{

    #ifdef DO_MODAL
    // create and assign Modeless dialog
    CUserMsg dlg;
    m_pMainWnd = &m_pDlg;
    dlg.DoModal();
    return FALSE;
    #endif

    #ifdnef DO_MODAL
    // create and assign Modal dialog
    m_pDlg = new CUserMsg();
    m_pDlg->Create( IDD_USER_DLG, AfxGetMainWnd() );
    m_pMainWnd = m_pDlg;
    return TRUE;
    #endif
}

int CUserThread::ExitInstance()
{
    // check for null
    m_pDlg->SendMessage( WM_CLOSE ) ;
    delete m_pDlg;

    return CWinThread::ExitInstance();
}

终止线程
// post quit message to thread
m_pUserThread->PostThreadMessage(WM_QUIT,0,0);

// wait until thread termineates
::WaitForSingleObject(m_pUserThread->m_hThread,INFINITE);

我强烈建议两种方式都将对话框设置为最顶层窗口:
BOOL CLicenseGenDlg::OnInitDialog() {
    CDialog::OnInitDialog();

    // TODO: Add extra initialization here
    SetWindowPos( &wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | WS_EX_TOPMOST );
    return TRUE;
}

恐怕这不会像这个评论中所解释的那样工作。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接