MFC是很老的技術了,但在工控領域,還是有優勢的。只是其中一些技術比較隱蔽,不能想當然。
廢話少說,上菜~~
一個簡單而常用的工控架構: 一個主對話框,對話框中有一個編輯控件輸入, 和一個OK按鈕控件。此處省略菜單,各種特殊事件響應,不在此討論。如下圖:
故事開始,左鍵點擊OK按鈕,後臺子線程開始運行。子線程運行過程中,彈出一個非模態對話框,用於一部分信息輸入。如下:
關鍵點來了:彈出的子對話框 pDlgOption 中的 combobox 控件 m_combo_x,需要根據主對話框的輸入進行初始化以及讀取操作。於是我想當然的這麼幹:
UINT ThreadFunc_TCS_SPOTFIRE(LPVOID lpParam)
{
CDlgTCSSpotfire* pDlg = (CDlgTCSSpotfire*) lpParam; //main dialog
CDlgTCSOption pDlgOption; // modeless input dialog
if(pDlgOption.DoModal() == IDOK){
CString combo_info;
pDlgOption.m_combo_x.GetWindowTextA(combo_info);
}
::PostMessage(pDlg->GetSafeHwnd(), TCS_SPOTFIRE_DATA_HANDLE_END, NULL, NULL); //
return TRUE;
}
很不幸,編譯通過,但是運行時錯誤。問題在於這句:
pDlgOption.m_combo_x.GetWindowTextA(combo_info);
子線程表示,在我的管轄範圍,讀寫訪問你的控件是違法的。
解決之道,直接來不行,可以繞個道。
1. 先在子對話框的類中聲明需要傳輸的數據。如定義一個 vector 等等,把需要對子對話框進行的操作信息放到裏面。
2. 在子線程裏面對子對話框的類數據成員傳數據,如上一部定義的 vector,當然必須是public的。
3. 以上完成後,再調用 DoModal()
4. 最後對子對話框控件的操作,在它的類中完成,如重載 OnInitDialog(),OnBnClickOk() 等等。
代碼如下:
UINT ThreadFunc_TCS_SPOTFIRE(LPVOID lpParam)
{
CDlgTCSSpotfire* pDlg = (CDlgTCSSpotfire*) lpParam; //main dialog
CDlgTCSOption pDlgOption; // modeless input dialog
pDlgOption.testno_vec.clear();
for(vector<string>::iterator it = log_data.test_vec.begin(); it != log_data.test_vec.end(); ++it){
pDlgOption.testno_vec.push_back(*it);
}
if(pDlgOption.DoModal() == IDOK){
}
::PostMessage(pDlg->GetSafeHwnd(), TCS_SPOTFIRE_DATA_HANDLE_END, NULL, NULL); //
return TRUE;
}
BOOL CDlgTCSOption::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
for(vector<string>::iterator it = testno_vec.begin(); it != testno_vec.end(); ++it){
m_combo_x.AddString((*it).c_str());
}
m_combo_x.SetCurSel(0);
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
小小思考:對話框雖然在子線程中聲明定義,但是對話框運行時有自己的線程,它的控件部分會被自己的線程綁定,不能在子線程中訪問控件,否則會出現兩個線程同時操作一個控件的問題。然而對話框的非控件數據成員沒有被綁定,仍然可以被聲明對話框的線程調用。
但是,在主對話框的線程中,訪問它的子對話框控件是沒問題的。猜想是,子對話框的線程會自動 assign 到 主對話框中。
這是MFC定義各類對象,相互間的權限問題。估計是安全考慮。這是需要在 MFC 或者 C++ 編程中需要注意的。