MFC在子線程中創建窗口(PostMessage方法)


1、創建子線程

C++創建線程的方式比較多

1)最簡單易用的<thread>頭文件,但是這種方法創建的子線程中無法給主線程PostMessage消息(也可能是我操作有誤,總之沒成功)

2)3)4)參見VC創建線程的三種方法https://blog.csdn.net/u014568921/article/details/44262645

第3、4種用在MFC程序中貌似也不行,多次嘗試之下我用了AfxBeginThread()方法成功了

void CMFCDLLTestDlg::OnBnClickedMessage()
{
    // TODO: 在此添加控件通知處理程序代碼
    // 啟動websocket線程
    AfxBeginThread((AFX_THREADPROC)MsgThread, (VOID*)this, THREAD_PRIORITY_NORMAL, 0, 0, NULL);
}

我這里是在一個按鈕點擊事件中啟動了一個websocket線程,全局線程函數MsgThread()

2、通過自定義消息創建窗口

在MFC程序中,在子線程中直接調用Create()方法無法創建非模態窗口,貌似子線程的循環阻塞了創建過程,所以需要用自定義消息方法通知主線程來創建

2.1 自定義消息

MFC自定義消息其實不難,分三個步驟

1、定義一個消息ID

我的程序名叫MFCDLLTestDlg,所以在MFCDLLTestDlg.cpp中定義下一個消息ID

#define TEST_SENDMSG WM_USER+200//給消息一個ID

2、定義消息處理函數

消息處理函數是用來處理收到的自定義消息的,這個有兩種方法,可以通過類向導添加一個自定義消息處理,或者自己手寫也行,使用類向導可以直接綁定,省了第三步

類向導方式:切換到類試圖--->類向導--->消息--->添加自定義消息,然后輸入自定義的消息ID和處理函數名稱就好了

 手寫方式和類向導一樣,反正消息ID必須是自己定義的,固定WM_USER+一個數,不重復就行

然后再頭文件中聲明消息處理函數,注意在對話框主類中寫

afx_msg LRESULT OnTestSendmsg(WPARAM wParam, LPARAM lParam);

然后在cpp中定義

LRESULT CMFCDLLTestDlg::OnTestSendmsg(WPARAM wParam, LPARAM lParam)
{
    switch (wParam)
    {
    case TEST_SENDMSG:
        CMsgWindow * p_MsgWindow = new CMsgWindow();
        p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP1));
        //p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP2));
        //p_MsgWindow->SetSkin(MAKEINTRESOURCE(IDB_BITMAP3));

        CString *cmsg = (CString *)lParam;

        if (!p_MsgWindow->Create(m_hWnd, _T("通知")))
        {
            AfxMessageBox(L"Create Failed!"); return -1;
        }
        p_MsgWindow->SetMsg(L"高仿QQ新聞右下角彈窗", *cmsg, L"http://blog.csdn.net/jackystudio");
        p_MsgWindow->Show();
        break;
    }

    return LRESULT();
}

這里我是在收到消息后彈了個窗,有興趣的訪問一下這個 http://blog.csdn.net/jackystudio 博客,我從這里找的漂亮的彈窗程序

注意這個函數的兩個參數[ WPARAM wParam, LPARAM lParam ],這個是可以自己類型轉換的,常用的可能就是這種,第一個參數為消息類型,第二個參數為字符串,整數等其他參數,這里是個Cstring字符串

這兩個參數是在PostMessage函數中傳進來的,下面會看到

3、添加消息處理映射

有了消息ID和處理函數,還要把兩者關聯起來,這就是消息映射同樣是在主類中操作,找到MESSAGE_MAP

BEGIN_MESSAGE_MAP(CMFCDLLTestDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_WIN_TEXT, &CMFCDLLTestDlg::OnBnClickedWinText)
    ON_BN_CLICKED(IDC_NEW_DLG, &CMFCDLLTestDlg::OnBnClickedNewDlg)
    ON_BN_CLICKED(IDC_MESSAGE, &CMFCDLLTestDlg::OnBnClickedMessage)

    ON_MESSAGE(TEST_SENDMSG, &CMFCDLLTestDlg::OnTestSendmsg)
END_MESSAGE_MAP()

可以看到系統消息和按鈕點擊事件的映射都是在這里的,需要注意的是看清楚是主類的消息映射 BEGIN_MESSAGE_MAP(CMFCDLLTestDlg, CDialogEx) ,我就第一次寫在了About類的里面

2.2 發送消息

在子線程函數中用PostMessage發送消息,一般用這個,SendMessage也行,一個同步一個異步

// 省略線程函數其他邏輯
// ...
// 發送消息
::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), TEST_SENDMSG, (WPARAM)TEST_SENDMSG, (LPARAM)cmsg);

這里需要注意PostMessage函數加了作用域限定符,否則這個函數有好幾個,調用方法不同

第一個參數是主窗口句柄,第二個參數是消息ID,第三、四個參數對應上面消息處理函數的兩個參數

然后當發送消息函數被執行的時候,窗口主線程就會收到消息,執行創建窗口函數

當然只要把消息ID和參數一換,比如說換成某個按鈕的點擊事件ID或者系統消息ID,就可以做些其他事情了  

3、參考文章

C++ Part8 MFC中的AfxBeginThread的使用方法(代碼實例)

使用MFC中的AfxBeginThread創建多線程

建立非模態對話框與在線程中建立非模態對話框

MFC SendMessage()函數傳遞字符串


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM