MFC子线程访问主线程对话框程序的控件对象
最近在使用 VC 開發軟件時需要用到多線程同步來解決開發過程中遇到的問題。本來以為只要象控制臺程序一樣,在主線程創建子線程,并設置好相應的對象事件就能解決問題,但是等到真正做起來,才在實踐中發現原來事情并沒有我想象的那么簡單。以下我介紹一下我在開發過程中遇到的問題。
? ? ? ?我的 對話框程序是這樣設計的,我把大部分需要用到的子控件都在主線程的對話框先設計好,然后,由于我需要實時監控軟件的運行情況,并在對話框的一個靜態控件實時顯示軟件的運行情況;也就是說,我需要通過在主線程創建的子線程來控制對話框程序的子控件。哈哈,問題來啦,就在這里。
? ? ? ? 起初,我的線程函數是這樣設計的,我在主線程那里直接把子線程需要控制的子控件的變量直接通過參數傳給子線程,然后直接在子線程那里控制子控件。本來,想想是覺得沒問題的,但是不管我怎么測試,程序都不能正常執行,為了尋找原因,我直接把斷點定位到子線程里,然后在子線程里面一步步的調試,一直調試到更改子控件的那語句之前都沒問題,就唯獨那句更改子控件的代碼,每次到那里,整個程序就卡在那里,調試也調試不了,比如像下面這樣的代碼:
?
// 在主線程創建子線程
CWinThread* powerThread = AfxBeginThread(Thread_PowerOn, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, NULL, NULL);
?
// 子線程入口函數
UINT Thread_SetVoltageOfThePowerOn( LPVOID lpParameter )
{
? ? ? 。。。。。。
? ? ? CSonyPeaceDlg* pDlg = (CSonyPeaceDlg*)lpParameter;
? ? ? pDlg ->GetDlgItem(IDC_STATIC_COMMANDRESULT) ->SetWindowTextA(_T("打開電源。。。"));
? ? ? 。。。。。。
}
當程序執行到紅色的那行代碼時,程序就完全卡在那里不能往下執行了。
? ? ? ?出現這個問題,一開始也很納悶,后來一直在網上搜索問題的原因,才知道,原來,在 MFC 編程中子線程是不能直接訪問主線程里的控件對象的,這樣極容易造成訪問異常,導致消息混亂程序卡死,MSDN 中也有說明,子線程直接訪問主線程的控件對象是不安全的;為此,微軟也提供了相應的解決方案:
? ? ? ? 1、通過傳遞控件句柄 HWND 給子線程,來對控件進行訪問;
? ? ? ? 2、通過消息傳送機制,也就是通過在子線程里給主線程傳遞訪問控件對象的消息,然后在主線程的消息響應函數里面對控件對象進行操作。
? ? ? ? 以上這兩種說法,可能是我一時半會還沒理解過來,不知道它說的是兩種解決方法,還是兩種方法得結合起來用。最后我都試過,非得把它們都結合起來使用才能讓程序達到我需要的效果。
? ? ? ? 在這里先插一下話題,一開始我是以為是兩種都可行的解決方法,尤其是第一種方法最簡單,所以我試了一下,只是在主線程那里把控件句柄傳遞給子線程,然后在子線程那里通過傳遞過來的控件句柄對控件對象進行訪問,但還是不行,還是出現上面提到的現象,整個程序呈現卡死狀態。如下面的代碼:
// 這里我傳遞的是主窗口的句柄m_hWnd
powerThread = AfxBeginThread(Thread_PowerOn, (LPVOID)m_hWnd),THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
?
// 子線程入口函數
UINT Thread_SetVoltageOfThePowerOn( LPVOID lpParameter )
{
? ? ? 。。。。。。
? ? ? HWND hwnd = (HWND)lpParameter;
? ? ? ::SetDlgItemTextA(hwnd, IDC_STATIC_COMMANDRESULT, _T("打開電源..."));
? ? ? 。。。。。。
}
? ? ? ?同樣,程序運行到紅色那句代碼是就又卡在那里了。
? ? ? ?最后,我就把上面提到的兩種說法結合起來用,在主線程里傳遞句柄給子線程,然后再子線程里通過給主線程發送消息給主線程來改變訪問控件對象。如以下代碼:
// ?創建打開電源的線程 powerThread = AfxBeginThread(Thread_PowerOn, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);?
// 子線程入口函數
UINT Thread_SetVoltageOfThePowerOn( LPVOID lpParameter )
{
? ? ? 。。。。。。
? ? ?CSonyPeaceDlg* pDlg = (CSonyPeaceDlg*)lpParameter;
? ? ? //將要執行的命令發送出去
? ? ? ::PostMessageA( pDlg -> m_hWnd, UM_OPENPOWER, (WPARAM)(LPCSTR)_T("打開電源。。。"), (LPARAM)(LPCTSTR)_T("Put on power...") );
}
注意,為了方便,我直接把對話框程序的 指針this傳遞給子線程。
? ? ? ?其中?
UM_OPENPOWER是自定義的變量,需要自己定義。以下我順便說下如何添加自定義消息的方法,以方便和我一樣的小鳥級學者快速入門,呵呵!笑一個吧!
? ? ? ?在源文件 ***Dlg.cpp 開頭添加如下代碼:
#define UM_OPENPOWER ? ?WM_USER+100 ? ?//自定義一個消息
? ? ? ?在頭文件 ***Dlg.h 添加消息映射函數聲明:
public:
? ? ? afx_msg LRESULT OnOpenPower(WPARAM WParam, LPARAM LParam);
?
? ? ? 在源文件 ***Dlg.cpp 開頭添加消息映射,如下:
? ? ? ?在源文件 ***Dlg.cpp 添加消息映射函數的代碼:
這樣就完成了自定義消息映射的添加工作。
? ? ? ?接下來就是開始測試是否能夠正常工作。讀者可以自己再添加一個按鈕控件,然后在按鈕的消息映射里面把創建子線程的那行代碼添加進去,以此來測試以上代碼,會發現程序工作正常。這樣就完成了通過子線程來訪問主線程的控件對象的任務,只不過是間接訪問而已。
? ? ? ?道理說破了,其實也很簡單對吧,呵呵!
? ? ? ?這里我再說明一下,為了測試方便,我創建的子線程函數時 C***Dlg 類的友元函數,函數聲明如下:
friend UINT Thread_PowerOn( LPVOID lpParameter );
? ? ? ?需要將以上代碼放到 C***Dlg 類的定義里面。
?
? ? ? ?另外,我在子線程的入口函數里進行消息傳送時,使用的是?
PostMessageA()函數,而不是SendMessage()
函數,這里說明一下,這里需要用到的就是PostMessageA()函數,要是用SendMessage()函數的話,會發生阻塞的現象。這兩個函數的主要區別就是,前者將消息發送出去后就會立即返回,后者需要等相應的消息映射函數處理完成后才能返回,所以會一直阻塞在那個地方。具體說明還請讀者自己
?百度 或 MSDN 一下!
?
? ? ? ? 附:以上只是將消息發送出去,然后等操作系統自己調用 WindowProc() 函數,再根據具體的消息而映射到相應的消息響應函數處對相應的控件進行處理,所以可能無法及時的處理,因為在整個程序的執行過程中會有相當多的 消息 等待著系統去處理。
? ? ? ? ? ? 針對以上問題,我們也可以根據需要重載WindowProc() 函數,然后根據需要在相應的地方調用它。比方說,我自己重載后的WindowProc() 函數的代碼如下所示:
? ? ? ? ? ? 然后們可以在相應的地方根據需要調用此函數,就可以及時的對自定義的消息進行處理,如:我用一個按鈕來對其進行測試:
這樣就完成了自己根據需要在特定的時刻直接調用窗口處理函數 WindowProc() 來處理自定義的窗口消息。
?
? ? ? ?以上的所有代碼都只是作為測試用的,還不能真正體現出他們的作用,這里只做個方法的演示,讀者只有在真的碰到類似的問題是,再使用此方法才能真正的體現出它的作用之處!
?
? ? ? ?希望此篇文章對讀者能有所收獲,哈哈!
原文:https://blog.csdn.net/xbmoxia/article/details/16981243?
?
總結
以上是生活随笔為你收集整理的MFC子线程访问主线程对话框程序的控件对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从程序员角度看ELF
- 下一篇: H.264软件解码器在PXA270平台上