天天看點

利用CWinThread實作跨線程父子MFC視窗

下面是示例代碼.

從CWinThread派生一個CUIThread類,可以利用VS向導生成,再添加一個成員:HWND m_hParentWnd.

建立一個對話框類CUIChildDlg,同樣用VS向導生成.

子視窗所在的線程.

class CUIThread : public CWinThread 

    DECLARE_DYNCREATE(CUIThread) 

protected: 

    CUIThread();           // protected constructor used by dynamic creation 

    virtual ~CUIThread(); 

public: 

    virtual BOOL InitInstance(); 

    virtual int ExitInstance(); 

    DECLARE_MESSAGE_MAP() 

    HWND                m_hParentWnd; //注意,它是父視窗句柄,不能是CWnd*對象.

}; 

線上程中建立對話框視窗.

BOOL CUIWinThread::InitInstance() 

    // TODO:  perform and per-thread initialization here 

ASSERT(::IsWindow(m_ hParentWnd)); 

    CWnd* pParent = CWnd::FromHandle(m_hParentWnd);//注意這行 

    CUIChildDlg* pDlg = new CUIChildDlg(pParent); 

    pDlg->Create(CUIChildDlg::IDD, pParent); 

    pDlg->ShowWindow(SW_SHOW); 

    return TRUE; 

在這個函數中,建立了一個對話框,作為主視窗的子視窗.

注意這個FromHandle的調用,他傳回一個CWnd對象.

這樣建立的視窗不會報錯.如果直接将主視窗對象傳遞過來,而不是通過調用FromHandle擷取,則調用pDlg->Create會報錯,在調試版本中會彈出一個視窗,指出錯誤位置.

CUIChildDlg是利用向導随便寫的一個對話框.注意要重載下面這個函數.

void CUIChildDlg::OnNcDestroy() 

    CDialog::OnNcDestroy(); 

    // TODO: Add your message handler code here 

    ::PostQuitMessage(0);//為了使線程自動退出. 

下面代碼是CWinApp派生類,主視窗在這個類中建立. 

BOOL CMFCSingleDocTestApp::InitInstance() 

    ... … 

    // The one and only window has been initialized, so show and update it 

    m_pMainWnd->ShowWindow(SW_SHOW);//主視窗 

    m_pMainWnd->UpdateWindow(); 

    // call DragAcceptFiles only if there's a suffix 

    //  In an SDI app, this should occur after ProcessShellCommand 

m_pUIThread = (CUIWinThread*)AfxBeginThread(RUNTIME_CLASS(CUIWinThread), 

THREAD_PRIORITY_NORMAL,    0, CREATE_SUSPENDED);//建立後先不要啟動. 

    m_pUIThread->m_hParentWnd = m_pMainWnd->m_hWnd;//主視窗句柄. 

    m_pUIThread->ResumeThread(); 

退出函數實作. 

int CMFCSingleDocTestApp::ExitInstance() 

    //TODO: handle additional resources you may have added 

    AfxOleTerm(FALSE); 

    ASSERT(NULL != m_pUIThread); 

    ::WaitForSingleObject(m_pUIThread->m_hThread, INFINITE); 

    return CWinAppEx::ExitInstance(); 

等待函數是必須的,這是為了等待子視窗線程退出後父視窗線程再退出.

在測試中發現,關閉主視窗之前隻能保證先關閉子視窗,但退出主線程之前并不能保證子視窗線程一定會退出,這可能會導緻某些資源不能正确釋放,是以這裡要調用等待函數,進而保證子視窗線程能夠正常退出.

跨線程父子視窗的好處是建立子視窗阻塞時不會影響父視窗的運作.例如啟動程式時,建立子視窗過程中由于加載太多内容而阻塞,導緻父視窗無法操作,分屬不同線程後,父視窗運作不受影響,它仍然可以正常啟動,最大最小化,移動,響應滑鼠消息等等.

但也不完全是這樣,程式運作起來之後,子視窗也會使用父視窗所在的線程消息循環,如果子視窗阻塞,同樣會導緻父視窗阻塞.

本文轉自jetyi51CTO部落格,原文連結:http://blog.51cto.com/jetyi/1074315 ,如需轉載請自行聯系原作者