1. 程式人生 > >MFC子執行緒訪問主執行緒對話方塊程式的控制元件物件

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() 來處理自定義的視窗訊息。

       以上的所有程式碼都只是作為測試用的,還不能真正體現出他們的作用,這裡只做個方法的演示,讀者只有在真的碰到類似的問題是,再使用此方法才能真正的體現出它的作用之處!

       希望此篇文章對讀者能有所收穫,哈哈!

相關推薦

MFC執行訪問執行對話方塊程式控制元件物件

       最近在使用 VC 開發軟體時需要用到多執行緒同步來解決開發過程中遇到的問題。本來以為只要象控制檯程式一樣,在主執行緒建立子執行緒,並設定好相應的物件事件就能解決問題,但是等到真正做起來,才在實踐中發現原來事情並沒有我想象的那麼簡單。以下我介紹一下我在開發過程中

toast彈框、imageview、進度條、執行訪問執行執行中的通訊handler)

1、imageview ?xml version="1.0" encoding="utf-8"?> <ImageView android:layout_width=“200dp” android:layout_marginLeft=“100dp” an

進度條與執行訪問執行

1.進度條(ProgressBar) 進度條的屬性: style=”?android:attr/progressBarStyleHorizontal” 預設為圓形 android:progress=”33” 進度條進行到的當前位置(去activity

MFC程式設計學習之改變對話方塊控制元件及文字顏色

//程式訊息流:首先對話方塊繪製時,會發送WM_CTLCOLOR訊息,那麼OnCtlColor()函式 //將響應該訊息,程式將用該函式返回的畫刷對控制元件進行繪製。 HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtl

VC MFC 對話方塊 Picture控制元件 顯示影象閃爍問題

在VC 使用StretchDIBits函式拉伸影象,然後顯示,使用 pWnd->Invalidate();  pWnd->UpdateWindow(); 重新整理視窗,發現當影象和背景相差很大的時候,有閃爍現象。 解決辦法: hdd=DrawDibOpen()

【VC MFC開發】Dll 中對話方塊控制元件無法接受到按鍵訊息的解決辦法

使用DLL注入到 別的程式中時,發現DLL的視窗過程無法響應WM_CHAR事件,摸索了很久,才找到原因,給碰到同樣問題的人蔘考。 WNDPROC OldComboEditProc = NULL; LRESULT CALLBACK ComboEditProc(   HW

MFC 動態建立對話方塊控制元件

         一段時間以來,都在思考不採用Qt,用Win32-API或者MFC實現的程式使介面可配置。即先配置好需要什麼對話方塊、包含什麼控制元件、完成什麼任務,然後執行時建立,用以實現介面和功能的分離。需要可以動態配置控制元件建立、訊息接收函式。之前準備的本文

關於mfc 對話方塊 DATETIMEPICKER控制元件的使用

按ctrl+w關聯一個CDateTimeCtrl變數,這個沒問題吧? CTime l_time; CString l_csTime; UpdateData(TRUE); m_ctlBeginDate.GetTime(l_time); l_csTime = l_time.Fo

MFC對話方塊 picture控制元件新增點陣圖,點陣圖拉伸,適應picture

1.事先準備好點陣圖。一定要點陣圖。點陣圖。 2.在對話方塊中新增picture控制元件,設定type為bitmap,將其ID改為IDC_STATIC1(不改不行)。然後為picture新增控制變數PT1。 3.在.cpp中找到onpaint()函式加入如下程式碼。 CDC

VS2013/MFC程式設計入門之六(對話方塊:為對話方塊新增控制元件

建立對話方塊資源需要建立對話方塊模板、修改對話方塊屬性、為對話方塊新增各種控制元件等步驟,前面一講中已經講了建立對話方塊模板和修改對話方塊屬性,本節繼續講如何為對話方塊新增控制元件。        上一講中建立了一個名為“Addition”的工程,目的是生成一個實現加法

在VS2010/MFC中如何對對話方塊新增控制元件

先說一下自己用的工具Visual Studio 2010 Ultimate 英文版(裝有Visual Assist X)。這裡簡單提一下Visual Assist X的安裝方法在選單欄選擇”Tools”,然後是Extension Manager,在Extension Manager左邊欄裡面選擇Onli

MFC修改對話方塊控制元件背景顏色

HBRUSH CMyDlg::OnCtlColor(CDC* pDC,CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr =CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Change any attributes o

Java多執行--讓執行等待執行執行完畢

參考連結:https://www.cnblogs.com/eoss/p/5902939.html 使用Java多執行緒程式設計時經常遇到主執行緒需要等待子執行緒執行完成以後才能繼續執行,那麼接下來介紹一種簡單的方式使主執行緒等待。 java.util.concurrent.CountDown

Qt中通過訊號和槽在執行執行中進行資料傳遞

QT中兩個執行緒之間進行自定義型別資料傳遞 兩個執行緒中進行資料傳遞時,傳遞的資料放到佇列中(queue),所以在這個過程中,需要在傳遞前將資料拷貝、儲存到佇列中;為了儲存這些引數,Qt需要construct、destruct、copy這些物件,為了讓Qt知道

Unity執行執行互動(委託方式)

using System; using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; /// <summary> /// 子執行緒與主執行

java在執行執行傳遞資料(回撥函式)

預習知識點: 什麼是回撥函式? 下面是知乎大神的回答,簡直不能再精闢 程式碼: package kun.thread; public class THread { static C c=new C(); //flag用來標誌子執行緒執行結束 stati

JAVA執行等待所有執行執行完成後執行執行

         如標題,此功能主要是JDK1.5引入的java.util.concurrent包下的CountDownLatch類,此類據介紹為以執行緒輔助類,通過執行緒計數器來實現一個或多個主執行緒等待其下所有子執行緒執行完後主執行緒再繼續執行的功能。        

Java 執行執行等待執行awaitTermination方法使用詳解

    Java中在使用Executors執行緒池時,有時場景需要主執行緒等各子執行緒都執行完畢後再執行。這時候就需要用到ExecutorService介面中的awaitTermination方法,我們來看看原始碼中對該方法的說明:大概意思是這樣的:該方法呼叫會被阻塞,並且在

qt4 執行執行發訊息,發資料,通訊,qthread gui postevent emit

【轉帖請註明出處:blog.csdn.net/lanmanck】 用了qthread後,自然想起給gui執行緒傳送資料。 在qt4中,QCustomEvent已經不使用了。因為qthread是從QObject繼承的,所以可以使用signal,slot機制來通訊。 1、線

Java執行執行交替輸出(一個簡單的例項)

實現主執行緒和子執行緒的交替輸出列印,首先建立一個實現執行緒方法的Service public class Service { private volatile boolean flag = false; //這是一個控制sub和mian的開關,加關鍵字volatil